Mongodb 多嵌套数组搜索

我的目的是搜索数据userid 1的记录

My aim is to search records of data userid 1

下面是我的数据

{ "_id" : 2,
  "name" : "test", 
  "data" :[{"_id" : "1","file" : "nic", "userid" : [1,2 ]},
           {"_id" : "2","file" : "nic1","userid" : [1 ]  },
           {"_id" : 3,"file" : "nick2","userid" : [1,2 ]} 
     ]},

{ "_id" : 3,
  "name" : "test",
  "data" : [{"_id" : "1","file" : "nic","userid" : [1,2 ]  },
            {"_id" : "2","file" : "nic1", "userid" : [3,2 ] } 
      ]}

输出应该是

{ "_id" : 2,
  "name" : "test", 
  "data" :[{"_id" : "1","file" : "nic", "userid" : [1,2 ]},
           {"_id" : "2","file" : "nic1","userid" : [1 ]  },
           {"_id" : 3,"file" : "nick2","userid" : [1,2 ]} 
     ]},

{ "_id" : 3,
  "name" : "test",
  "data" : [{"_id" : "1","file" : "nic","userid" : [1,2 ]  },          
      ]}

我试过了

$res=$collection->find(array("data.userid" =>array('$in'=>array('52')))); 

返回空

推荐答案

您需要 .aggregate() 方法以过滤"任何数组内容以进行多个单一匹配,而且基本匹配要简单得多,因为 MongoDB 并不关心数据在数组内,只要指定的路径是正确的:

You need the .aggregate() method in order to "filter" any array content for more than a singular match, and also the basic match is a lot simplier as MongoDB does not care that the data is within arrays, just as long as the specified path is correct:

db.collection.aggregate([
    { "$match": { "data.userid": 1 } },
    { "$project": {
        "data": {
            "$setDifference": [
                { "$map": {
                    "input": "$data",
                    "as": "el",
                    "in": { 
                        "$cond": [
                            { "$setIsSubset": [ [1], "$$el.userid" ] },
                            "$$el",
                            false
                        ]
                    }
                }},
                [false]
            ]
        }
    }},
    { "$match": { "data.0": { "$exists": true } }}
])

在 PHP 中,这表示如下:

With PHP this notates as follows:

$collection->aggregate(array(
    array( '$match' => array( "data.userid" => 1 )),
    array(
        '$project' => array(
            'data' => array(
                '$setDifference' => array(
                    array(
                        '$map' => array(
                            'input' => '$data',
                            'as' => 'el',
                            'in' => array(
                                '$cond' => array(
                                    array( '$setIsSubset' => array(array(1),'$$el.userid') ),
                                    '$$el',
                                    FALSE
                                )
                            )
                        )
                    ),
                    array(FALSE)
                )
            )
        )
    ),
    array( '$match' => array( 'data.0' => array( '$exists' => TRUE ) ) )
))

$map 运算符允许检查外部数组的每个元素并将每个元素传递给 $cond 三元运算.这会处理 $setIsSubset 对内部"数组的操作,以查看它是否实际上包含备用集合中的值之一(在本例中为 [1] )以及其中 true 进行评估,然后返回元素,否则 false.

The $map operator allows inspection of each element ofthe outer array and passed each element to the $cond ternary operation. This processes a $setIsSubset operation on the "inner" array to see if it in fact contains one of the values in the alternate set ( in this case [1] ) and where a true evaluation is made then the element is returned or otherwise false.

$setDifference 的要点 是从修改后的数组中删除那些 false 值,只返回匹配的元素.最后是 $exists 测试查看外部数组实际上至少有一个元素并且由于过滤而不是空的.

The point of $setDifference is to remove those false values from the modified array and only return matched elements. And finally the $exists test looks to see that outer array actually has at least one element and is not empty as a result of the filtering.

返回的文档是符合条件的文档,并且只有数组元素也符合指定的条件.

The documents returned are the ones with the matching condition and only the array elements that also match the specified condition.

当然,这里的操作员要求您至少有 MongoDB 2.6 作为服务器(这是一个相当老的版本,至少是一个建议的更新)但是如果您仍然有一个较小的版本,那么您需要使用 $unwind$group:

Of course the operators here require that you have at least MongoDB 2.6 as the server ( which is quite an old release now and an advised update at least ) but if you still have a lesser version then you need a traditional approach with $unwind and $group:

$collection->aggregate(array(
    array( '$match' => array( "data.userid" => 1 )),
    array( '$unwind' => '$data' ),
    array( '$match' => array( 'data.userid' => 1 )),
    array( 
        '$group' => array(
            '_id' => '$_id',
            'data' => array( '$push' => '$data' )
        )
    )
))

相关文章