mongodb - 如何在与 MongoDB 中的特定过滤器匹配的数组元素上创建索引?
问题描述
假设有一个对象集合,每个对象都包含一个元素数组,每个元素都包含字段attributeName
和attributeValue
. 如何在 上创建索引attributeValue
,但仅限于对应attributeName
等于特定值的值?
示例集合:
{ "_id": 0, "attributes":
[
{"attributeName": "name", "attributeValue": "John", ...},
{"attributeName": "age", "attributeValue": "30", ...}
]
},
{ "_id": 1, "attributes":
[
{"attributeName": "name", "attributeValue": "Brian", ...},
{"attributeName": "gender", "attributeValue": "male", ...}
]
},
{ "_id": 2, "attributes":
[
{"attributeName": "name", "attributeValue": "Kevin", ...},
{"attributeName": "age", "attributeValue": "35", ...}
]
}
"attributeName" == "age"
对于给定的示例,我们将如何为值 where (值30
和在这种情况下)创建索引35
?
解决方案
部分索引已由 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”和“attributes.attributeValue”为 30。尽管数组中有不同的元素,但它仍然与查询匹配定义。我相信我们只想在同一个子文档中找到具有年龄和 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", "attributes.attributeValue": 30, "attributes": { $elemMatch: { "attributeName": "age", "attributeValue": 30 } } } ).explain("allPlansExecution")
结论:
不能有针对性的部分过滤表达式,我们能做的最好的就是一个范围。如果在数组元素上使用部分索引,我们必须单独包含数组元素和 elemMatch 以利用索引。数据类型必须匹配。如果我用“30”(作为字符串)查询,它将找不到数据,也不会使用索引。
边注:
数组中键值对的索引称为属性模式。有关详细信息,请参阅https://www.mongodb.com/blog/post/building-with-patterns-the-attribute-pattern。复合索引首先使用值字段构建,然后是键字段。这是故意的,因为值字段可能更具选择性,并允许索引扫描更有效。
推荐阅读
- odata - 如何将 1.0 版 edmx 文件内容转换为 POJO?
- python - 通过 lxml etree 提取原始 XML
- apache-spark - 正确配置 spark 充分利用 EMR 集群资源
- java - 如何从字符串矩阵中获取元素的 int 值?
- php - 页面重新加载后会话变量为空
- java - 使用java从json中拆分时间值
- text - 如何向 Tesseract 4.0 添加新字体?
- python - 有没有在边缘中间用箭头绘制边缘的功能?
- spring - Spring boot 无法构建 Hibernate SessionFactory 不显示错误
- windows - 在 Windows 上,Git Bash vs Windows Power Shell vs 命令提示符有什么区别