使用另一个字段的值查找MongoDB对象

3

最近我发现在一个文档中,通过另一个字段的键找到存储的对象有些困难。

{
    list : {
        "red" : 397n8,
        "blue" : j3847,
        "pink" : 8nc48,
        "green" : 983c4,
    },
    result : [
                { "id" : 397n8, value : "anger" },
                { "id" : j3847, value : "water" },
                { "id" : 8nc48, value : "girl" },
                { "id" : 983c4, value : "evil" }
             ]
    }
}

我正在尝试获取id为'j3847'、值为'water'的'blue'的值。
db.docs.find( { result.id : list.blue }, { result.value : 1 } );

# list.blue would return water
# list.pink would return girl
# list.green would return evil

我尝试了很多方法,甚至找到了一篇关于如何使用同一文档中的一个值更新另一个值的文章:Update MongoDB field using value of another field,但是没有成功... :/

我该如何使用另一个字段的值找到MongoDB对象?

2个回答

5
你可以使用mongo聚合中的$filter运算符来实现。它返回一个仅包含符合条件的元素的数组:
db.docs.aggregate([
  {  
    $project: {
      result: {
        $filter: {
          input: "$result", 
          as:"item", 
          cond: { $eq: ["$list.blue", "$$item.id"]}
        }
      }
    }
  }
])

这个查询的输出看起来像这样:

{ 
  "_id" : ObjectId("569415c8299692ceedf86573"), 
  "result" : [ { "id" : "j3847", "value" : "water" } ] 
}

除了匹配条件的值,是否可能返回其他值? - samland
@samland 是的,你可以使用以下语法将其他字段添加到你的投影中:field: 1。或者你可以使用$$ROOT将整个文档投影到一个字段中。在投影中它会像这样:document: 'ROOT'。 - Volodymyr Synytskyi

2
一种方法是使用$where运算符,但不建议使用它,因为使用它会调用完整的集合扫描,而不管其他条件可能使用索引选择,并且在每个结果文档上调用JavaScript解释器,这将比本地代码慢得多。
话虽如此,对于这种类型的比较,请改用.aggregate()方法作为更好的选择:
db.docs.aggregate([
    { "$unwind": "$result" },
    {
        "$project": {
            "result": 1,
            "same": { "$eq": [ "$list.blue", "$result.id" ] }
        }
    },
    { "$match": { "same": true } },
    { 
        "$project": {
            "_id": 0,
            "value": "$result.value"
        }
    }
])

当在result数组字段上应用$unwind运算符时,它将为每个结果字段的元素生成一个新记录。它基本上会展开数据,然后在接下来的$project步骤中检查数组的每个成员以比较两个字段是否相同。 示例输出
{
    "result" : [ 
        {
            "value" : "water"
        }        
    ],
    "ok" : 1
}

另一个选择是在单个$project步骤中使用$map$setDifference运算符,可以避免在非常大的集合上使用昂贵的$unwind操作,并且在大多数情况下会导致16MB BSON限制约束。
db.docs.aggregate([
    {
        "$project": {
            "result": { 
                "$setDifference": [
                    { 
                        "$map": {
                            "input": "$result",
                            "as": "r",
                            "in": { 
                                "$cond": [
                                    { "$eq": [ "$$r.id", "$list.blue" ] },
                                    "$$r",
                                    false
                                ]
                            }
                        }
                    },
                    [false]
                ]
            }
        }
    }
])

示例输出
{
    "result" : [ 
        {
            "_id" : ObjectId("569412e5a51a6656962af1c7"),
            "result" : [ 
                {
                    "id" : "j3847",
                    "value" : "water"
                }
            ]
        }
    ],
    "ok" : 1
}

你的查询是否遍历整个文档集合,或者我们可以通过文档 _id 指定要“聚合”的文档? - samland
如果您想通过 _id 或任何其他键过滤聚合管道中的文档,可以使用 $match 管道运算符。这类似于 MongoDB 集合的 find() 方法和 SQL 的 WHERE 子句。基本上,它会过滤传递给下一个运算符的数据。在管道中可以有多个 $match 运算符。 - chridam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接