我有以下文件:
doc1: {
'array': [
{'field': 'ABC', 'enabled': 'false'},
{'field': 'BCD', 'enabled': 'true'},
{'field': 'DEF', 'enabled': 'false'},
{'field': 'XYZ', 'enabled': 'true'},
]
}
doc2: {
'array': [
{'field': 'ABC', 'enabled': 'true'},
{'field': 'BCD', 'enabled': 'true'},
{'field': 'DEF', 'enabled': 'false'},
{'field': 'XYZ', 'enabled': 'true'},
]
}
我正在按特定领域进行搜索。 我希望获得启用此字段的所有文档,并且在此之前没有启用其他字段(在列表中)。
例如:
搜索字段:BCD
,已启用:true - 应仅返回第一个文档(因为在第二个文档中ABC
也已启用)。
搜索XYZ
,启用:true - 根本不应返回任何文档,因为此列表顶部已启用其他字段。
我尝试使用$elemMatch
进行smth,但我不知道是否可以在elemMatch
匹配的元素之上应用过滤器。
有什么建议吗?
答案 0 :(得分:3)
除了正常查询之外,最好还是使用$where
进行搜索,并且仍然保留在服务器上:
db.getCollection('collection').find({
"array": {
"$elemMatch": { "field": "BCD", "enabled": "true" },
},
"$where": function() {
return this.array.map((e,i) => Object.assign(e,{ i }))
.filter( e => e.field === "BCD" && e.enabled === "true" )
.map( e => e.i )[0] <=
this.array.map(e => e.enabled).indexOf("true")
}
})
如果您的MongoDB 3.4支持$indexOfArray
和$range
,那么它可能看起来更长,但实际上$redact
效率最高:
db.getCollection('collection').aggregate([
{ "$match": {
"array": {
"$elemMatch": { "field": "BCD", "enabled": "true" },
}
}},
{ "$redact": {
"$cond": {
"if": {
"$lte": [
{ "$arrayElemAt": [
{ "$map": {
"input": {
"$filter": {
"input": {
"$map": {
"input": {
"$zip": {
"inputs": [
"$array",
{ "$range": [0, { "$size": "$array" }] }
]
}
},
"as": "a",
"in": {
"field": { "$arrayElemAt": [ "$$a.field", 0 ] },
"enabled": { "$arrayElemAt": [ "$$a.enabled", 0 ] },
"index": { "$arrayElemAt": [ "$$a", 1 ] }
}
}
},
"as": "a",
"cond": {
"$and": [
{ "$eq": [ "$$a.field", "BCD" ] },
{ "$eq": [ "$$a.enabled", "true" ] }
]
}
}
},
"as": "a",
"in": "$$a.index"
}},
0
]},
{ "$indexOfArray": [ "$array.enabled", "true" ] }
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
因此实际上没有实际的查询操作强制执行,但这两种情况都将选择“保留在服务器上”,而不是通过网络将数据发送到客户端然后进行过滤。
因为如果你这样做,它首先会否定使用数据库的目的。所以你真的希望在服务器上发生这种情况。
答案 1 :(得分:1)
为什么不按enabled
字段进行搜索,然后检查field
是否合适?
db.collection("col").findOne({
"array.enabled": true
}, {
array: {
$elemMatch {
enabled: true
}
}
})
.then(function(docs){
docs.forEach(function(doc){
if(doc.array[0].field == "ABC"){
// Ok, we get it
}
})
})
find
的第二个参数是投影,因此应用程序不会从文档中下载完整的数组,而只是第一个匹配的元素。
答案 2 :(得分:0)
$where
关键字允许为类似目的生成复杂条件。
find($where: 'this.array.filter(function(e){return e.enabled=="true"})[0].field=="ABC"')
由于这不使用任何索引,我将添加更多条件以从优化中受益。