2015-08-22 18 views
0

存储在MongoDB中的对象的结构如下:如何通过调整嵌套对象数组来检索文档?

obj = {_id: "55c898787c2ab821e23e4661", ingredients: [{name: "ingredient1", value: "70.2"}, {name: "ingredient2", value: "34"}, {name: "ingredient3", value: "15.2"}, ...]} 

我想要做的是检索所有文件,其中具体成分的值大于任意数量。

更具体地讲,假设我们想检索所有包含有名为“ingredient1”成分的文件,并将其值大于50

尝试以下我无法获取期望的结果:

var collection = db.get('docs'); 
var queryTest = collection.find({$where: 'this.ingredients.name == "ingredient1" && parseFloat(this.ingredients.value) > 50'}, function(e, docs) { 
            console.log(docs); 
           }); 

有谁知道什么是正确的查询条件的特定数组元素名称和值?

谢谢!

+0

我在这里指出,你的 “价值” 项事实上是字符串。因此,在“500”下进行基本的词汇比较是可行的(字符串方式),但在存储中将其转换为数字更好。 –

回答

1

你真的不需要的 JavaScript的评价这里,只使用基本的查询操作符与$elemMatch查询数组。虽然这里的“价值”元素实际上是字符串,但这不是真正的重点(正如我在本文末尾解释的那样)。主要的一点是,以得到它在第一时间:

collection.find(
    { 
     "ingredients": { 
      "$elemMatch": { 
       "name": "ingredient1", 
       "value": { "$gt": 50 } 
      } 
     } 
    }, 
    { "ingredients.$": 1 } 
) 

在第二部分的$postional operator,突出只从查询条件的阵列的匹配元素。

这比JavaScript评估速度快得多,因为评估代码不需要编译并使用本机编码的运算符,并且可以在“名称”上使用“索引”,甚至数组的“值”元素有助于过滤匹配。

如果您希望阵列中有多个匹配项,那么.aggregate()命令是最佳选择。随着现代MongoDB的版本,这是很简单的:

collection.aggregate([ 
    { "$match": { 
     "ingredients": { 
      "$elemMatch": { 
       "name": "ingredient1", 
       "value": { "$gt": 50 } 
      } 
     } 
    }}, 
    { "$redact": { 
     "$cond": { 
      "if": { 
       "$and": [ 
        { "$eq": [ { "$ifNull": [ "$name", "ingredient1" ] }, "ingredient1" ] }, 
        { "$gt": [ { "$ifNull": [ "$value", 60 ] }, 50 ] } 
       ] 
      }, 
      "then": "$$DESCEND", 
      "else": "$$PRUNE" 
     } 
    }} 
]) 

即使simplier在即将发行的这些介绍$filter操作:

collection.aggregate([ 
    { "$match": { 
     "ingredients": { 
      "$elemMatch": { 
       "name": "ingredient1", 
       "value": { "$gt": 50 } 
      } 
     } 
    }}, 
    { "$project": { 
     "ingredients": { 
      "$filter": { 
       "input": "$ingredients", 
       "as": "ingredient", 
       "cond": { 
        "$and": [ 
         { "$eq": [ "$$ingredient.name", "ingredient1" ] }, 
         { "$gt": [ "$$ingredient.value", 50 ] } 
        ] 
       } 
      } 
     } 
    }} 
]) 

凡在这两种情况下,你都有效地“过滤”那些数组元素不符合初始文件匹配后的条件。另外,由于您的“价值”现在实际上是“字符串”,因此您应该将其更改为数字。这里是一个基本的过程:

var bulk = collection.initializeOrderedBulkOp(), 
    count = 0; 

collection.find().forEach(function(doc) { 
    doc.ingredients.forEach(function(ingredient,idx) { 
     var update = { "$set": {} }; 
     update["$set"]["ingredients." + idx + ".value"] = parseFloat(ingredients.value); 
     bulk.find({ "_id": doc._id }).updateOne(update); 
     count++; 

     if (count % 1000 != 0) { 
      bulk.execute(); 
      bulk = collection.initializeOrderedBulkOp(); 
     } 
    }) 
]); 

if (count % 1000 != 0) 
    bulk.execute(); 

而且这将修复数据,所以这里的查询形式工作。

这比使用JavaScript $where进行处理要好得多,它需要评估集合中的每个文档,而没有筛选索引的好处。如果正确的格式是:

collection.find(function() { 
    return this.ingredients.some(function(ingredient) { 
     return ( 
      (ingredient.name === "ingredient1") && 
      (parseFloat(ingredient.value) > 50) 
     ); 
    }); 
}) 

而且,也不能在结果中“投射”匹配的值,因为其他格式可以。

0

尝试使用$ elemMatch:

var queryTest = collection.find(
    { ingredients: { $elemMatch: { name: "ingredient1", value: { $gte: 50 } } } } 
); 
相关问题