我正在评估MongoDB,我希望看到它在查询方面的能力。
关于我的数据集,我可能需要使用字段的值来与其他字段进行比较。最好的解释方法是举个例子。
在下面的json中,我想 返回至少有一个年龄小于30且国家人口超过100万的人的文件。 :
{
people: [
{ name: "Feyyaz", age: 28, country: "Turkiye" },
{ name: "Joseph", age: 25, country: "USA" },
...
],
countries: [
{ name: "Turkiye", population: 75000000 },
{ name: "USA", population: 300000000 },
...
]
}
注意:这个例子完全由我组成,因为我的真实世界的例子要复杂得多。并且改变结构应该是最后的选择。
答案 0 :(得分:3)
如果你可以使用Python来完成这项工作,你可以考虑使用查询语言ObjectPath 这使您可以在一行中完成作业:
$.people[@.age<30 and $.countries[@.name is @@.country].population > 100000000]
除了&#34; @@&#34;还没有实现 - 如果你想使用它,你可以在github页面上写一个功能请求
免责声明:计划在不久的将来将此语言与MongoDB集成,以便它可以利用MongoDB分布式功能。
答案 1 :(得分:1)
使用.find()
的标准查询操作将无法按照您要求的方式将两个字段匹配在一起。您可以使用标准匹配条件获得“接近”结果,但实际上比较数组元素更为先进。
您正在寻找的“高级瑞士军刀”以MongoDB的aggregation framework形式出现。这不仅仅是“聚合”数据,因为它也是一般文档操作和评估的工具:
db.pop.aggregate([
// Match possible documents to reduce work
{ "$match": {
"people.age": { "$lt": 30 },
"countries.population": { "$gt": 100000000 }
}},
// Test the conditions against the arrays
{ "$project": {
"people": 1,
"countries": 1,
"match": {
"$anyElementTrue": {
"$map": {
"input": "$people",
"as": "p",
"in": {
"$anyElementTrue": {
"$map": {
"input": "$countries",
"as": "c",
"in": {
"$and": [
{ "$lt": [ "$$p.age",30 ] },
{ "$gt": [ "$$c.population",100000000 ] },
{ "$eq": [ "$$p.country", "$$c.name" ] }
]
}
}
}
}
}
}
}
}},
// Filter any documents that did not match
{ "$match": { "match": true }}
])
如果您正在“过滤”那些仅仅匹配结果,那么您可以稍微改变一下。我将分解$project
个阶段,但你可以在一个阶段完成:
db.pop.aggregate([
// Match possible documents to reduce work
{ "$match": {
"people.age": { "$lt": 30 },
"countries.population": { "$gt": 100000000 }
}},
// Filter the people array for matches
{ "$project": {
"people": {
"$setDifference": [
{ "$map": {
"input": "$people",
"as": "p",
"in": {
"$cond": [
{ "$and": [
{ "$lt": [ "$$p.age", 30 ] },
{
"$anyElementTrue": {
"$map": {
"input": "$countries",
"as": "c",
"in": {
"$and": [
{ "$gt": [ "$$c.population", 100000000 ] },
{ "$eq": [ "$$p.country", "$$c.name" ] }
]
}
}
}
}
]},
"$$p",
false
]
}
}},
[false]
]
},
"countries": 1
}},
// Discard any document that did not meet conditions
{ "$match": { "people": { "$ne": false } }},
// Filter the countries to matching people
{ "$project": {
"people": 1,
"countries": {
"$setDifference": [
{ "$map": {
"input": "$countries",
"as": "c",
"in": {
"$cond": [
{ "$and": [
{ "$gt": [ "$$c.population", 100000000 ] },
{
"$anyElementTrue": {
"$map": {
"input": "$people",
"as": "p",
"in": {
"$eq": [ "$$p.country", "$$c.name" ]
}
}
}
}
]},
"$$c",
false
]
}
}},
[false]
]
}
}}
])
在第二种情况下,您将获得与此类不匹配的数组元素“已过滤”的文档:
{
"_id" : ObjectId("53c8f1645117367f5ff2036c"),
"people" : [
{
"name" : "Joseph",
"age" : 25,
"country" : "USA"
}
],
"countries" : [
{
"name" : "USA",
"population" : 300000000
}
]
}
相当强大的东西。
另请参阅文档中的aggregation framework operators和其他聚合示例。
您也可以使用mapReduce执行类似的操作,但通常首选聚合框架,因为它是本机代码实现,而MongoDB mapReduce依赖于JavaScipt解释来运行。
答案 2 :(得分:0)
在NeilLunn的回答中提到FeyyazE的评论,实际上你也可以使用标准的javascript和非常经典且易于阅读的功能,如下所示:
function test1 (field) {return field <= 30;}
function test2 (field) {return field >= 100000000;}
var fct = function (array1, field1, pivot1, array2, field2, pivot2) {
for (var key in array1) {
if (test1(array1[key][field1])) {
for (var key2 in array2) {
if (array2[key2][pivot2] == array1[key][pivot1] && test2(array2[key2][field2])) {
return true;
}
}
}
}
return false;
}
db.test.find({$where: "fct(
this.people,
'age',
'country',
this.countries,
'population',
'name'
)"});
但这真的需要一段时间才能让mongo进行评估。我在shell中尝试了一个小的100K文档集合,它花了... 3秒!所以也许你会更喜欢努力和难以阅读的脚本......