【问题标题】:How to query embedded values in an object?如何查询对象中的嵌入值?
【发布时间】:2015-10-13 01:30:09
【问题描述】:

如何编写一个查询来选择集合中的所有对象,其中values 中的至少一个键具有正值。

例如它选择这个:

{
  "_id" : "kDi4C9sZpq782kKJf",
  "values": {
    "a": -1,
    "b": -1,
    "c": 1234
  }
}

还有这个

{
  "_id" : "kDi4C9sZpq782kKJf",
  "values": {
    "345jscf": -1,
    "6234gdr": 2341,
    "485hfls": -1
  }
}

但不是

{
  "_id" : "kDi4C9sZpq782kKJf",
  "values": {
    "a134dsd": -1,
    "bdgssvs": -1,
    "c345scv": -1
  }
}

{
  "_id" : "kDi4C9sZpq782kKJf",
  "values": {
  }
}

理想情况下,我想要一些类似的东西 db.coll.find("values.*": {$gt: 0})

其中 * 匹配任何键。

编辑 我已经编辑了值以更能描述问题。

【问题讨论】:

  • 在你的 values 对象中总是只包含三个值 a,b,c
  • 您不能使用正则表达式来匹配字段名称。
  • 我建议您更改文档结构并将值更改为子文档数组。
  • 嗨 @user3100115 如果将值更改为 [ [a, 1], [b,-1], [asdfasf, -1 ] 那如何解决问题?谢谢!
  • @JD 请在下面阅读我的回答

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

您可以使用.aggregate() 方法来执行此操作:

db.collection.aggregate([
    { "$group": { 
        "_id": "$_id", 
        "valA": { "$push": "$values.a" }, 
        "valB": { "$push": "$values.b" }, 
        "valC": { "$push": "$values.c" }, 
        "values": { "$first": "$values" }
    }}, 
    { "$project": { 
        "values": 1, 
        "allvalues": { "$setUnion": [ "$valA", "$valB", "$valC" ] }
    }}, 
    { "$redact": { "$cond": [
        { "$anyElementTrue": [
            { "$map": {
                "input": "$allvalues", 
                "as": "av", 
                "in": { "$cond": [ 
                    { "$gt": [ "$$av", 0 ]}, 
                    true, 
                    false 
                ]}
            }}
        ]}, 
        "$$KEEP", 
        "$$PRUNE"
    ]}}
])

返回:

{
        "_id" : "kDi4C9sZpq782kKJf",
        "values" : {
                "a" : -1,
                "b" : -1,
                "c" : 1234
        },
        "allvalues" : [
                1234,
                -1
        ]
}

使用$setUnion 运算符和$redact 仍然可以简化此查询

db.collection.aggregate([
    { "$group": { 
        "_id": "$_id", 
        "valA": { "$push": "$values.a" }, 
        "valB": { "$push": "$values.b" }, 
        "valC": { "$push": "$values.c" }, 
        "values": { "$first": "$values" }
    }}, 
    { "$redact": { "$cond": [
        { "$anyElementTrue": [
            { "$map": {
                "input": { "$setUnion": [ "$valA", "$valB", "$valC" ] },
                "as": "av", 
                "in": { "$cond": [ 
                    { "$gt": [ "$$av", 0 ]}, 
                    true, 
                    false 
                ]}
            }}
        ]}, 
        "$$KEEP", 
        "$$PRUNE"
    ]}}
])

如果values 键不固定,最好的办法是使用“批量”操作更改文档结构以获得最大效率。

var bulkOp = db.collection.initializeUnorderedBulkOp();
var count  = 0;
db.collection.find().forEach(function(doc) { 
    var values = doc.values; 
    var anotherValues = []; 
    for(var key in values) {       
        if(Object.prototype.hasOwnProperty.call(values, key)) {
            anotherValues.push({"name": key, "value": values[key]});     
        } 
    }
    bulkOp.find({ "_id": doc._id }).updateOne({ 
        "$set": { "values": anotherValues } 
    }); 
    count++; 
    if(count % 125 === 0) {     
        bulkOp.execute();     
        bulkOp = db.collection.initializeUnorderedBulkOp(); 
    } 
})

if(count > 0) { bulkOp.execute(); }

现在您的文档如下所示:

{
        "_id" : "kDi5C9sZpq782kKJf",
        "values" : [
                {
                        "name" : "a",
                        "value" : -1
                },
                {
                        "name" : "b",
                        "value" : -1
                },
                {
                        "name" : "c",
                        "value" : 1234
                }
        ]
}
{
        "_id" : "kDi4C9sZpq782kKJf",
        "values" : [
                {
                        "name" : "345jscf",
                        "value" : -1
                },
                {
                        "name" : "6234gdr",
                        "value" : 2341
                },
                {
                        "name" : "485hfls",
                        "value" : -1
                }
        ]
}

然后您可以使用.aggregate() 方法。

db.collection.aggregate([
    { "$redact": { 
         "$cond": [
             { "$anyElementTrue": [
                 { "$map": { 
                      "input": "$values", 
                      "as": "av", 
                      "in": { "$cond": [ 
                          { "$gt": ["$$av.value", 0] }, 
                          true, 
                          false
                      ]}
                 }}
             ]}, 
             "$$KEEP", 
             "$$PRUNE"
         ]
    }}
])

【讨论】:

    【解决方案2】:

    你可以这样试试

    db.collection.find({$and:[{"values":{$ne:{}}},{$or:[{"values.a":{$gt:0}},{"values.b":{$gt:0}},{"values.c":{$gt:0}}]}]}).pretty()
    

    在您的文档中,值不是数组,因此您必须检查值文档的各个元素。

    希望对你有帮助

    【讨论】:

    • 感谢您的回答!我修改了解释以更准确地描述我的问题。 a、b 和 c 实际上是任意数量的未知值的占位符。
    猜你喜欢
    • 2016-07-12
    • 2020-03-24
    • 1970-01-01
    • 2013-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多