MongoDB将包含数组字段的文档与所有匹配查询的元素进行匹配
从$elementMatch
的MongoDB文档:
$ elemMatch操作符将包含数组字段的文档与至less一个匹配所有指定查询条件的元素进行匹配。
但是,如何将包含数组字段的文档与匹配查询的所有元素进行匹配?
例如,我有这样的文件:
{ "_id": ObjectId("55c99649b8b5fc5b0a2f1c83"), "sku": "ed-39211", "created_at": ISODate("2015-08-11T06:29:29.139+0000"), "formats": [{ "name": "thefile", "_id": ObjectId("55c99649f2e2d6353348ec9c"), "prices": [{ "price": 4.49, "currency": "GBP", "territory": "GB", "_id": ObjectId("55c99649f2e2d6353348ec9f") }, { "price": 6.99, "currency": "USD", "territory": "US", "_id": ObjectId("55c99649f2e2d6353348ec9e") }, { "price": 6.99, "currency": "CHF", "territory": "CH", "_id": ObjectId("55c99649f2e2d6353348ec9d") }] }] }
我需要匹配所有formats.prices.price
> 5的所有文档
如果我使用以下查询:
{ 'formats.prices': { $elemMatch: { price: { $gte: 5 } } } }
该文件将被匹配,因为至less有一个价格> 5
我也试过这个,但似乎并不奏效:
{ 'formats.prices': { $all: { $elemMatch: {price: { $gte: 0.98 } } } } }
有没有一种方法可以排除那些至less看不到一个价格的文件?
find了! 这很容易,只需使用$not
操作符并检查相反的(<5):
{ 'formats.prices': { $not: { $elemMatch: {price: { $lt: 5 } } } } }
您可以使用Aggegation或MAP REDUCE来实现它:
第一个解决scheme是使用Map-Reduce :
我创build了一个名为“格式”的集合,并插入以下数据:
{ "_id" : ObjectId("55c99649b8b5fc5b0a2f1c83"), "sku" : "ed-39211", "created_at" : ISODate("2015-08-11T06:29:29.139Z"), "formats" : [ { "name" : "thefile", "_id" : ObjectId("55c99649f2e2d6353348ec9c"), "prices" : [ { "price" : 4.49, "currency" : "GBP", "territory" : "GB", "_id" : ObjectId("55c99649f2e2d6353348ec9f") }, { "price" : 6.99, "currency" : "USD", "territory" : "US", "_id" : ObjectId("55c99649f2e2d6353348ec9e") }, { "price" : 6.99, "currency" : "CHF", "territory" : "CH", "_id" : ObjectId("55c99649f2e2d6353348ec9d") } ] } ] } { "_id" : ObjectId("55c99649b8b5fc5b0a2f1c84"), "sku" : "ed-39211", "created_at" : ISODate("2015-08-11T06:29:29.139Z"), "formats" : [ { "name" : "thefile", "_id" : ObjectId("55c99649f2e2d6353348ec9a"), "prices" : [ { "price" : 5.49, "currency" : "GBP", "territory" : "GB", "_id" : ObjectId("55c99649f2e2d6353348ec9f") }, { "price" : 6.99, "currency" : "USD", "territory" : "US", "_id" : ObjectId("55c99649f2e2d6353348ec9e") }, { "price" : 6.99, "currency" : "CHF", "territory" : "CH", "_id" : ObjectId("55c99649f2e2d6353348ec9d") } ] } ] }
Map_reduce:
db.format.mapReduce( function() { var doc = {"_id" : this._id, "sku" : this.sku, "created_at" : this.created_at, "formats" : this.formats}; var prices; var flag = 0; for ( var i = 0 ; i < doc.formats.length; i++) { prices = doc.formats[i].prices for ( var j =0 ; j < prices.length; j++) { if( prices[j].price < 5) { flag = 1; break; } } if( flag == 1) doc.formats.splice(i,1); } if( doc.formats.length > 0 ) emit( this._id, doc); }, function(){}, { "out": { "inline": 1 } } )
输出:
{ "results" : [ { "_id" : ObjectId("55c99649b8b5fc5b0a2f1c84"), "value" : { "_id" : ObjectId("55c99649b8b5fc5b0a2f1c84"), "sku" : "ed-39211", "created_at" : ISODate("2015-08-11T06:29:29.139Z"), "formats" : [ { "name" : "thefile", "_id" : ObjectId("55c99649f2e2d6353348ec9a"), "prices" : [ { "price" : 5.49, "currency" : "GBP", "territory" : "GB", "_id" : ObjectId("55c99649f2e2d6353348ec9f") }, { "price" : 6.99, "currency" : "USD", "territory" : "US", "_id" : ObjectId("55c99649f2e2d6353348ec9e") }, { "price" : 6.99, "currency" : "CHF", "territory" : "CH", "_id" : ObjectId("55c99649f2e2d6353348ec9d") } ] } ] }
使用聚合的第二个解决scheme :
使用集合运算符$ unwind和$ size,我们可以使用下面的查询得到所需的结果:
在“Formats”和“Formats.prices”的$ unwind之后,获取“Formats.prices”的大小,然后在“prices”上执行$匹配 ,并再次计算“Formats.prices”的新大小。
如果大小相同,则“格式”字段中的所有“价格”都大于5,文档将被投影。
db.format.aggregate([ { $unwind: "$formats" }, { $project : { _id : 1, sku : 1, created_at : 1, formats : 1, "size" : { $size : "$formats.prices" } } }, { $unwind: "$formats.prices" }, { $match: { "formats.prices.price" : { $gt:5 } } }, { $group: { _id: { "name" : "$formats.name" , "_id" : "$formats._id", "id" : "$_id" }, prices : { $push: "$formats.prices" } , sku: { $first: "$sku" }, created_at : { $first: "$created_at" }, oldsize : { $first: "$size" } } }, { $project: { _id : 1, prices : 1, sku : 1, created_at : 1, oldsize : 1, newsize : {$size: "$prices" } } }, { $project: { _id : 1, prices : 1, sku : 1, created_at : 1, cmp_value: { $cmp: ["$oldsize", "$newsize"] } } }, { $match: { cmp_value:{ $eq:0 } } }, { $group : { _id : "$_id.id" , sku: { $first: "$sku" }, created_at : { $first: "$created_at" }, formats : { $push: { name : "$_id.name", "_id" : "$_id._id", prices: "$prices" } } } } ]).pretty()
输出:
{ "_id" : ObjectId("55c99649b8b5fc5b0a2f1c84"), "sku" : "ed-39211", "created_at" : ISODate("2015-08-11T06:29:29.139Z"), "formats" : [ { "name" : "thefile", "_id" : ObjectId("55c99649f2e2d6353348ec9a"), "prices" : [ { "price" : 5.49, "currency" : "GBP", "territory" : "GB", "_id" : ObjectId("55c99649f2e2d6353348ec9f") }, { "price" : 6.99, "currency" : "USD", "territory" : "US", "_id" : ObjectId("55c99649f2e2d6353348ec9e") }, { "price" : 6.99, "currency" : "CHF", "territory" : "CH", "_id" : ObjectId("55c99649f2e2d6353348ec9d") } ] } ] }