Overview
在本指南中,您可以学习如何通过 MongoDB Java 驱动程序使用索引。
MongoDB 中的索引支持高效执行查询。如果没有索引,MongoDB 必须扫描集合中的每个文档(集合扫描),才能找到与每个查询匹配的文档。这些集合扫描很慢,可能会对应用程序的性能产生负面影响。如果查询存在适当的索引,MongoDB 就可以使用该索引来限制必须检查的文档。
索引还:
提示
查找要更新的文档时,更新操作会使用索引;查找要删除的文档时,删除操作会使用索引;聚合管道的某些阶段也会使用索引。
查询覆盖和性能
对 MongoDB 执行查询时,您的命令可以包含各种元素:
用于指定您要查找的字段和值的查询条件
影响查询执行的选项,例如读关注
用于指定 MongoDB 返回的字段的投影条件(可选)
排序标准,用于指定从 MongoDB 返回文档的顺序(可选)
当查询、投影和排序中指定的所有字段均位于同一索引时,MongoDB 会直接从此索引返回结果,而这也被称为覆盖查询。
重要
排序顺序
排序条件必须与索引的顺序一致或相反。
考虑字段上的索引, name按升序 (AZ) 排列,而age按降序 (9-0) 排列:
name_1_age_-1
当您按以下任一方式对数据进行排序时,MongoDB 会使用此索引:
name升序,age降序name降序,age升序
指定 name 和 age 升序或 name 和 age 降序排序需要进行内存中排序。
有关如何确保索引涵盖查询条件和投影的更多信息,请参阅 MongoDB 手册中有关查询覆盖范围的文章。
操作注意事项
若要提高查询性能,请对应用程序查询中经常出现的字段以及其他操作返回的排序结果中经常出现的字段建立索引。您添加的每个索引在活动状态下都会占用磁盘空间和内存,因此您应当跟踪索引内存和磁盘使用情况以进行容量规划。此外,当写入操作更新索引化字段时,MongoDB 还须更新相关索引。
由于MongoDB支持动态模式,因此应用程序可以查询事先不知道名称或任意名称的字段。MongoDB 4.2 引入了通配符索引来帮助支持这些查询。通配符索引并不是为了取代基于工作负载的索引规划而设计的。
有关设计数据模型和选择适合应用程序的索引的更多信息,请参阅 MongoDB Server 数据建模和索引。
索引类型
MongoDB 支持多种索引类型来进行数据查询。以下部分描述了最常见的索引类型,并提供了创建每种索引类型的示例代码。有关索引类型的完整列表,请参阅索引。
以下示例使用 createIndex() 方法创建各种索引以及以下设置:
import com.mongodb.DuplicateKeyException; import com.mongodb.MongoCommandException; import com.mongodb.client.*; import com.mongodb.client.model.IndexOptions; import com.mongodb.client.model.Indexes; import com.mongodb.client.model.Sorts; import com.mongodb.client.model.geojson.Point; import com.mongodb.client.model.geojson.Position; import org.apache.log4j.BasicConfigurator; import org.bson.Document; import org.bson.conversions.Bson; import static com.mongodb.client.model.Filters.*; import static com.mongodb.client.model.Projections.*;
final String uri = "mongodb+srv://<atlas-uri>/<dbname>?retryWrites=true&w=majority"; mongoClient = MongoClients.create(uri); database = mongoClient.getDatabase("sample_mflix"); collection = database.getCollection("movies");
单字段索引和复合索引
单字段索引
单字段索引是对集合文档中的单字段进行引用的索引。其提高了单字段查询和排序性能,并支持 TTL 索引,可在一定时间后或特定时钟时间自动从集合中删除文档。
注意
_id_ 索引是单字段索引的一个示例。创建新集合时,会在 _id 字段上自动创建此索引。
以下示例将对 title 字段按升序创建索引:
String resultCreateIndex = collection.createIndex(Indexes.ascending("title")); System.out.println(String.format("Index created: %s", resultCreateIndex));
以下是前面代码段中创建的索引涵盖的查询示例:
Bson filter = eq("title", "Batman"); Bson sort = Sorts.ascending("title"); Bson projection = fields(include("title"), excludeId()); FindIterable<Document> cursor = collection.find(filter).sort(sort).projection(projection);
有关更多信息,请参阅 MongoDB Server 手册中的单字段索引。
复合索引
复合索引包含对集合文档中多个字段的引用,从而提高查询和排序性能。
提示
在此处阅读有关复合索引、索引前缀和排序顺序的更多信息。
以下示例在 type 和 rated 字段上创建复合索引:
String resultCreateIndex = collection.createIndex(Indexes.ascending("type", "rated")); System.out.println(String.format("Index created: %s", resultCreateIndex));
以下是前面代码段中创建的索引涵盖的查询示例:
Bson filter = and(eq("type", "movie"), eq("rated", "G")); Bson sort = Sorts.ascending("type", "rated"); Bson projection = fields(include("type", "rated"), excludeId()); FindIterable<Document> cursor = collection.find(filter).sort(sort).projection(projection);
有关更多信息,请参阅 MongoDB Server 手册中的复合索引。
多键索引(数组字段索引)
多键索引可提高使用包含数组值的索引指定字段的查询性能。您可以使用与单字段或复合索引相同的语法定义多键索引。
以下示例在 rated、genres(字符串数组)和 title 字段上创建了复合多键索引:
String resultCreateIndex = collection.createIndex(Indexes.ascending("rated", "genres", "title")); System.out.println(String.format("Index created: %s", resultCreateIndex));
以下是前面代码段中创建的索引涵盖的查询示例:
Bson filter = and(eq("genres", "Animation"), eq("rated", "G")); Bson sort = Sorts.ascending("title"); Bson projection = fields(include("title", "rated"), excludeId()); FindIterable<Document> cursor = collection.find(filter).sort(sort).projection(projection);
在查询覆盖、索引绑定计算和排序行为方面,多键索引的行为与其他索引不同。要了解有关多键索引的更多信息,包括对其行为和限制的讨论,请参阅 MongoDB 手册中的多键索引页面。
MongoDB Search 和 Vector Search 索引
您可以使用Java驱动程序以编程方式管理MongoDB搜索和MongoDB Vector搜索搜索索引。
MongoDB搜索功能使您能够对MongoDB Atlas上托管的集合执行全文搜索。要了解有关 MongoDB 搜索的更多信息,请参阅 MongoDB 搜索索引 文档。
MongoDB 向量搜索使您能够对存储在 MongoDB Atlas 中的向量嵌入执行语义搜索。要学习;了解有关MongoDB Vector Search 的更多信息,请参阅 MongoDB Vector Search指南。
您可以在集合上调用以下方法来管理MongoDB 搜索 和 Vector 搜索 搜索索引:
createSearchIndex()(仅对MongoDB 搜索索引有效)createSearchIndexes()listSearchIndexes()updateSearchIndex()dropSearchIndex()
注意
MongoDB搜索索引管理方法异步运行。驱动程序方法可以在确认成功运行之前返回。要确定索引的当前状态,请调用 listSearchIndexes() 方法。
以下各节将提供代码示例,演示如何使用上述每种方法。
创建搜索索引
您可以使用 createSearchIndex() 方法创建 MongoDB 搜索索引。您不能使用此方法创建 Vector Search索引。
以下代码示例展示了如何创建MongoDB搜索索引:
Document searchIdx = new Document("mappings", new Document("dynamic", true)); collection.createSearchIndex("myIndex", searchIdx);
您可以使用 createSearchIndexes() 方法创建多个 MongoDB 搜索索引或一个或多个 Vector 搜索索引。您必须为每个索引创建并传递一个SearchIndexModel实例。
以下代码示例展示了如何在一次调用中创建“搜索”和“向量搜索”索引:
SearchIndexModel searchIdxMdl = new SearchIndexModel( "searchIdx", new Document("analyzer", "lucene.standard").append( "mappings", new Document("dynamic", true)), SearchIndexType.search() ); SearchIndexModel vectorSearchIdxMdl = new SearchIndexModel( "vsIdx", new Document( "fields", Arrays.asList( new Document("type", "vector") .append("path", "embeddings") .append("numDimensions", 1536) .append("similarity", "dotProduct") ) ), SearchIndexType.vectorSearch() ); collection.createSearchIndexes( Arrays.asList(searchIdxMdl, vectorSearchIdxMdl) );
搜索索引列表
您可以使用 listSearchIndexes() 方法返回集合的 MongoDB 搜索索引。
以下代码示例显示如何打印集合的搜索索引列表:
try (MongoCursor<Document> resultsCursor = collection.listSearchIndexes().iterator()) { while (resultsCursor.hasNext()) { System.out.println(resultsCursor.next()); } }
更新搜索索引
您可以使用 updateSearchIndex() 方法更新 MongoDB 搜索索引。
以下代码展示如何更新搜索索引:
collection.updateSearchIndex("myIndex", new Document("analyzer", "lucene.simple").append( "mappings", new Document("dynamic", false) .append("fields", new Document("title", new Document("type", "string"))) ) );
删除搜索索引
您可以使用 dropSearchIndex() 方法删除 MongoDB 搜索索引。
以下代码展示了如何从集合中删除搜索索引:
collection.dropSearchIndex("myIndex");
MongoDB自动嵌入搜索索引模型
You can use the Java driver to create an auto-embedding MongoDB Vector Search index by specifying the index definition as a Document and using the createSearchIndexes() method on your collection. This allows MongoDB to automatically generate vector embeddings for a specified text field by using a supported embedding model. The following example uses the voyage-4-large model to create an auto-embedding vector search index named myIndex on the plot field:
SearchIndexModel indexModel = new SearchIndexModel( "myIndex", new Document("fields", Collections.singletonList( new Document("type", "autoEmbed") .append("modality", "text") .append("model", "voyage-4-large") .append("path", "plot") ) ), SearchIndexType.vectorSearch() ); collection.createSearchIndexes(Collections.singletonList(indexModel));
This approach allows you to automate vector generation for text fields, making it easier to perform semantic search queries without manually generating and storing embeddings.
Text Indexes
文本索引支持对字符串内容进行文本查询。这些索引可以包括值为字符串或字符串元素大量的任何字段。 MongoDB支持各种语言的文本查询。创建索引时,您可以指定默认语言作为选项。
提示
MongoDB提供改进的全文搜索解决方案,MongoDB 搜索。要了解有关MongoDB搜索索引及其使用方法的更多信息,请参阅本指南的MongoDB搜索和向量搜索索引部分。
单个字段
以下示例在 plot 字段上创建一个文本索引:
try { String resultCreateIndex = collection.createIndex(Indexes.text("plot")); System.out.println(String.format("Index created: %s", resultCreateIndex)); // Prints a message if a text index already exists with a different configuration } catch (MongoCommandException e) { if (e.getErrorCodeName().equals("IndexOptionsConflict")) System.out.println("there's an existing text index with different options"); }
以下是使用前面代码片段中创建的索引的示例查询。请注意,由于文本索引不包含排序顺序,因此省略了 sort。
Bson filter = text("java coffee shop"); Bson projection = fields(include("fullplot"), excludeId()); FindIterable<Document> cursor = collection.find(filter).projection(projection);
多个字段
一个集合只能包含一个文本索引。如果要为多个文本字段创建文本索引,则必须创建复合索引。对复合索引中的所有文本字段运行文本查询。
以下代码段为 title 和 genre 字段创建复合文本索引:
collection.createIndex(Indexes.compoundIndex(Indexes.text("title"), Indexes.text("genre")));
如需了解更多信息,请参阅以下“服务器手册条目”:
地理空间索引
MongoDB 支持使用 2dsphere 索引查询地理空间坐标数据。借助 2dsphere 索引,您可以针对地理空间数据进行包含、相交和邻近范围方面的查询。有关查询地理空间数据的更多信息,请参阅地理空间查询。
要创建2dsphere索引,您必须指定仅包含GeoJSON对象的字段。 有关此类型的更多详细信息,请参阅有关GeoJSON对象的MongoDB Server手册页面。
以下样本文档中的 location.geo 字段来自 sample_mflix 数据库中的 theaters 集合,且为用于描述影院坐标的 GeoJSON 点对象:
{ "_id" : ObjectId("59a47286cfa9a3a73e51e75c"), "theaterId" : 104, "location" : { "address" : { "street1" : "5000 W 147th St", "city" : "Hawthorne", "state" : "CA", "zipcode" : "90250" }, "geo" : { "type" : "Point", "coordinates" : [ -118.36559, 33.897167 ] } } }
以下示例在 location.geo 字段上创建一个 2dsphere 索引:
重要
尝试在地理空间索引所覆盖的字段上创建地理空间索引会导致错误。
try { String resultCreateIndex = collection.createIndex(Indexes.geo2dsphere("location.geo")); System.out.println(String.format("Index created: %s", resultCreateIndex)); // Prints a message if a geospatial index already exists with a different configuration } catch (MongoCommandException e) { if (e.getErrorCodeName().equals("IndexOptionsConflict")) System.out.println("there's an existing geospatial index with different options"); }
以下是使用 "location.geo" 索引进行地理空间查询的示例。
// Stores the coordinates of the NY MongoDB headquarters Point refPoint = new Point(new Position(-73.98456, 40.7612)); // Retrieves documents that represent locations up to 1000 meters from the specified point directly from the geospatial index // Creates a filter to match a document Bson filter = near("location.geo", refPoint, 1000.0, 0.0); FindIterable<Document> cursor = collection.find(filter);
MongoDB 还支持 2d 索引,用于计算欧几里德平面上的距离,以及处理 MongoDB 2.2 及更早版本中使用的“旧版坐标对”语法。有关更多信息,请参阅 MongoDB Server 手册中的“地理空间查询”页面。
Unique Indexes
唯一索引可确保索引字段不存储重复值。默认情况下,MongoDB 在创建集合期间会在 _id 字段上创建唯一索引。要创建唯一索引,请指定要防止重复的字段或字段组合,并将 unique 选项设置为 true。
以下示例在 theaterId 字段上创建唯一的降序索引:
try { IndexOptions indexOptions = new IndexOptions().unique(true); String resultCreateIndex = collection.createIndex(Indexes.descending("theaterId"), indexOptions); System.out.println(String.format("Index created: %s", resultCreateIndex)); // Prints a message if the "theaterID" field contains duplicate values } catch (DuplicateKeyException e) { System.out.printf("duplicate field values encountered, couldn't create index: \t%s\n", e); }
重要
如果您执行写操作,存储违反唯一索引的重复值,则 MongoDB Java 驱动程序会引发 DuplicateKeyException,并且 MongoDB 会抛出类似以下内容的错误:
E11000 duplicate key error index
有关更多信息,请参阅 MongoDB 服务器手册中的唯一索引页面。
通配符索引 (Wildcard Indexes)
通配符索引可对未知或任意字段进行查询。如果您使用的是动态模式,则这些索引可能很有用。
以下示例将对 location 字段的所有值(包括嵌套在子文档和数组中的值)创建升序通配符索引:
String resultCreateIndex = collection.createIndex(Indexes.ascending("location.$**")); System.out.println(String.format("Index created: %s", resultCreateIndex));
有关更多信息,请参阅 MongoDB 服务器手册中的通配符索引页面。
聚集索引
集群化索引指示集合存储按键值排序的文档。要创建集群化索引,请在创建集合时指定集群化索引选项,其中 _id 字段作为键,唯一字段为 true。
以下示例对 vendors 集合中的 _id 字段创建集群索引:
MongoDatabase database = mongoClient.getDatabase("tea"); ClusteredIndexOptions clusteredIndexOptions = new ClusteredIndexOptions(new Document("_id", 1), true); CreateCollectionOptions createCollectionOptions = new CreateCollectionOptions().clusteredIndexOptions(clusteredIndexOptions); database.createCollection("vendors", createCollectionOptions);
有关详细信息,请参阅 MongoDB Server 手册部分:
删除索引
您可以删除针对 _id 字段的所有未使用索引,但默认唯一索引除外。
以下几节将介绍了删除索引的方法:
使用索引规范文档
使用索引名称字段
使用通配符删除所有索引
使用索引规范文档删除索引
将索引规范文档传递给 dropIndex() 方法以从集合中删除索引。索引规范文档是一个 Bson 实例,用于指定指定字段的索引类型。
以下代码片段将删除某一集合中针对 title 字段的升序索引:
collection.dropIndex(Indexes.ascending("title"));
重要
如果要删除文本索引,则必须改用索引的名称。有关详细信息,请参阅使用名称字段删除索引部分。
使用名称字段删除索引
将索引的 name 字段传递给 dropIndex() 方法,以从集合中删除索引。
要查找索引名称,请使用 listIndexes() 方法,查看索引中 name 字段的值。
以下代码片段将检索并打印集合中的所有索引:
collection.listIndexes().forEach(doc -> System.out.println(doc.toJson()));
如果您对包含文本索引的集合调用 listIndex(),输出可能如下所示:
{ "v": 2, "key": {"_id": 1}, "name": "_id_" } { "v": 2, "key": {"_fts": "text", "_ftsx": 1}, "name": "title_text", "weights": {"title": 1}, "default_language": "english", "language_override": "language", "textIndexVersion": 3 }
输出结果显示现有索引的名称为 "_id" 和 "title_text"。
以下代码片段将从集合中删除“title_text”索引:
collection.dropIndex("title_text");
注意
不能从复合文本索引中删除单个字段。您必须删除整个索引,然后创建新索引,才能更新索引字段。
使用通配符删除索引
从 MongoDB 4.2 开始,您可以在集合上调用 dropIndexes() 方法来删除所有索引:
collection.dropIndexes();
对于 MongoDB 的早期版本,请将“*”作为参数传递给对集合上 dropIndex() 的调用:
collection.dropIndex("*");
有关本节中方法的更多信息,请参阅以下 API 文档: