@@ -6,7 +6,7 @@ import type { OneOrMore } from '../mongo_types';
6
6
import { ReadPreference } from '../read_preference' ;
7
7
import type { Server } from '../sdam/server' ;
8
8
import type { ClientSession } from '../sessions' ;
9
- import { Callback , maxWireVersion , MongoDBNamespace , parseIndexOptions } from '../utils' ;
9
+ import { Callback , isObject , maxWireVersion , MongoDBNamespace } from '../utils' ;
10
10
import {
11
11
CollationOptions ,
12
12
CommandOperation ,
@@ -51,14 +51,17 @@ const VALID_INDEX_OPTIONS = new Set([
51
51
52
52
/** @public */
53
53
export type IndexDirection = - 1 | 1 | '2d' | '2dsphere' | 'text' | 'geoHaystack' | number ;
54
-
54
+ function isIndexDirection ( x : unknown ) : x is IndexDirection {
55
+ return (
56
+ typeof x === 'number' || x === '2d' || x === '2dsphere' || x === 'text' || x === 'geoHaystack'
57
+ ) ;
58
+ }
55
59
/** @public */
56
60
export type IndexSpecification = OneOrMore <
57
61
| string
58
62
| [ string , IndexDirection ]
59
63
| { [ key : string ] : IndexDirection }
60
- | [ string , IndexDirection ] [ ]
61
- | { [ key : string ] : IndexDirection } [ ]
64
+ | Map < string , IndexDirection >
62
65
> ;
63
66
64
67
/** @public */
@@ -86,7 +89,7 @@ export interface IndexDescription
86
89
> {
87
90
collation ?: CollationOptions ;
88
91
name ?: string ;
89
- key : Document ;
92
+ key : { [ key : string ] : IndexDirection } | Map < string , IndexDirection > ;
90
93
}
91
94
92
95
/** @public */
@@ -130,23 +133,37 @@ export interface CreateIndexesOptions extends CommandOperationOptions {
130
133
hidden ?: boolean ;
131
134
}
132
135
133
- function makeIndexSpec ( indexSpec : IndexSpecification , options : any ) : IndexDescription {
134
- const indexParameters = parseIndexOptions ( indexSpec ) ;
135
-
136
- // Generate the index name
137
- const name = typeof options . name === 'string' ? options . name : indexParameters . name ;
138
-
139
- // Set up the index
140
- const finalIndexSpec : Document = { name, key : indexParameters . fieldHash } ;
136
+ function isSingleIndexTuple ( t : unknown ) : t is [ string , IndexDirection ] {
137
+ return Array . isArray ( t ) && t . length === 2 && isIndexDirection ( t [ 1 ] ) ;
138
+ }
141
139
142
- // merge valid index options into the index spec
143
- for ( const optionName in options ) {
144
- if ( VALID_INDEX_OPTIONS . has ( optionName ) ) {
145
- finalIndexSpec [ optionName ] = options [ optionName ] ;
140
+ function makeIndexSpec (
141
+ indexSpec : IndexSpecification ,
142
+ options ?: CreateIndexesOptions
143
+ ) : IndexDescription {
144
+ const key : Map < string , IndexDirection > = new Map ( ) ;
145
+
146
+ const indexSpecs =
147
+ ! Array . isArray ( indexSpec ) || isSingleIndexTuple ( indexSpec ) ? [ indexSpec ] : indexSpec ;
148
+
149
+ // Iterate through array and handle different types
150
+ for ( const spec of indexSpecs ) {
151
+ if ( typeof spec === 'string' ) {
152
+ key . set ( spec , 1 ) ;
153
+ } else if ( Array . isArray ( spec ) ) {
154
+ key . set ( spec [ 0 ] , spec [ 1 ] ?? 1 ) ;
155
+ } else if ( spec instanceof Map ) {
156
+ for ( const [ property , value ] of spec ) {
157
+ key . set ( property , value ) ;
158
+ }
159
+ } else if ( isObject ( spec ) ) {
160
+ for ( const [ property , value ] of Object . entries ( spec ) ) {
161
+ key . set ( property , value ) ;
162
+ }
146
163
}
147
164
}
148
165
149
- return finalIndexSpec as IndexDescription ;
166
+ return { ... options , key } ;
150
167
}
151
168
152
169
/** @internal */
@@ -183,7 +200,7 @@ export class CreateIndexesOperation<
183
200
> extends CommandOperation < T > {
184
201
override options : CreateIndexesOptions ;
185
202
collectionName : string ;
186
- indexes : IndexDescription [ ] ;
203
+ indexes : ReadonlyArray < Omit < IndexDescription , 'key' > & { key : Map < string , IndexDirection > } > ;
187
204
188
205
constructor (
189
206
parent : OperationParent ,
@@ -195,8 +212,22 @@ export class CreateIndexesOperation<
195
212
196
213
this . options = options ?? { } ;
197
214
this . collectionName = collectionName ;
198
-
199
- this . indexes = indexes ;
215
+ this . indexes = indexes . map ( userIndex => {
216
+ // Ensure the key is a Map to preserve index key ordering
217
+ const key =
218
+ userIndex . key instanceof Map ? userIndex . key : new Map ( Object . entries ( userIndex . key ) ) ;
219
+ const name = userIndex . name != null ? userIndex . name : Array . from ( key ) . flat ( ) . join ( '_' ) ;
220
+ const validIndexOptions = Object . fromEntries (
221
+ Object . entries ( { ...userIndex } ) . filter ( ( [ optionName ] ) =>
222
+ VALID_INDEX_OPTIONS . has ( optionName )
223
+ )
224
+ ) ;
225
+ return {
226
+ ...validIndexOptions ,
227
+ name,
228
+ key
229
+ } ;
230
+ } ) ;
200
231
}
201
232
202
233
override execute (
@@ -209,31 +240,6 @@ export class CreateIndexesOperation<
209
240
210
241
const serverWireVersion = maxWireVersion ( server ) ;
211
242
212
- // Ensure we generate the correct name if the parameter is not set
213
- for ( let i = 0 ; i < indexes . length ; i ++ ) {
214
- // Did the user pass in a collation, check if our write server supports it
215
- if ( indexes [ i ] . collation && serverWireVersion < 5 ) {
216
- callback (
217
- new MongoCompatibilityError (
218
- `Server ${ server . name } , which reports wire version ${ serverWireVersion } , ` +
219
- 'does not support collation'
220
- )
221
- ) ;
222
- return ;
223
- }
224
-
225
- if ( indexes [ i ] . name == null ) {
226
- const keys = [ ] ;
227
-
228
- for ( const name in indexes [ i ] . key ) {
229
- keys . push ( `${ name } _${ indexes [ i ] . key [ name ] } ` ) ;
230
- }
231
-
232
- // Set the name
233
- indexes [ i ] . name = keys . join ( '_' ) ;
234
- }
235
- }
236
-
237
243
const cmd : Document = { createIndexes : this . collectionName , indexes } ;
238
244
239
245
if ( options . commitQuorum != null ) {
@@ -271,12 +277,6 @@ export class CreateIndexOperation extends CreateIndexesOperation<string> {
271
277
indexSpec : IndexSpecification ,
272
278
options ?: CreateIndexesOptions
273
279
) {
274
- // createIndex can be called with a variety of styles:
275
- // coll.createIndex('a');
276
- // coll.createIndex({ a: 1 });
277
- // coll.createIndex([['a', 1]]);
278
- // createIndexes is always called with an array of index spec objects
279
-
280
280
super ( parent , collectionName , [ makeIndexSpec ( indexSpec , options ) ] , options ) ;
281
281
}
282
282
override execute (
0 commit comments