MongoDB:多个$elemMatch

19

我有类似于这样结构的MongoDB文档:

{_id: ObjectId("53d760721423030c7e14266f"),
fruit: 'apple',
vitamins: [
    {
     _id: 1,
     name: 'B7',
     usefulness: 'useful',
     state: 'free',
    }
    {
     _id: 2,
     name: 'A1',
     usefulness: 'useful',
     state: 'free',
    }
    {
     _id: 3,
     name: 'A1',
     usefulness: 'useful',
     state: 'non_free',
    }
  ]
}
{_id: ObjectId("53d760721423030c7e142670"),
fruit: 'grape',
vitamins: [
    {
     _id: 4,
     name: 'B6',
     usefulness: 'useful',
     state: 'free',
    }
    {
     _id: 5,
     name: 'A1',
     usefulness: 'useful',
     state: 'non_free',
    }
    {
     _id: 6,
     name: 'Q5',
     usefulness: 'non_useful',
     state: 'non_free',
    }
  ]
}
我想查询并获取所有既有 {name: 'A1', state: 'non_free'},又有 {name: 'B7', state: 'free'} 的水果。如果无法获取它们,至少我希望能够计算这些条目,并且如果 pymongo 中存在等效的代码,我想知道如何编写它。
对于给定的示例,我只想检索苹果(第一个)文档。
如果我使用 $elemMatch ,它仅适用于一个条件,而不是两个条件。例如,如果我查询 find({'vitamins': {'$elemMatch': {'name': 'A1', 'state': 'non_free'}, '$elemMatch': {'name': 'B7', 'state': 'free'}}}) ,它将检索出所有具有 {'name': 'B7', 'state': 'free'} 的水果。
3个回答

42
在这种情况下,您可以使用$and运算符
尝试这个查询:
find({
    $and: [
         {'vitamins': {'$elemMatch': {'name': 'A1', 'state': 'non_free'} } },
         {'vitamins': {'$elemMatch': {'name': 'B7', 'state': 'free'} } }
    ]
});

为了解释为什么你只收到与第二个条件匹配的结果:你传递给MongoDB的每个 {} 内部的对象都是键/值对。每个键在一个对象中只能存在一次。当你尝试将一个值分配给同一个键两次时,第二次分配会覆盖第一次。在你的情况下,你在同一个对象中为键$elemMatch分配了两个不同的值,因此第一个被忽略了。实际传递到MongoDB的查询只是find({'vitamins': {'$elemMatch': {'name': 'B7', 'state': 'free'}}})

每当你需要将相同的运算符应用于同一个键两次时,你需要使用$or$and


1
但我需要水果含有两种维生素,而不是其中任何一种。 - gevra
1
@gevra 不好意思,那种情况下请使用 $and 而不是 $or。已更新答案。 - Philipp
请使用 $and 替代 $or - Barno
@Philipp 你好快啊 :) - Barno
1
能否动态地为未知数量的 $elemMatch 进行类似的操作? - Puma
1
@chillinpuma 你可以在应用程序中动态构建传递给 $and 的数组。 - Philipp

8
var fruits = db.fruits.find({
    "vitamins": {
        $all: [{
            $elemMatch: {
                "name": "A1",
                "state": "non_free"
            }
        }, {
            $elemMatch: {
                "name": "B7",
                "state": "free"
            }
        }]
    }
})

请解释你的答案 - Mo.
查询找到满足以下条件的文档: 1)名称为B7且状态为free的子文档完全匹配 2)名称为A1且状态为non_free的子文档完全匹配 数组应同时满足这两个条件,因此我使用了$all运算符。 - Karthik Bashyam
根据stackoverflow的建议,您应该解释答案。 - Mo.
注意:"$all等同于指定值的$and操作",参见$all operator docs - ppython

-2
let query = [];
  query.push({
    id: product.id,
  });
  query.push({ date });
  for (const slot of slots) {
    query.push({
      slots: {
        $elemMatch: {
          id: slot.id,
          spots: { $gte: slot.spots },
        },
      },
    });
  }
  const cal = await Product.findOne({ $and: query });

这似乎不是由问题标记为本地Mongo查询或Python代码。 - ray

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