我将以下两个项目插入到“框架”集合中:
frame1 = {
"number": 1,
"hobjects": [ { "htype": 1, "weight": 50 },
{ "htype": 2, "weight": 220 },
{ "htype": 2, "weight": 290 },
{ "htype": 3, "weight": 450 } ],
"sobjects": [ { "stype": 1, "size": 10.0 },
{ "stype": 2, "size": 5.1 },
{ "stype": 2, "size": 6.5 } ],
}
frame2 = {
"number": 2,
"hobjects": [ { "htype": 1, "weight": 61 },
{ "htype": 2, "weight": 210 },
{ "htype": 2, "weight": 250 } ],
"sobjects": [ { "stype": 1, "size": 12.1 },
{ "stype": 2, "size": 4.9 },
{ "stype": 2, "size": 6.2 },
{ "stype": 2, "size": 5.7 } ],
}
frames.insert(frame1)
frames.insert(frame2)
现在我想对部分帧数据进行查询:
query = { "hobjects.htype": 3, "sobjects.stype": 2 }
db.frames.find(query)
导致:
{u'_id': ObjectId('545b6ea7b9ad9a03462d743b'), u'hobjects': [{u'htype': 1, u'weight': 50}, {u'htype': 2, u'weight': 220}, {u'htype': 2, u'weight': 290}, {u'htype': 3, u'weight': 450}], u'number': 1, u'sobjects': [{u'stype': 1, u'size': 10.0}, {u'stype': 2, u'size': 5.1}, {u'stype': 2, u'size': 6.5}]}
并不是我想要的。我希望根据查询过滤集合,以便我得到以下结果:
{u'_id': ObjectId('545b6ea7b9ad9a03462d743b'), u'hobjects': [{u'htype': 3, u'weight': 450}], u'number': 1, u'sobjects': [{u'stype': 2, u'size': 5.1}, {u'stype': 2, u'size': 6.5}]}
我找到的唯一解决方案涉及每个集合的展开和分组聚合:
query = { "hobjects.htype": 3, "sobjects.stype": 2 }
db.frames.aggregate([
{ "$match": query },
{ "$unwind": "$hobjects" },
{ "$match": dict((key, value) for key, value in query.iteritems() if "hobjects." in key) },
{ "$group": { "_id": "$_id", "number": { "$first": "$number" } , "hobjects": { "$push": "$hobjects" }, "sobjects": { "$first": "$sobjects" } } },
{ "$unwind": "$sobjects" },
{ "$match": dict((key, value) for key, value in query.iteritems() if "sobjects." in key) },
{ "$group": { "_id": "$_id", "number": { "$first": "$number" } , "hobjects": { "$first": "$hobjects" }, "sobjects": { "$push": "$sobjects" } } },
])
我猜这不是一种非常有效和灵活的查询方式。我想知道是否还有其他选择?
答案 0 :(得分:0)
如果您的服务器是MongoDB 2.6或更高版本,那么您可以随时执行此操作:
db.frames.aggregate([
// Still helps to match the documents by conditions to filter
{ "$match": {
"hobjects.htype": 3, "sobjects.stype": 2
}},
// Now filter inline using $map and $setDifference
{ "$project": {
"number": 1,
"hobjects": {
"$setDifference": [
{ "$map": {
"input": "$hobjects",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el.htype", 3 ] },
"$$el",
false
]
}
}},
[false]
]
},
"sobjects": {
"$setDifference": [
{ "$map": {
"input": "$sobjects",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el.stype", 2 ] },
"$$el",
false
]
}
}},
[false]
]
}
}}
])
这里的问题是基本投影和$elemMatch
之类的东西目前只能匹配与条件匹配的数组中的第一个元素。所以要做更多的事情,你需要某种形式的高级操作,这些操作只适用于聚合框架等。
$setDiffence
和$map
运算符会为您提供"内联"处理数组的方式实际上是#34; set"在单个文件中。这比使用$unwind
更有效,特别是在涉及大型数组的情况下。
这里的JavaScript符号我知道(主要是在评论中),但它与python基本相同。
答案 1 :(得分:0)
以下聚合可能会对您有所帮助
db.frames.aggregate({"$unwind":"$hobjects"},{"$unwind":"$sobjects"},{"$match":{"hobjects.htype": 3, "sobjects.stype": 2}},{"$group":{"_id":"$_id","u'hobjects":{"$first":"$hobjects"},"u'number":{"$first":"$number"},"u'sobjects":{"$push":"$sobjects"}}})