如何在与 MongoDB 中的特定过滤器匹配的数组元素上创建索引?

问题描述

假设有一个对象集合,每个对象包含一个元素数组,每个元素包含字段 attributeNameattributeValue。如何在 attributeValue 上创建索引,但仅针对其对应的 attributeName 等于特定值的值?

示例集合:

  { "_id": 0,"attributes": 
    [
      {"attributeName": "name","attributeValue": "John",...},{"attributeName": "age","attributeValue": "30",...}
    ]
  },{ "_id": 1,"attributeValue": "Brian",{"attributeName": "gender","attributeValue": "male",{ "_id": 2,"attributeValue": "Kevin","attributeValue": "35",...}
    ]
  }

对于给定的示例,我们如何为 "attributeName" == "age" 的值(在本例中为 3035)创建索引?

解决方法

MongoDB 不支持这种方式的索引。

您可以使用部分索引来确定要索引哪些文档。

对于每个索引文档,数组的所有元素都将包含在索引中。

,

部分索引已被 2 个用户建议。但是即使是部分索引也有这个查询的问题。如果我了解您的要求,您希望仅索引具有 {"name": "age": 30} OR {"name": "age","age": 35 } 属性元素的文档。您的原始文档将年龄显示为字符串而不是整数,但我相信整数足以进行此讨论。

部分过滤器表达式不允许 IN 条件或 OR 条件,但允许 AND 条件。此外,我们不能在同一个字段上创建两个几乎相同的索引,Mongo 对此进行了限制。由于这些原因,我们无法在 30 或 35 上创建索引,但可以在 BETWEEN 30 和 35 上创建索引。

db.test.createIndex(
   { "attributes.attributeValue": 1,"attributes.attributeName": 1 },{
       partialFilterExpression: 
       {
           $and:
           [
               {"attributes.attributeName": "age"},{"attributes.attributeValue": {$gte: 30} },{"attributes.attributeValue": { $lte: 35} }
           ]
       }
   }
)

现在查询这些数据并利用索引完全是另一回事。

我们可以通过明显的方式查询文档...

db.test.find({"attributes.attributeValue": 30,"attributes.attributeName": "age"}).pretty()

...但这可能不会导致我们想要的结果。例如,考虑这个文档...

{ "_id": 3,"attributes": 
    [
      {"attributeName": "name","attributeValue": "Alisa"},{"attributeName": "age","attributeValue": 17},{"attributeName": "favoriteNumber","attributeValue": 30}
    ]
  }

这个文档将被上面的查询返回,因为作为一个文档,它既有包含“age”的“attributes.attributeName”,又有 30 的“attributes.attributeValue”。尽管数组中有不同的元素,但仍然是匹配查询定义。我相信我们只想在同一个子文档中找到具有年龄和 30 的“属性”文档的文档。为此,我们需要 elemMatch...

db.test.find( { "attributes": { $elemMatch: { "attributeName": "age","attributeValue": 30 } } } ).pretty()

当我使用这个查询时,我收到了预期的结果,但是在评估解释计划时我表明这没有使用我的索引。这是执行集合扫描...

db.test.find( { "attributes": { $elemMatch: { "attributeName": "age","attributeValue": 30 } } } ).explain("allPlansExecution")

...那是什么?事实证明,为了使用这个索引,我们需要有两种风格的查询。我们需要单独包含每个字段,但也需要使用 elemMatch...

db.test.find( { "attributes.attributeName": "age","attributes.attributeValue": 30,"attributes": { $elemMatch: { "attributeName": "age","attributeValue": 30 } } } ).pretty()

.. 现在这个查询给出了正确的结果并且它利用了索引....

db.test.find( { "attributes.attributeName": "age","attributeValue": 30 } } } ).explain("allPlansExecution")

结论:

不能有针对性的部分过滤器表达式,我们能做的最好是一个范围。如果在数组元素上使用部分索引,我们必须将数组元素单独包含在 elemMatch 中以利用索引。数据类型必须匹配。如果我使用“30”(作为字符串)查询,它将找不到数据,也不会使用索引。

旁注:

对数组中的键值对进行索引称为属性模式。有关详细信息,请参阅 https://www.mongodb.com/blog/post/building-with-patterns-the-attribute-pattern。复合索引首先使用值字段构建,然后是键字段。这是有意为之,因为值字段可能更具选择性,并使索引扫描更有效。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...