The document discusses guidelines for ordering fields in compound indexes to optimize query performance. It recommends the E-S-R approach: placing equality fields first, followed by sort fields, and range fields last. This allows indexes to leverage equality matches, provide non-blocking sorts, and minimize scanning. Examples show how indexes ordered by these guidelines can support queries more efficiently by narrowing the search bounds.
9. E-S-R
Have you thought about the order of the fields in a compound index?
Does it really matter?
10. E-S-R
Have you thought about the order of the fields in a compound index?
The ordering of index keys in a compound index is critically important.
E-S-R provides guidance that is useful in most cases:
• Equality first
• Sort next
• Range last
11. E-S-R
The ordering of index keys in a compound index is critically important.
E-S-R provides guidance that is useful in most cases:
• Equality first
• Sort next
• Range last
What is the difference between
Equality and Range?
13. Definitions
Equality Fields Sort
The (entire) requested sort.
.sort({a:1, b:-1})
Bounds:
"[MinKey, MaxKey]",
"[MinKey, MaxKey]"
An exact match on a single
value. For example:
• {x:123}
• {x:{$eq:123}}
• {"x.y":123}
Bounds:
"[123.0, 123.0]"
14. Definitions
Equality Fields Sort Range Predicates
Any predicates that are not
exact matches. Some
operators include:
• {z:{$gt:9000}}
• {z:{$lte:1000}}
Bounds:
"[9000.0, inf.0]",
The (entire) requested sort.
.sort({a:1, b:-1})
Bounds:
"[MinKey, MaxKey]",
"[MaxKey, MinKey]"
An exact match on a single
value. For example:
• {x:123}
• {x:{$eq:123}}
• {"x.y":123}
Bounds:
"[123.0, 123.0]"
16. Equality
Equality keys are placed first in any order
If present in the query shape,
equality fields should always form the prefix for the index.
17. Equality
Equality keys are placed first in any order
If present in the query shape,
equality fields should always form the prefix for the index.
Why?
18. Equality
Equality keys are placed first in any order
If present in the query shape,
equality fields should always form the prefix for the index.
db.games.find( {gamertag: "Ace", score: {$gt: 9000}} )
19. Equality
db.games.find( {gamertag: "Ace", score: {$gt: 9000}} )
If present in the query shape,
equality fields should always form the prefix for the index.
Equality keys are placed first in any order
42. Sort fields are placed next
Placing sort predicates after sequential equality keys allow for the index to:
Provide a non-blocking sort.
Minimize the amount of scanning required.
db.games.find( {gamertag: "Ace"} ).sort( {score: 1} )
Sort
43. db.games.find( {gamertag: "Ace"} ).sort( {score: 1} )
Sort
100 15,000 50,000 99,999
Ace Bob Bob Acegamertag
score
db.games.createIndex({score:1, gamertag:1})
70. Range fields are usually last
This allows them to :
Still participate in filtering the data
But does not force a blocking sort.
db.games.find( {gamertag: "Ace", score: {$gt: 9000}} )
Range
113. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
114. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
115. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
planecar racecar
116. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
planecar racecar
117. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
planecar racecar
118. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
p???ecar racecar
119. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
p???ecar racecar
120. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
121. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
raincarry
122. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
raincarry
123. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
raincarry Carpool
124. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
raincarry Carpool
125. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
raincarry Carpool
126. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
raincarry Carpool
Since the regex is left
anchored, can’t we skip
the middle tree?
127. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
raincarry Carpool
Since the regex is left
anchored, can’t we skip
the middle tree?
128. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
cArrycarry Carpool
Since the regex is left
anchored, can’t we skip
the middle tree?
129. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
cArrycarry Carpool
Since the regex is left
anchored, can’t we skip
the middle tree?
130. Operator Type Check - Regex
Regex Operators: E, S, or R?
• {str:/car/}
• {str:/^car/i}
Range!
cArrycarry Carpool
131. Operator Type Check - $in
$in filters: E, S, or R?
• {field:{$in:[1,3]}}
… it depends with respect to key ordering
Alone: a series of Equality matches
Combined: possibly a Range
133. Operator Type Check - $in
.find({field:{$in:[1,3]}})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
134. Operator Type Check - $in
.find({field:{$in:[1,3]}})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
135. Operator Type Check - $in
.find({field:{$in:[1,3]}})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
136. Operator Type Check - $in
.find({field:{$in:[1,3]}})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
137. Operator Type Check - $in
.find({field:{$in:[1,3]}})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
138. Operator Type Check - $in
.find({field:{$in:[1,3]}})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
139. Operator Type Check - $in
.find({field:{$in:[1,3]}})
.sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
140. Operator Type Check - $in
.find({field:{$in:[1,3]}})
.sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
141. Operator Type Check - $in
.find({field:{$in:[1,3]}})
.sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
142. Operator Type Check - $in
.find({field:{$in:[1,3]}})
.sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
143. Operator Type Check - $in
.find({field:{$in:[1,3]}})
.sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
144. Operator Type Check - $in
.find({field:{$in:[1,3]}})
.sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
BS
db.coll.createIndex({field:1, sortField:1})
145. Operator Type Check - $in
.find({field:{$in:[1,3]}})
.sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
"[3.0, 3.0]"
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
BS
db.coll.createIndex({field:1, sortField:1})
146. Operator Type Check - $in
.find({field:{$in:[1,3]}})
.sort({sortField:1})
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
147. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1}) ?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
148. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
149. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
150. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
151. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
152. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
153. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
"indexBounds" : {
"field" : [
"[3.0, 3.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
154. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
"indexBounds" : {
"field" : [
"[3.0, 3.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
155. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
"indexBounds" : {
"field" : [
"[3.0, 3.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
156. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
"indexBounds" : {
"field" : [
"[3.0, 3.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
?
1 2 3
C G B F A D
db.coll.createIndex({field:1, sortField:1})
157. Operator Type Check - $in
.find({field:{$in:[1,3]}}).sort({sortField:1})
"indexBounds" : {
"field" : [
"[1.0, 1.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
"indexBounds" : {
"field" : [
"[3.0, 3.0]",
],
"sortField" : [
"[MinKey, MaxKey]"
]
}
?
1 2 3
C G B F A D
?
1 2 3
C G B F A D
{2} {4} {1} {3}
Sort Merge
db.coll.createIndex({field:1, sortField:1})
159. Operator Type Check – Cheat Sheet
$eq - EQUALITY
$gte - RANGE
$ne - RANGE
$nin - RANGE
$regex – RANGE
$in
• If alone – a series of EQUALITY matches
• If combined – possibly a RANGE
161. Consecutive Index Keys
Given Indexes:
{gamertag:1, date:1, game:1}
{gamertag:1, game:1, date:1}
Which one is “better”?
It depends on the query shape(s)!
Consider the query:
.find({
gamertag:"Ace",
game: "Halo”
})
180. Exceptions
Is the E-S-R “rule” always optimal?
Nope.
Consider the following query:
db.games
.find({gamertag:"Ace", date:{$gte:2019}})
.sort({score:1})
207. E-S-R Guidance
A good starting place applicable to
most use cases
Place keys in the following order:
Equality first
Sort next
Range last
Remember:
• Some operators may be range
instead of equality
• Having consecutive keys used in
the index is important
• Specifics about your data set may
need a different approach