MongoDB对象上的部分匹配

时间:2018-12-18 13:42:16

标签: mongodb mongodb-query

例如,我有一个这样的文档:

{
   name: "Test",
   params: {
     device: "windows",
     gender: "m",
     age: 28
   }
}

现在我得到以下参数输入:

{
   device: "windows",
   gender: "m"
}

在这种情况下,我想查找与输入对象部分匹配的所有文档。有没有简单的方法可以解决此问题?我试过$ elemMatch,但这似乎只能用于数组。

3 个答案:

答案 0 :(得分:0)

如果我正确理解了您的问题,则您有一个输入对象,其中可能包含主文档的params对象中的一些字段,并且按任何顺序,例如:

{
   device: "windows",
   gender: "m"
}

{
   gender: "m",
   device: "windows"
}

{
   device: "windows",
   age: 28
}

,并且仅当输入对象中的 all 域存在于主文档中时,您才想匹配:

{
   device: "linux",    // NO MATCH
   gender: "m"
}

{
   gender: "m",        // MATCH
   device: "windows"
}

{
   device: "windows",  // NO MATCH
   age: 29
}

正确吗?


选项1

您的param对象是否始终仅包含这三个字段(devicegenderage)?

如果是这样,您可以在根级别手动投影每个投影,进行匹配,然后再次“投影”:

db.my_collection.aggregate([
    {
        $project: {
            name: 1, 
            params: 1, 
            device: "$params.device",  // add these three for your match stage
            gender: "$params.gender", 
            age: "$params.age"
        }
    },
    {
        $match: input_params
    },
    {
        $project: {name: 1, params: 1}  // back to original
    }
]);

但是我假设您想对params对象中的任意数量的字段执行此操作。


选项2

有一种方法可以处理输入的对象?如果是这样,您可以在所有字段前面加上“ params.”:

let input_params = 
{
   device: "windows",
   gender: "m"
};

let new_input_params =
{
   "params.device": "windows",
   "params.gender": "m"
};

那么您的查询就是:

db.my_collection.find(new_input_params);

选项3

如果您无法修改输入,则可以使用$replaceRoot聚合运算符(从3.4版开始,归功于this answer)来展平{{ 1}}进入根文档。由于这将用嵌入的文档替换根文档,因此您需要首先提取感兴趣的字段,至少要提取params字段:

_id

这将匹配文档并保留db.my_collection.aggregate([ { $addFields: {"params.id": "$_id"} // save _id inside the params doc, // as you will lose the original one }, { $replaceRoot: {newRoot: "$params"} }, { $match: input_params }, ... ]); 字段,您可以使用该字段再次通过_id获取文档的其余部分:

$lookup

这将以以下格式获取您的文档:

    ...
    {
        $lookup: {
            from: "my_collection", 
            localField: "id", 
            foreignField: "_id", 
            as: "doc"
        }
    },
    ...

要全面了解并恢复原始文档格式,您可以:

{ 
  "device" : "windows", 
  "gender" : "m", 
  "age" : 28, 
  "id" : ObjectId("XXX"), 
  "doc" : [ 
    {
      "_id" : ObjectId("XXX"), 
      "name" : "Test", 
      "params" : { 
        "device" : "windows", 
        "gender" : "m", 
        "age" : 28 
      }
    }
  ]
}

在撰写本文时,我不敢相信没有一种更干净的方法可以单独使用MongoDB来做到这一点,但我想不出任何办法。

我希望这会有所帮助!

答案 1 :(得分:0)

您可以使用$or运算符对 params 对象中的字段进行部分比较。您可以这样使用:

db.collection.find({
  $or: [
    {
      "params.device": "windows"
    },
    {
      "params.gender": "m"
    }
  ]
})

答案 2 :(得分:0)

Using projection

文档中可能有许多字段,但并非所有这些字段在需要时可能都是必要且重要的。在这种情况下,您只能使用 投影 在样本中包括必填字段。

  

默认情况下,MongoDB中的查询返回匹配的所有字段   文件。限制MongoDB发送到的数据量   应用程序,您可以包括一个投影文档以指定或   限制字段​​返回。

例如,您先前已将以下文档添加到数据库:

> db.users.insertOne({"name": "Jennifer", "gender": "female", languages: ["english", "spanish"]})
> db.users.insertOne({"name": "Alex", "gender": "male", languages: ["english", "french"]})
> db.users.insertOne({"name": "Maria", "gender": "female", languages: ["english", "german"]})

现在您要显示与字段gender匹配且具有值female的信息,因此您可以通过查询指定它:

db.users.find({gender: "female"}, {name: 1})

该操作返回以下文档:

{ "name" : "Jennifer", "gender" : "female" }
{ "name" : "Maria", "gender" : "female" }

<value>可以是以下任意一项:

{ field1: <value>, field2: <value> ... }
  • 1或true,以将字段包括在退货单中。
  • 0或false以排除该字段。
  • 使用投影运算符表达。

此外,如果您不想具体化选择内容,但希望显示所有文档,那么我们可以将第一个方括号留空:

db.users.find({}, {name: 1, _id: 0})