fix PR comments

pull/27/head
Timothée Rebours 2 years ago
parent fbe2c828e7
commit 0305223082
  1. 30
      lib/datastore.js
  2. 19
      lib/indexes.js
  3. 11
      lib/model.js
  4. 6
      test/db.async.test.js
  5. 10
      test/indexes.test.js

@ -431,7 +431,7 @@ class Datastore extends EventEmitter {
/** /**
* Callback version of {@link Datastore#ensureIndex}. * Callback version of {@link Datastore#ensureIndex}.
* @param {object} options * @param {object} options
* @param {string} options.fieldName * @param {string|string[]} options.fieldName
* @param {boolean} [options.unique = false] * @param {boolean} [options.unique = false]
* @param {boolean} [options.sparse = false] * @param {boolean} [options.sparse = false]
* @param {number} [options.expireAfterSeconds] * @param {number} [options.expireAfterSeconds]
@ -448,7 +448,7 @@ class Datastore extends EventEmitter {
* This function acts synchronously on the indexes, however the persistence of the indexes is deferred with the * This function acts synchronously on the indexes, however the persistence of the indexes is deferred with the
* executor. * executor.
* @param {object} options * @param {object} options
* @param {string} options.fieldName Name of the field to index. Use the dot notation to index a field in a nested * @param {string|string[]} options.fieldName Name of the field to index. Use the dot notation to index a field in a nested
* document. For a compound index, use an array of field names. Using a comma in a field name is not permitted. * document. For a compound index, use an array of field names. Using a comma in a field name is not permitted.
* @param {boolean} [options.unique = false] Enforce field uniqueness. Note that a unique index will raise an error * @param {boolean} [options.unique = false] Enforce field uniqueness. Note that a unique index will raise an error
* if you try to index two documents for which the field is not defined. * if you try to index two documents for which the field is not defined.
@ -465,26 +465,32 @@ class Datastore extends EventEmitter {
err.missingFieldName = true err.missingFieldName = true
throw err throw err
} }
if (Array.isArray(options.fieldName)) {
options.fieldName.sort() const _fields = [].concat(options.fieldName).sort()
}
if ([].concat(options.fieldName).some(field => field.includes(','))) { if (_fields.some(field => field.includes(','))) {
throw new Error('Cannot use comma in index fieldName') throw new Error('Cannot use comma in index fieldName')
} }
if (this.indexes[options.fieldName]) return
this.indexes[options.fieldName] = new Index(options) const _options = {
if (options.expireAfterSeconds !== undefined) this.ttlIndexes[options.fieldName] = options.expireAfterSeconds // With this implementation index creation is not necessary to ensure TTL but we stick with MongoDB's API here ...options,
fieldName: _fields.join(',')
}
if (this.indexes[_options.fieldName]) return
this.indexes[_options.fieldName] = new Index(_options)
if (options.expireAfterSeconds !== undefined) this.ttlIndexes[_options.fieldName] = _options.expireAfterSeconds // With this implementation index creation is not necessary to ensure TTL but we stick with MongoDB's API here
try { try {
this.indexes[options.fieldName].insert(this.getAllData()) this.indexes[_options.fieldName].insert(this.getAllData())
} catch (e) { } catch (e) {
delete this.indexes[options.fieldName] delete this.indexes[_options.fieldName]
throw e throw e
} }
// We may want to force all options to be persisted including defaults, not just the ones passed the index creation function // We may want to force all options to be persisted including defaults, not just the ones passed the index creation function
await this.executor.pushAsync(() => this.persistence.persistNewStateAsync([{ $$indexCreated: options }]), true) await this.executor.pushAsync(() => this.persistence.persistNewStateAsync([{ $$indexCreated: _options }]), true)
} }
/** /**

@ -38,16 +38,26 @@ class Index {
* All methods on an index guarantee that either the whole operation was successful and the index changed * All methods on an index guarantee that either the whole operation was successful and the index changed
* or the operation was unsuccessful and an error is thrown while the index is unchanged * or the operation was unsuccessful and an error is thrown while the index is unchanged
* @param {object} options * @param {object} options
* @param {string} options.fieldName On which field should the index apply (can use dot notation to index on sub fields) * @param {string} options.fieldName On which field should the index apply, can use dot notation to index on sub fields, can use comma-separated notation to use compound indexes
* @param {boolean} [options.unique = false] Enforces a unique constraint * @param {boolean} [options.unique = false] Enforces a unique constraint
* @param {boolean} [options.sparse = false] Allows a sparse index (we can have documents for which fieldName is `undefined`) * @param {boolean} [options.sparse = false] Allows a sparse index (we can have documents for which fieldName is `undefined`)
*/ */
constructor (options) { constructor (options) {
/** /**
* On which field the index applies to (may use dot notation to index on sub fields). * On which field the index applies to, can use dot notation to index on sub fields, can use comma-separated notation to use compound indexes.
* @type {string} * @type {string}
*/ */
this.fieldName = options.fieldName this.fieldName = options.fieldName
if (typeof this.fieldName !== 'string') throw new Error('fieldName must be a string')
/**
* Internal property which is an Array representing the fieldName split with `,`, useful only for compound indexes.
* @type {string[]}
* @private
*/
this._fields = this.fieldName.split(',')
/** /**
* Defines if the index enforces a unique constraint for this index. * Defines if the index enforces a unique constraint for this index.
* @type {boolean} * @type {boolean}
@ -99,7 +109,7 @@ class Index {
return return
} }
const key = model.getDotValues(doc, this.fieldName) const key = model.getDotValues(doc, this._fields)
// We don't index documents that don't contain the field if the index is sparse // We don't index documents that don't contain the field if the index is sparse
if ((key === undefined || (typeof key === 'object' && key !== null && Object.values(key).every(el => el === undefined))) && this.sparse) return if ((key === undefined || (typeof key === 'object' && key !== null && Object.values(key).every(el => el === undefined))) && this.sparse) return
@ -171,8 +181,7 @@ class Index {
return return
} }
const key = model.getDotValues(doc, this.fieldName) const key = model.getDotValues(doc, this._fields)
if (key === undefined && this.sparse) return if (key === undefined && this.sparse) return
if (!Array.isArray(key)) { if (!Array.isArray(key)) {

@ -507,17 +507,14 @@ const getDotValue = (obj, field) => {
* Get dot values for either a bunch of fields or just one. * Get dot values for either a bunch of fields or just one.
*/ */
const getDotValues = (obj, fields) => { const getDotValues = (obj, fields) => {
if (Array.isArray(fields)) { if (!Array.isArray(fields)) throw new Error('fields must be an Array')
if (fields.length > 1) {
const key = {} const key = {}
const len = fields.length for (const field of fields) {
for (let i = 0; i < len; i++) {
const field = fields[i]
key[field] = getDotValue(obj, field) key[field] = getDotValue(obj, field)
} }
return key return key
} else { } else return getDotValue(obj, fields[0])
return getDotValue(obj, fields)
}
} }
/** /**

@ -1302,7 +1302,7 @@ describe('Database async', function () {
assert.equal(d.indexes.z.tree.search('3')[0], d.getAllData()[2]) assert.equal(d.indexes.z.tree.search('3')[0], d.getAllData()[2])
}) })
it('ensureIndex can be called twice on the same field, the second call will ahve no effect', async () => { it('ensureIndex can be called twice on the same field, the second call will have no effect', async () => {
assert.equal(Object.keys(d.indexes).length, 1) assert.equal(Object.keys(d.indexes).length, 1)
assert.equal(Object.keys(d.indexes)[0], '_id') assert.equal(Object.keys(d.indexes)[0], '_id')
@ -1327,7 +1327,7 @@ describe('Database async', function () {
assert.equal(d.indexes.planet.getAll().length, 2) assert.equal(d.indexes.planet.getAll().length, 2)
}) })
it('ensureIndex can be called twice on the same compound field, the second call will ahve no effect', async () => { it('ensureIndex can be called twice on the same compound field, the second call will have no effect', async () => {
assert.equal(Object.keys(d.indexes).length, 1) assert.equal(Object.keys(d.indexes).length, 1)
assert.equal(Object.keys(d.indexes)[0], '_id') assert.equal(Object.keys(d.indexes)[0], '_id')
@ -1352,7 +1352,7 @@ describe('Database async', function () {
assert.equal(d.indexes['planet,star'].getAll().length, 2) assert.equal(d.indexes['planet,star'].getAll().length, 2)
}) })
it('ensureIndex can be called twice on the same compound field with a different order, the second call will ahve no effect', async () => { it('ensureIndex can be called twice on the same compound field with a different order, the second call will have no effect', async () => {
assert.equal(Object.keys(d.indexes).length, 1) assert.equal(Object.keys(d.indexes).length, 1)
assert.equal(Object.keys(d.indexes)[0], '_id') assert.equal(Object.keys(d.indexes)[0], '_id')

@ -30,7 +30,7 @@ describe('Indexes', function () {
}) })
it('Can insert pointers to documents in the index correctly when they have compound fields', function () { it('Can insert pointers to documents in the index correctly when they have compound fields', function () {
const idx = new Index({ fieldName: ['tf', 'tg'] }) const idx = new Index({ fieldName: 'tf,tg' })
const doc1 = { a: 5, tf: 'hello', tg: 'world' } const doc1 = { a: 5, tf: 'hello', tg: 'world' }
const doc2 = { a: 8, tf: 'hello', tg: 'bloup' } const doc2 = { a: 8, tf: 'hello', tg: 'bloup' }
const doc3 = { a: 2, tf: 'bloup', tg: 'bloup' } const doc3 = { a: 2, tf: 'bloup', tg: 'bloup' }
@ -81,7 +81,7 @@ describe('Indexes', function () {
}) })
it('Inserting twice for the same compound fieldName in a unique index will result in an error thrown', function () { it('Inserting twice for the same compound fieldName in a unique index will result in an error thrown', function () {
const idx = new Index({ fieldName: ['tf', 'tg'], unique: true }) const idx = new Index({ fieldName: 'tf,tg', unique: true })
const doc1 = { a: 5, tf: 'hello', tg: 'world' } const doc1 = { a: 5, tf: 'hello', tg: 'world' }
idx.insert(doc1) idx.insert(doc1)
@ -90,7 +90,7 @@ describe('Indexes', function () {
}) })
it('Inserting twice for a compound fieldName the docs dont have with a unique and sparse index will not throw, since the docs will be non indexed', function () { it('Inserting twice for a compound fieldName the docs dont have with a unique and sparse index will not throw, since the docs will be non indexed', function () {
const idx = new Index({ fieldName: ['nope', 'nopeNope'], unique: true, sparse: true }) const idx = new Index({ fieldName: 'nope,nopeNope', unique: true, sparse: true })
const doc1 = { a: 5, tf: 'hello' } const doc1 = { a: 5, tf: 'hello' }
const doc2 = { a: 5, tf: 'world' } const doc2 = { a: 5, tf: 'world' }
@ -245,8 +245,8 @@ describe('Indexes', function () {
}) // ==== End of 'Array fields' ==== // }) // ==== End of 'Array fields' ==== //
describe('Compound Indexes', function () { describe('Compound Indexes', function () {
it('Supports arrays of fieldNames', function () { it('Supports field names separated by commas', function () {
const idx = new Index({ fieldName: ['tf', 'tf2'] }) const idx = new Index({ fieldName: 'tf,tf2' })
const doc1 = { a: 5, tf: 'hello', tf2: 7 } const doc1 = { a: 5, tf: 'hello', tf2: 7 }
const doc2 = { a: 8, tf: 'hello', tf2: 6 } const doc2 = { a: 8, tf: 'hello', tf2: 6 }
const doc3 = { a: 2, tf: 'bloup', tf2: 3 } const doc3 = { a: 2, tf: 'bloup', tf2: 3 }

Loading…
Cancel
Save