在MongoDB的通配符索引中包含特定字段

发布于

在MongoDB中创建通配符索引时,您可以选择指定单个字段,所有字段或仅指定一些字段。

您可以使用该参数在通配符索引中包含或排除特定字段路径。本文提供了在通配符索引中包含特定字段的示例。wildcardProjection

示例文档

假设我们有一个包含以下文档的集合: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”) } }

我们可以在整个集合上创建一个通配符索引,但只包含我们想要的字段。

创建索引

下面是一个示例:

db.pets.createIndex(
  { "$**" : 1 },
  {
    "wildcardProjection" : {
      "details.type" : 1,
      "details.born" : 1
    }
  }
)

输出:

{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 1, “numIndexesAfter” : 2, “ok” : 1 }

该部分是创建通配符索引的部分,该部分是指定要包含哪些字段的部分。在本例中,我们包含了字段和字段。为它们指定值 显式将它们包含在索引中。{ "$**" : 1 }``wildcardProjection``details.type``details.born``1

查看索引

我们可以通过调用方法查看集合上的索引:getIndexes()

db.pets.getIndexes()

输出:

[
	{
		"v" : 2,
		"key" : {
			"_id" : 1
		},
		"name" : "_id_"
	},
	{
		"v" : 2,
		"key" : {
			"$**" : 1
		},
		"name" : "$**_1",
		"wildcardProjection" : {
			"details.type" : 1,
			"details.born" : 1
		}
	}
]

我们可以看到有两个索引。

  • 第一个索引在字段上。这是在创建集合时创建的(MongoDB在创建集合期间在_id字段上创建一个唯一的索引)。_id
  • 第二个索引是我们的通配符索引。我们可以看到它已被自动命名,它包括我们指定的字段。$**_1

测试索引

我们还可以运行一些查询来查看我们的索引是否会被使用。

理论上,以下查询应使用该索引:

db.pets.find( { "details.type" : "Dog" } )

为了测试这一点,我们可以附加方法来查看查询计划:explain()

db.pets.find( { "details.type" : "Dog" } ).explain()

输出:

{
	"queryPlanner" : {
		"plannerVersion" : 1,
		"namespace" : "PetHotel.pets",
		"indexFilterSet" : false,
		"parsedQuery" : {
			"details.type" : {
				"$eq" : "Dog"
			}
		},
		"queryHash" : "F1C5286F",
		"planCacheKey" : "5326DE93",
		"winningPlan" : {
			"stage" : "FETCH",
			"inputStage" : {
				"stage" : "IXSCAN",
				"keyPattern" : {
					"$_path" : 1,
					"details.type" : 1
				},
				"indexName" : "$**_1",
				"isMultiKey" : false,
				"multiKeyPaths" : {
					"$_path" : [ ],
					"details.type" : [ ]
				},
				"isUnique" : false,
				"isSparse" : false,
				"isPartial" : false,
				"indexVersion" : 2,
				"direction" : "forward",
				"indexBounds" : {
					"$_path" : [
						"[\"details.type\", \"details.type\"]"
					],
					"details.type" : [
						"[\"Dog\", \"Dog\"]"
					]
				}
			}
		},
		"rejectedPlans" : [ ]
	},
	"ok" : 1
}

我们可以看到它在我们的索引中使用了索引扫描(IXSCAN)。

与此相反,当我们对_未_包含在索引中的字段运行查询时,会发生什么情况:

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),因此正如预期的那样,它没有使用索引。