Indexes
On this page
- Overview
- Query Coverage and Performance
- Operational Considerations
- Index Types
- Single Field and Compound Indexes
- Multikey Indexes (Indexes on Array Fields)
- Atlas Search and Vector Search Indexes
- Text Indexes
- Geospatial Indexes
- Unique Indexes
- Clustered Indexes
- Remove an Index
- Remove an Index Using an Index Specification Document
- Remove an Index Using a Name Field
- Remove an Index Using a Wildcard Character
Overview
In this guide, you can learn how to create and manage indexes by using the MongoDB Kotlin Driver.
Indexes support the efficient execution of queries in MongoDB. Without indexes, MongoDB must scan every document in a collection (a collection scan) to find the documents that match each query. These collection scans are slow and can negatively affect the performance of your application. If an appropriate index exists for a query, MongoDB can use the index to limit the documents that the query must inspect.
Indexes also have the following benefits:
Indexes allow efficient sorting.
Indexes enable special capabilities such as geospatial queries.
Indexes allow the creation of constraints to ensure a field value is unique.
To learn more, see Indexes in the Server manual.
Tip
Update operations use indexes when finding documents to update, and delete operations use indexes when finding documents to delete. Certain stages in the aggregation pipeline also use indexes to improve performance.
Query Coverage and Performance
When you execute a query against MongoDB, your command can include various elements:
Query criteria that specify fields and values you are looking for
Options that affect the query's execution, such as the read concern
Projection criteria to specify the fields MongoDB returns (optional)
Sort criteria to specify the order of documents returned from MongoDB (optional)
When all the fields specified in the query, projection, and sort are in the same index, MongoDB returns results directly from the index, also called a covered query.
Important
Sort Order
Sort criteria must match or invert the order of the index.
Consider an index on the field name
in ascending order (A-Z) and age
in descending order (9-0):
name_1_age_-1
MongoDB uses this index when you sort your data in either of the following ways:
name
ascending,age
descendingname
descending,age
ascending
Specifying a sort order of name
and age ascending or name and age
descending requires an in-memory sort.
For more information on how to ensure your index covers your query criteria and projection, see the Server manual articles on query coverage.
Operational Considerations
The following guidelines describe how you can optimize the way your application uses indexes:
To improve query performance, build indexes on fields that appear often in your application's queries and operations that return sorted results.
Track index memory and disk usage for capacity planning, because each index that you add consumes disk space and memory when active.
Avoid adding indexes that you infrequently use. Note that when a write operation updates an indexed field, MongoDB updates the related index.
Since MongoDB supports dynamic schemas, applications can query against fields whose names cannot be known in advance or are arbitrary. MongoDB 4.2 introduced wildcard indexes to help support these queries. Wildcard indexes are not designed to replace workload-based index planning.
For more information on designing your data model and choosing indexes appropriate for your application, see the MongoDB server documentation on Indexing Strategies and Data Modeling and Indexes.
Index Types
MongoDB supports several different index types to support querying your data. The following sections describe the most common index types and provide sample code for creating each index type. For a full list of index types, see Indexes in the Server manual.
Tip
The Kotlin driver provides the Indexes class to create and manage indexes. This class includes static factory methods to create index specification documents for different MongoDB index key types.
The following examples use the createIndex() method to create various indexes, and the following data classes to model data in MongoDB:
// Data class for the movies collection data class Movie( val title: String, val year: Int, val cast: List<String>, val genres: List<String>, val type: String, val rated: String, val plot: String, val fullplot: String, ) // Data class for the theaters collection data class Theater( val theaterId: Int, val location: Location ) { data class Location( val address: Address, val geo: Point ) { data class Address( val street1: String, val city: String, val state: String, val zipcode: String ) } }
Single Field and Compound Indexes
Single Field Indexes
Single field indexes are indexes with a reference to a single field within a collection's documents. They improve single field query and sort performance, and support TTL Indexes that automatically remove documents from a collection after a certain amount of time or at a specific clock time.
Note
The _id_
index is an example of a single field index. This index is automatically created on the _id
field
when a new collection is created.
The following example creates an index in ascending order on the title
field:
val resultCreateIndex = moviesCollection.createIndex(Indexes.ascending(Movie::title.name)) println("Index created: $resultCreateIndex")
Index created: title_1
The following is an example of a query that is covered by the index created in the preceding code snippet:
val filter = Filters.eq(Movie::title.name, "The Dark Knight") val sort = Sorts.ascending(Movie::title.name) val projection = Projections.fields( Projections.include(Movie::title.name), Projections.excludeId() ) data class Results(val title: String) val resultsFlow = moviesCollection.find<Results>(filter).sort(sort).projection(projection) resultsFlow.collect { println(it) }
See the MongoDB server manual section on single field indexes for more information.
Compound Indexes
Compound indexes hold references to multiple fields within a collection's documents, improving query and sort performance.
Tip
Read more about compound indexes, index prefixes, and sort order here.
The following example creates a compound index on the type
and rated
fields:
val resultCreateIndex = moviesCollection.createIndex(Indexes.ascending(Movie::type.name, Movie::rated.name)) println("Index created: $resultCreateIndex")
Index created: type_1_rated_1
The following is an example of a query that is covered by the index created in the preceding code snippet:
val filter = Filters.and( Filters.eq(Movie::type.name, "movie"), Filters.eq(Movie::rated.name, "G") ) val sort = Sorts.ascending(Movie::type.name, Movie::rated.name) val projection = Projections.fields( Projections.include(Movie::type.name, Movie::rated.name), Projections.excludeId() ) val resultsFlow = moviesCollection.find(filter).sort(sort).projection(projection) resultsFlow.collect { println(it) }
See the MongoDB server manual section on Compound indexes for more information.
Multikey Indexes (Indexes on Array Fields)
Multikey indexes are indexes that improve performance for queries that specify a field with an index that contains an array value. You can define a multikey index using the same syntax as a single field or compound index.
The following example creates a compound, multikey index on the rated
, genres
(an array of
Strings), and title
fields:
val resultCreateIndex = moviesCollection.createIndex(Indexes.ascending(Movie::rated.name, Movie::genres.name, Movie::title.name)) println("Index created: $resultCreateIndex")
Index created: rated_1_genres_1_title_1
The following is an example of a query that is covered by the index created in the preceding code snippet:
val filter = Filters.and( Filters.eq(Movie::genres.name, "Animation"), Filters.eq(Movie::rated.name, "G") ) val sort = Sorts.ascending(Movie::title.name) val projection = Projections.fields( Projections.include(Movie::title.name, Movie::rated.name), Projections.excludeId() ) val resultsFlow = moviesCollection.find(filter).sort(sort).projection(projection) resultsFlow.collect { println(it) }
Multikey indexes behave differently from other indexes in terms of query coverage, index-bound computation, and sort behavior. To learn more about multikey indexes, including a discussion of their behavior and limitations, see Multikey Indexes in the Server manual.
Atlas Search and Vector Search Indexes
You can programmatically manage your Atlas Search and Atlas Vector Search indexes by using the Kotlin driver.
The Atlas Search feature enables you to perform full-text searches on collections hosted on MongoDB Atlas. To learn more about MongoDB Atlas Search, see the Atlas Search Indexes documentation.
Atlas Vector Search enables you to perform semantic searches on vector embeddings stored in MongoDB Atlas. To learn more about Atlas Vector Search, see the Atlas Vector Search section in the Aggregates Builder guide.
You can call the following methods on a collection to manage your Atlas Search and Vector Search indexes:
createSearchIndex()
createSearchIndexes()
listSearchIndexes()
updateSearchIndex()
dropSearchIndex()
Note
The Atlas Search index-management methods run asynchronously. The
driver methods can return before confirming that they ran
successfully. To determine the current status of the indexes, call the
listSearchIndexes()
method.
The following sections provide code examples that demonstrate how to use each of the preceding methods.
Create a Search Index
You can use the createSearchIndex() and createSearchIndexes() methods to create Atlas Search and Vector Search indexes on a collection.
The following code example shows how to create an Atlas Search index:
val searchIdx = Document( "mappings", Document("dynamic", true) ) val resultCreateIndex = moviesCollection.createSearchIndex("myIndex", searchIdx)
To create multiple Search or Vector Search indexes, you must create a SearchIndexModel instance for each index.
The following code example shows how to create Search and Vector Search indexes in one call:
val searchIdxMdl = SearchIndexModel( "searchIdx", Document("analyzer", "lucene.standard").append( "mappings", Document("dynamic", true) ), SearchIndexType.search() ) val vectorSearchIdxMdl = SearchIndexModel( "vsIdx", Document( "fields", listOf( Document("type", "vector") .append("path", "embeddings") .append("numDimensions", 1536) .append("similarity", "dotProduct") ) ), SearchIndexType.vectorSearch() ) val resultCreateIndexes = moviesCollection.createSearchIndexes( listOf(searchIdxMdl, vectorSearchIdxMdl) )
List Search Indexes
You can use the listSearchIndexes() method to return a list of the Atlas Search indexes on a collection.
The following code example shows how to print a list of the search indexes on a collection:
val searchIndexesList = moviesCollection.listSearchIndexes().toList()
Update a Search Index
You can use the updateSearchIndex() method to update an Atlas Search index.
The following code shows how to update a search index:
moviesCollection.updateSearchIndex( "myIndex", Document("analyzer", "lucene.simple").append( "mappings", Document("dynamic", false) .append( "fields", Document( "title", Document("type", "string") ) ) ) )
Drop a Search Index
You can use the dropSearchIndex() method to remove an Atlas Search index.
The following code shows how to delete a search index from a collection:
moviesCollection.dropSearchIndex("myIndex");
Text Indexes
Text indexes support text search queries on string content. These indexes can include any field whose value is a string or an array of string elements. MongoDB supports text search for various languages. You can specify the default language as an option when creating the index.
Tip
MongoDB offers an improved full-text search solution, Atlas Search. To learn more about Atlas Search indexes and how to use them, see the Atlas Search and Vector Search Indexes section of this guide.
Single Field
The following example creates a text index on the plot
field:
try { val resultCreateIndex = moviesCollection.createIndex(Indexes.text(Movie::plot.name)) println("Index created: $resultCreateIndex") } catch (e: MongoCommandException) { if (e.errorCodeName == "IndexOptionsConflict") { println("there's an existing text index with different options") } }
Index created: plot_text
The following is an example of a query that is covered by the index
created in the preceding code snippet. Note that the sort
is
omitted because text indexes do not contain sort order.
val filter = Filters.text("Batman") val projection = Projections.fields( Projections.include(Movie::fullplot.name), Projections.excludeId() ) data class Results(val fullplot: String) val resultsFlow = moviesCollection.find<Results>(filter).projection(projection) resultsFlow.collect { println(it) }
Multiple Fields
A collection can only contain one text index. If you want to create a text index for multiple text fields, you must create a compound index. A text search runs on all the text fields within the compound index.
The following snippet creates a compound text index for the title
and genre
fields:
try { val resultCreateIndex = moviesCollection.createIndex( Indexes.compoundIndex( Indexes.text(Movie::title.name), Indexes.text(Movie::genres.name) ) ) println("Index created: $resultCreateIndex") } catch (e: MongoCommandException) { if (e.errorCodeName == "IndexOptionsConflict") { println("there's an existing text index with different options") } }
Index created: title_text_genre_text
For more information, see the following Server Manual Entries:
Geospatial Indexes
MongoDB supports queries of geospatial coordinate data using 2dsphere indexes. With a 2dsphere
index, you can query
the geospatial data for inclusion, intersection, and proximity. For more information on querying geospatial data, see
Geospatial Queries in the Server manual.
To create a 2dsphere
index, you must specify a field that contains
only GeoJSON objects. To learn more about this type, see
GeoJSON objects in the Server manual.
The location.geo
field in the following sample document from the theaters
collection in the sample_mflix
database is a GeoJSON Point object that describes the coordinates of the theater:
{ "_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 ] } } }
The following example creates a 2dsphere
index on the location.geo
field:
val resultCreateIndex = theatersCollection.createIndex( Indexes.geo2dsphere("${Theater::location.name}.${Theater.Location::geo.name}") ) println("Index created: $resultCreateIndex")
Index created: location.geo_2dsphere
Important
Attempting to create a geospatial index on a field that is already covered by a geospatial index results in an error.
The following is an example of a geospatial query that is covered by the index created in the preceding code snippet:
// MongoDB Headquarters in New York, NY. val refPoint = Point(Position(-73.98456, 40.7612)) val filter = Filters.near( "${Theater::location.name}.${Theater.Location::geo.name}", refPoint, 1000.0, 0.0 ) val resultsFlow = theatersCollection.find(filter) resultsFlow.collect { println(it) }
MongoDB also supports 2d
indexes for calculating distances on a
Euclidean plane and for working with the "legacy coordinate pairs"
syntax used in MongoDB 2.2 and earlier. To learn more, see
Geospatial Queries in the Server manual.
Unique Indexes
Unique indexes ensure that the indexed fields do not store duplicate values. By default, MongoDB creates a unique index
on the _id
field during the creation of a collection. To create a unique index, specify the field or combination of
fields that you want to prevent duplication on and set the unique
option to true
.
The following example creates a unique, descending index on the theaterId
field:
try { val indexOptions = IndexOptions().unique(true) val resultCreateIndex = theatersCollection.createIndex( Indexes.descending(Theater::theaterId.name), indexOptions ) println("Index created: $resultCreateIndex") } catch (e: DuplicateKeyException) { println("duplicate field values encountered, couldn't create index: \t${e.message}") }
Index created: theaterId_-1
Important
If you perform a write operation that stores a duplicate value that
violates the unique index, the driver raises a DuplicateKeyException
,
and MongoDB throws an error resembling the following:
E11000 duplicate key error index
Refer to the Unique Indexes page in the MongoDB server manual for more information.
Clustered Indexes
Clustered indexes instruct a collection to store documents ordered
by a key value. To create a clustered index, specify the clustered index
option with the _id
field as the key and the unique field as
true
when you create your collection.
The following example creates a clustered index on the _id
field in
the vendors
collection:
val clusteredIndexOptions = ClusteredIndexOptions(Document("_id", 1), true) val createCollectionOptions = CreateCollectionOptions().clusteredIndexOptions(clusteredIndexOptions) database.createCollection("vendors", createCollectionOptions)
See the MongoDB server manual sections for more information:
Remove an Index
You can remove any unused index except the default unique index on the
_id
field.
The following sections show the ways to remove indexes:
Using an index specification document
Using an indexed name field
Using a wildcard character to remove all indexes
Remove an Index Using an Index Specification Document
Pass an index specification document to the dropIndex()
method to
remove an index from a collection. An index specification document is
a Bson
instance that specifies the type of index on a
specified field.
The following snippet removes an ascending index on the title
field
in a collection:
moviesCollection.dropIndex(Indexes.ascending(Movie::title.name));
Important
If you want to drop a text index, you must use the name of the index instead. See the Remove an Index Using a Name Field section for details.
Remove an Index Using a Name Field
Pass the name
field of the index to the dropIndex()
method to
remove an index from a collection.
If you must find the name of your index, use the listIndexes()
method to see the value of the name
fields in your indexes.
The following snippet retrieves and prints all the indexes in a collection:
val indexes = moviesCollection.listIndexes() indexes.collect { println(it.toJson()) }
If you call listIndex()
on a collection that contains a text index,
the output might resemble the following:
{ "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 }
This output tells us the names of the existing indexes are "_id" and "title_text".
The following snippet removes the "title_text" index from the collection:
moviesCollection.dropIndex("title_text")
Note
You cannot remove a single field from a compound text index. You must drop the entire index and create a new one to update the indexed fields.
Remove an Index Using a Wildcard Character
Starting with MongoDB 4.2, you can drop all indexes by calling the
dropIndexes()
method on your collection:
moviesCollection.dropIndexes()
For prior versions of MongoDB, pass "*" as a parameter to your call to
dropIndex()
on your collection:
moviesCollection.dropIndex("*")
For more information on the methods in this section, see the following API Documentation: