db.test.insert(
{
'name': 'outer',
'foos': [
{
'name': 'a',
'type': 'bar',
},
{
'name': 'a',
'type': 'bar',
},
{
'name': 'z',
'type': 'baz',
},
{
'name': 'z',
'type': 'baz',
},
]
}
)
如何在foos
等于foo.type
的情况下获取bar
的独特列表?
我想找到:
[
{
'name': 'a',
'type': 'bar'
}
]
以下内容不起作用,而是为所有foos返回一个不同的值。
db.test.distinct('foos', {'foos.type': 'bar'})
答案 0 :(得分:4)
是的,哎呀!这里对这个功能有点误解。这是它返回的内容,我将解释原因:
[
{
"name" : "a",
"type" : "bar"
},
{
"name" : "z",
"type" : "baz"
}
]
所以其他条目中“type”等于“baz”,两者现在都是“明显的”,但你错过了你真正的问题。
你确实要求不同的“foos”,这是对的。但是你也只是要求从“文件”中得到它,这个“文档”的数组条目的“type”等于“bar”。这不会将内容“过滤”到那些数组条目,因此您获得其他结果的原因。
因此,您需要在获取“不同”值之前“过滤”内容。你只能用.aggregate()
方法做到这一点。 $filter
是最好的方法:
db.test.aggregate([
// Match documents
{ "$match": { "foos.type": "bar" } },
// Pre-filter the array
{ "$project": {
"foos": {
"$filter": {
"input": "$foos",
"as": "el",
"cond": {
"$eq": [ "$$el.type", "bar" ]
}
}
}
}},
// Unwind the array
{ "$unwind": "$foos" },
// Group distinct
{ "$group": {
"_id": "$foos"
}}
])
或者比MongoDB 3.2早,但版本2.6及更高版本,您可以将$map
替换为$setDifference
:
db.test.aggregate([
// Match documents
{ "$match": { "foos.type": "bar" } },
// Pre-filter the array
{ "$project": {
"foos": {
"$setDifference": [
{ "$map": {
"input": "$foos",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el.type", "bar" ] },
"$$el",
false
]
}
}}
]
}
}},
// Unwind the array
{ "$unwind": "$foos" },
// Group distinct
{ "$group": {
"_id": "$foos"
}}
])
同样的事情,$map
处理每个数组元素并返回mactched元素或false
和$setDiffernce
删除false
个:
最后在2.6以上的任何东西:
db.test.aggregate([
// Match documents
{ "$match": { "foos.type": "bar" } },
// Unwind the array
{ "$unwind": "$foos" },
// Filter the denormalized array
{ "$match": { "foos.type": "bar" } },
// Group distinct
{ "$group": {
"_id": "$foos"
}}
])
一般原则是只留下匹配“type”的数组条目等于“bar”,并且在使用$unwind
减少需要的工作之前,理想地“预过滤”数组处理过后,因为过滤后会基本上为每个数组条目创建一个新文档,无论它是否匹配。
无论如何,在某些时候,您需要使用$unwind
对数组条目进行“反规范化”,然后使用“foos”(子文档)将$group
作为主键值。
这不是.distinct()
提供的简单“数组”,而是基本上你只是“清除”你不想考虑的数组条目。
这是要记住的事情,因为正常的查询操作不会“过滤”数组元素,然后类似地,.distinct()
的查询输入也不会这样做,其中删除这些元素是你打算做的