在 MongoDB 中创建通配符索引
在MongoDB中创建索引有几种方法,从MongoDB 4.2开始,我们可以创建通配符索引。
通配符索引可以被认为是一种过滤器,它自动匹配集合中的任何字段、子文档或数组,然后为这些匹配项编制索引。
如果您的文档包含不同层次结构中具有不同字段的非结构化数据,则这可能很有用。在这种情况下,无法预测索引应该是什么,因为您不知道每个文档中将包含哪些数据。
通配符索引可用于此类非结构化数据,因为它们为字段的所有标量值编制索引,自动递归到任何子文档或数组中,并为子文档/数组中的所有标量字段编制索引。
示例集合
通配符索引并非适用于所有集合。您只会对某些集合创建通配符索引,其中包含包含不同层次结构中不同字段的非结构化数据的文档。
下面是一个名为 pets 的集合示例,该集合非常适合创建通配符索引:
{
"_id" : 1,
"name" : "Wag",
"details" : {
"type" : "Dog",
"weight" : 20,
"awards" : {
"Florida Dog Awards" : "Top Dog",
"New York Marathon" : "Fastest Dog",
"Sumo 2020" : "Biggest Dog"
}
}
}
{
"_id" : 2,
"name" : "Fetch",
"details" : {
"born" : ISODate("2020-06-22T14:00:00Z"),
"color" : "Black"
}
}
{
"_id" : 3,
"name" : "Scratch",
"details" : {
"eats" : [
"Mouse Porridge",
"Bird Soup",
"Caviar"
],
"type" : "Cat",
"born" : ISODate("2020-12-19T14:00:00Z")
}
}
此集合中的 3 个文档中的每一个都有一个字段,但它们在该字段中包含不同的字段。这是不一致的。这通常会使创建索引变得困难,因为我们不知道每个文档中将包含哪些字段。在仔细分析可能的文档结构后,我们可能需要创建多个索引。details
幸运的是,我们可以创建一个通配符索引。
但首先,让我们看一下查询其中一个字段时查询计划的外观。想象一下,我们想找出哪只狗在纽约马拉松比赛中获得了“最快的狗”奖。我们可以执行以下操作:
db.pets.find( { "details.awards.New York Marathon" : "Fastest Dog" } )
如果我们想检查查询计划,我们可以附加到末尾:explain()
db.pets.find( { "details.awards.New York Marathon" : "Fastest Dog" } ).explain()
这将返回以下内容:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "PetHotel.pets",
"indexFilterSet" : false,
"parsedQuery" : {
"details.awards.New York Marathon" : {
"$eq" : "Fastest Dog"
}
},
"queryHash" : "EC0D5185",
"planCacheKey" : "EC0D5185",
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"details.awards.New York Marathon" : {
"$eq" : "Fastest Dog"
}
},
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"ok" : 1
}
这告诉我们它将进行集合扫描(COLLSCAN),这意味着它必须扫描每个文档以查找该字段。
创建通配符索引
下面是为上述集合创建通配符索引的示例。
db.pets.createIndex({ "details.$**": 1 });
输出:
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 1, “numIndexesAfter” : 2, “ok” : 1 }
就是这样。已创建通配符索引。
为了创建通配符索引,我们使用了要在其上创建索引的字段名称(在本例中为字段),然后我们用点 () 附加它,然后是重要的部分,即部分。details``.``$**
指定应从此字段及其所有子文档创建通配符索引。$**
前缀 with 会将通配符索引的范围限制为仅字段。$**``details``details
现在让我们重新检查上述查询的查询计划:
db.pets.find( { "details.awards.New York Marathon" : "Fastest Dog" } ).explain()
结果:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "PetHotel.pets",
"indexFilterSet" : false,
"parsedQuery" : {
"details.awards.New York Marathon" : {
"$eq" : "Fastest Dog"
}
},
"queryHash" : "EC0D5185",
"planCacheKey" : "7DFA23ED",
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"$_path" : 1,
"details.awards.New York Marathon" : 1
},
"indexName" : "details.$**_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"$_path" : [ ],
"details.awards.New York Marathon" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"$_path" : [
"[\"details.awards.New York Marathon\", \"details.awards.New York Marathon\"]"
],
"details.awards.New York Marathon" : [
"[\"Fastest Dog\", \"Fastest Dog\"]"
]
}
}
},
"rejectedPlans" : [ ]
},
"ok" : 1
}
这次,集合扫描 (COLLSCAN) 已被我们新创建的通配符索引上的索引扫描 (IXSCAN) 所取代。
我们字段中的每个字段都已作为路径/值编制索引,并且层次结构中的每个字段的索引中都有一个条目。其中字段值是子文档(例如我们的。 字段),索引已下降到子文档中并重复该过程。details``awards
在所有字段路径上创建通配符索引
在前面的示例中,我们在单个字段路径上创建了一个通配符索引。只需使用 即可在所有字段路径上创建通配符索引,而无需在它前面加上字段前缀。$**
例如,我们可以这样做:
db.pets.createIndex({ "$**": 1 });
这将在所有字段路径上创建一个通配符索引。
实际上,这并不完全正确。默认情况下,不会在字段上创建通配符索引。要包含该字段,您需要将其包含在文档中。_id``_id``wildcardProjection
无法创建通配符索引?检查此设置。
必须至少创建通配符索引。mongod``featureCompatibilityVersion``4.2
您可以使用以下代码检查此设置:
db.adminCommand(
{
getParameter: 1,
featureCompatibilityVersion: 1
}
)
您可以使用以下命令进行设置:setFeatureCompatibilityVersion
db.adminCommand( { setFeatureCompatibilityVersion: "4.4" } )
该命令需要在数据库中运行。setFeatureCompatibilityVersion``admin