mongodb - 具有多个标识符和混合条件(AND/OR)的 arrayFilters

fab*_*ols 5 mongodb conditional-statements

我试图做一个匹配一些嵌套数组条件的 mongodb 查询,但没有成功......

文件:

[
   {
      "_id":1,
      "name":"foo",
      "games":[
         {
            "name":"Game1",
            "data":[
               { "data_id":1, "date":"YYYMMDD" },
               { "data_id":2, "date":"YYYMMDD" }
            ]
         },
         {
            "name":"Game2",
            "data":[
               { "data_id":1, "date":"YYYMMDD" },
               { "data_id":2, "date":"YYYMMDD" }
            ]
         }
      ]
   },
   {
      "_id":2,
      "name":"bar",
      "games":[
         {
            "name":"Game2",
            "data":[ 
               { "data_id":1, "date":"YYYMMDD" },
               { "data_id":2, "date":"YYYMMDD" }
            ]
         }
      ]
   }
]
Run Code Online (Sandbox Code Playgroud)

我想做一个update_many,匹配以下数据:

all documents
AND ( (_id = 1 and games.name = 'Game1' and games.data.data_id = 1 ) OR (_id = 2 and games.name = 'Game2' and games.data.data_id = 1 ) )
set valid = True
Run Code Online (Sandbox Code Playgroud)

因此,对于每个文档,我想更新一个特定的 games.data.data_id 元素。

我试过的

[
   {
      "_id":1,
      "name":"foo",
      "games":[
         {
            "name":"Game1",
            "data":[
               { "data_id":1, "date":"YYYMMDD" },
               { "data_id":2, "date":"YYYMMDD" }
            ]
         },
         {
            "name":"Game2",
            "data":[
               { "data_id":1, "date":"YYYMMDD" },
               { "data_id":2, "date":"YYYMMDD" }
            ]
         }
      ]
   },
   {
      "_id":2,
      "name":"bar",
      "games":[
         {
            "name":"Game2",
            "data":[ 
               { "data_id":1, "date":"YYYMMDD" },
               { "data_id":2, "date":"YYYMMDD" }
            ]
         }
      ]
   }
]
Run Code Online (Sandbox Code Playgroud)

但它返回一个错误:

WriteError: Error parsing array filter :: caused by :: Expected a single top-level field name, found 'i' and 'j'
Run Code Online (Sandbox Code Playgroud)

预期的输出是:

[
   {
      "_id":1,
      "name":"foo",
      "games":[
         {
            "name":"Game1",
            "data":[
               { "data_id":1, "date":"YYYMMDD", "valid": True },
               { "data_id":2, "date":"YYYMMDD" }
            ]
         },
         {
            "name":"Game2",
            "data":[
               { "data_id":1, "date":"YYYMMDD" },
               { "data_id":2, "date":"YYYMMDD" }
            ]
         }
      ]
   },
   {
      "_id":2,
      "name":"bar",
      "games":[
         {
            "name":"Game2",
            "data":[ 
               { "data_id":1, "date":"YYYMMDD", "valid": True },
               { "data_id":2, "date":"YYYMMDD" }
            ]
         }
      ]
   }
]
Run Code Online (Sandbox Code Playgroud)

那么,我做错了什么?这该怎么做?我尝试了其他几种选择,例如组合 $or 和 $and,但是每次我在相同条件下使用多个标识符时,它都会失败

pra*_*ad_ 7

根据使用规则arrayFilters,您不能随意使用条件,例如,中的_id字段arrayFilters。请参阅 为数组更新操作指定数组过滤器

  • 在更新文档中,使用 $[] 过滤位置运算符定义一个标识符,然后在数组过滤器文档中引用该标识符。如果标识符未包含在更新文档中,则您不能拥有标识符的数组过滤器文档。
  • 您可以在更新文档中多次包含相同的标识符;但是,对于更新文档中的每个不同的标识符 ($[identifier]),您必须准确指定一个对应的数组过滤器文档。也就是说,您不能为同一个标识符指定多个数组过滤器文档。

以下是其他更新方式;但这些不能使用arrayFilters你想要的方式。


1. 使用 Bulk.find.arrayFilters:

var bulk = db.games.initializeUnorderedBulkOp();
bulk.find( { _id: 1 } )
    .arrayFilters( [ { "g.name": "Game1" }, { "d.data_id": 1 } ] )
    .updateOne( { $set: { "games.$[g].data.$[d].valid": true } } );
bulk.find( { _id: 2 } )
    .arrayFilters( [ { "g.name": "Game2" }, { "d.data_id": 1 } ] )
    .updateOne( { $set: { "games.$[g].data.$[d].valid": true } } );
bulk.execute();
Run Code Online (Sandbox Code Playgroud)

更多详情请访问:Bulk.find.arrayFilters


2. 使用聚合管道更新

从 MongoDB 4.2 开始,该db.collection.updateMany()方法可以接受[ <stage1>, <stage2>, ... ]指定要执行的修改的聚合管道。详细信息:使用聚合管道更新

db.games.updateMany(
  {
      $or: [ { $and: [ { _id: 1 }, { "games.name": "Game1" }, { "games.data.data_id": 1 } ] }, 
               { $and: [ { _id: 2 }, { "games.name": "Game2" }, { "games.data.data_id": 1 } ] } 
      ]
  },
  [
    { 
      $set: { 
          games: { 
              $map: { 
                  input: "$games", as: "g",
                  in: { 
                      $mergeObjects: [ 
                          "$$g", 
                          { data: { 
                               $map: { 
                                   input: "$$g.data", as: "d",
                                   in: { 
                                       $cond: [
                                           { $or: [ 
                                               { $and: [ { $eq: [ "$_id", 1 ] }, { $eq: [ "$$g.name", "Game1" ] }, { $eq: [ "$$d.data_id", 1 ] } ] },
                                               { $and: [ { $eq: [ "$_id", 2 ] }, { $eq: [ "$$g.name", "Game2" ] }, { $eq: [ "$$d.data_id", 1 ] } ] }
                                            ] }, 
                                            { $mergeObjects: [ "$$d", { valid: true } ] }, 
                                            "$$d"
                                       ]
                                   }
                               }
                          } }
                      ]
                  }
              }
          }
      }
    } 
  ]
)
Run Code Online (Sandbox Code Playgroud)


3. 聚合管道和更新

聚合 + 更新适用于 4.2 之前的MongoDB 版本。

db.games.aggregate( [
  { 
      $addFields: { 
          games: { 
              $map: { 
                  input: "$games", as: "g",
                  in: { 
                      $mergeObjects: [ 
                          "$$g", 
                          { data: { 
                               $map: { 
                                   input: "$$g.data", as: "d",
                                   in: { 
                                       $cond: [
                                           { $or: [ 
                                               { $and: [ { $eq: [ "$_id", 1 ] }, { $eq: [ "$$g.name", "Game1" ] }, { $eq: [ "$$d.data_id", 1 ] } ] },
                                               { $and: [ { $eq: [ "$_id", 2 ] }, { $eq: [ "$$g.name", "Game2" ] }, { $eq: [ "$$d.data_id", 1 ] } ] }
                                            ] }, 
                                            { $mergeObjects: [ "$$d", { valid: true } ] }, 
                                            "$$d"
                                       ]
                                   }
                               }
                          } }
                      ]
                  }
              }
          }
      }
  }
] ).forEach( doc => db.games.updateOne( { _id: doc._id }, { $set: { games: doc.games } } ) )
Run Code Online (Sandbox Code Playgroud)