Pymongo高级查询(嵌入对象和数组)

时间:2016-12-20 20:59:55

标签: mongodb mongodb-query pymongo

我已经研究了大约一个星期,已经学习了很多关于pymongo但仍然无法破解它。

我在mongo中有以下JSON

{ 
    "_id" : ObjectId("5845e25xxxxxxxxxx"), 
    "timestamp" : ISODate("2016-08-24T14:59:04.000+0000"), 
    "new_key" : "cambiar", 
    "Records" : [
        {
            "RecordType" : "WFD", 
            "Properties" : [
                {
                    "Property" : {
                        "IsReadOnly" : "False", 
                        "ValueType" : "System", 
                        "Name" : "LastWrite"
                    }, 
                    "value" : "42"
                }, 
                {
                    "Property" : {
                        "IsReadOnly" : "True", 
                        "ValueType" : "String", 
                        "Name" : "Time_as_String"
                    }, 
                    "value" : "24-08-2016 14:59:08"
                }, 
                {
                    "Property" : {
                        "IsReadOnly" : "False", 
                        "ValueType" : "32", 
                        "Name" : "YES"
                    }, 
                    "value" : "1472065148"

下面还有更多属性。我试图只返回"值" :1472065148,没别的。我写了这个查询

x = dataset.find_one({"Records.Properties.Property.Name":'YES'},{"Records.Properties.Property.value":1})

但要避免价值'与所有其他值处于同一水平,我得到了所有的值结果,而不仅仅是我希望的结果。

有没有办法在匹配查询的对象之后只打印结果对象? "名称" :"是"作为我查询和"价值的对象" :" 1472065148"是我想要打印的对象。

------------------------ ADDITIONAL PART ---------------

以上是一个名称为'的文件。 :'是'我想要追溯的价值观。但是每个文档都有相同的名称:YES'在里面。我想做的是,首先根据不同的名称选择文件。 :' xxxx'以它的价值。例如上面 - 查找名称' :' lastwrite'在检索名称之前检查其值是否为42(因此选择此文档而不选择其他文档)。 :'是'价值(如你给我的答案)。

(如果这是一个新问题,请告诉我,我会将其删除并发布新问题)

1 个答案:

答案 0 :(得分:2)

您对现有结构的唯一选择是使用聚合。

db.dataset.aggregate([{
    $unwind: "$Records"
}, {
    $unwind: "$Records.Properties"
}, {
    $match: {
        "Records.Properties.Property.Name": 'YES'
    }
}, {
    $project: {
        "_id": 0,
        "value": "$Records.Properties.value"
    }
}]).pretty()

样本回复

{ "value" : "1472065148" }

假设您能够按如下方式更新结构(为简洁起见,删除了一些字段)。这里的变化是记录不再是一个数组,而只是一个嵌入的文档。

db.dataset.insertMany([{
  "timestamp": ISODate("2016-08-24T14:59:04.000+0000"),
  "Records": {
      "RecordType": "WFD",
      "Properties": [{
          "Property": {
              "Name": "LastWrite"
          },
          "value": "42"
      }, {
          "Property": {
              "Name": "YES"
          },
          "value": "1472065148"
      }]
  }
}])

查询

db.dataset.find({"Records.Properties.Property.Name":'YES'},{"Records.Properties.$":1}).pretty()

响应

{
    "_id": ObjectId("5859e80591c255c059a3da50"),
    "Records": {
        "Properties": [{
            "Property": {
                "Name": "YES"
            },
            "value": "1472065148"
        }]
    }
}

附加部分的更新:

有几种方法可以解决这个问题。事情可以稍微优化一下,但我会把它留给你。

选项1:

$ map应用传递的条件与每个结果元素中的字段之间的等于比较,并生成具有true和false值的数组。 $ anyElementTrue检查此数组,并且仅当数组中至少有一个true值时才返回true。匹配阶段仅包含匹配值为true的元素。

使用系统变量$$ ROOT保存数据的数据字段。

完成查询

db.dataset.aggregate([{
    $unwind: "$Records"
}, {
    $project: {
        "_id": 0,
        "matched": {
            "$anyElementTrue": {
                "$map": {
                    "input": "$Records.Properties",
                    "as": "result",
                    "in": {
                        "$and": [{
                            $eq: ["$$result.Property.Name", "LastWrite"]
                        }, {
                            $eq: ["$$result.value", "42"]
                        }]
                    }
                }
            }
        },
        "data": "$$ROOT"
    }
}, {
    "$match": {
        "matched": true
    }
}, {
    $unwind: "$data.Records"
}, {
    $unwind: "$data.Records.Properties"
}, {
    $match: {
        "data.Records.Properties.Property.Name": 'YES'
    }
}, {
    $project: {
        "_id": 0,
        "value": "$data.Records.Properties.value"
    }
}]).pretty()

选项2:

更好的选择(性能优越)所以如果你是支持$ redact的驱动程序,请使用它。

与上述版本类似,但这个版本将项目和匹配阶段合二为一。带有$ redact的$ cond用于匹配,当找到匹配时,它会保留完整的树或者丢弃它。

完成查询

db.dataset.aggregate([{
    $unwind: "$Records"
}, {
    "$redact": {
        "$cond": [{
                "$anyElementTrue": {
                    "$map": {
                        "input": "$Records.Properties",
                        "as": "result",
                        "in": {
                            "$and": [{
                                $eq: ["$$result.Property.Name", "LastWrite"]
                            }, {
                                $eq: ["$$result.value", "42"]
                            }]
                        }
                    }
                }
            },
            "$$KEEP",
            "$$PRUNE"
        ]
    }
}, {
    $unwind: "$Records.Properties"
}, {
    $match: {
        "Records.Properties.Property.Name": 'YES'
    }
}, {
    $project: {
        "_id": 0,
        "value": "$Records.Properties.value"
    }
}]).pretty()