База данных index — это способ ускорить определенные запросы за счет более медленного обновления.

База данных index — это способ ускорить определенные запросы за счет более медленного обновления. Например, предположим, что у вас есть модель Product с 10 000 документов. Без индекса MongoDB необходимо сканировать все 10 000 документов каждый раз, когда вам нужен один продукт.

Термин сканирование коллекции означает запрос, в котором MongoDB необходимо выполнить итерацию по всей коллекции. Является ли сканирование коллекции проблемой, зависит от размера коллекции и варианта использования. Например, если вы создаете 100 000 документов о продуктах, сканирование коллекции может занять более 40–45 миллисекунд в зависимости от характеристик вашего оборудования и нагрузки на базу данных.

В зависимости от вашего приложения это может не быть проблемой. Но если запросы (поиск) являются основной функцией вашего приложения, вам нужно избегать сканирования коллекций.

Полезное правило. Если ваши конечные пользователи рассчитывают получить доступ к результатам запроса почти мгновенно (40–45 мс), в вашем запросе не следует выполнять сканирование коллекции на моделях с более чем 100 000 документов. . Если у вас менее 10 000 документов, сканирование коллекции не должно быть проблемой.

Так как же избежать сканирования коллекции? Ответ заключается в том, чтобы создать индекс для most searched property like product name product category .

Определение индексов

Большинство приложений определяют индексы в своих схемах Mongoose. Вы можете определить индексы в определении схемы, используя index: true:

// `schema` has 2 indexes: one on `name`, and one on `email`.
const schema = new Schema({
  productName: { type: String, index: true },
  productCategory: { type: String, index: true }
});

Вы также можете определить индекс с помощью функции Schema#index():

const schema = new Schema({ productName: String, productCategory: String });
// Add 2 separate indexes to `schema`
schema.index({ productName: 1 });
schema.index({ productCategory: 1 });

Строительные индексы

Вы можете определить индексы в своей схеме, но индексы живут на сервере MongoDB. Чтобы действительно использовать индекс, вам нужно создать индекс на сервере MongoDB.

Mongoose автоматически создает все индексы, определенные в вашей схеме, при создании модели:

const schema = new Schema({ productName: String, productCategory: String });
schema.index({ productName: 1 });
schema.index({ productCategory: 1 });
// If the indexes already exist, Mongoose doesn't do anything.
const Model = mongoose.model('Test', schema);

Когда вы создаете новую модель, Mongoose автоматически вызывает функцию createIndexes() этой модели. Вы можете отключить автоматическое построение индекса с помощью параметра схемы autoIndex:

const opts = { autoIndex: false }; // Disable auto index build
const schema = Schema({ productName: { type: String, index: true } }, opts);
const Model = mongoose.model('Test', schema);
await Model.init();
// Does **not** have the index on `name`
const indexes = await Model.listIndexes();

Вы также можете сами вызвать createIndexes(), чтобы построить все индексы схемы.

const opts = { autoIndex: false };
const schema = Schema({ productName: { type: String, index: true } }, opts);
const Model = mongoose.model('Test', schema);
await Model.init();
let indexes = await Model.listIndexes();
indexes.length; // 1
await Model.createIndexes();
indexes = await Model.listIndexes();
indexes.length; // 2

После создания индекса он остается на сервере MongoDB навсегда, если только кто-то явно не удалит индекс. При удалении базы данных или коллекции также удаляются все индексы. Это означает, что как только ваши индексы будут построены, вам, как правило, больше не придется о них беспокоиться.

Хотя Mongoose автоматически создает все индексы, определенные в вашей схеме, Mongoose не удаляет любые существующие индексы, которых нет в вашей схеме. Вы можете использовать функцию syncIndexes(), чтобы убедиться, что индексы в MongoDB совпадают с индексами в вашей схеме:

let schema = Schema({ productName: { type: String, index: true } });
let Model = mongoose.model('Test', schema);
await Model.init();
// Now suppose you change the property `productName` to `productTitle`.
// By default, Mongoose won't drop the `productName` index
schema = Schema({ productTitle: { type: String, index: true } });
mongoose.deleteModel('Test');
Model = mongoose.model('Test', schema);
await Model.init();
// There are now 3 indexes in the database: one on `_id`, one
// on `productTitle`, and one on `productName`.
let indexes = await Model.listIndexes();
indexes.length; // 3

Составные индексы

Составной индекс – это индекс нескольких свойств. Существует два способа определения индексов для нескольких свойств. Во-первых, используя Schema#index():

let schema = Schema({ firstName: String, lastName: String });
// Define a compound index on { firstName, lastName }
schema.index({ firstName: 1, lastName: 1 });
const Model = mongoose.model('Test', schema);
await Model.init();
const indexes = await Model.listIndexes();
indexes.length; // 2
indexes[1].key; // { firstName: 1, lastName: 1 }

Альтернативой является определение 2 путей SchemaType в вашей схеме с одинаковым индексом name. Mongoose группирует индексы с одинаковыми именами в один составной индекс.

let schema = Schema({
  firstName: {
    type: String,
    index: { name: 'firstNameLastName' }
  },
  lastName: {
    type: String,
    index: { name: 'firstNameLastName' }
  }
});
const indexes = schema.indexes();
indexes.length; // 1
indexes[0][0]; // { firstName: 1, lastName: 1 }

Чем полезны составные индексы? Индексы бесполезны, если они не достаточно специфичны. но интуиция подсказывает, что вы хотите свести к минимуму количество документов, которые MongoDB необходимо просмотреть, чтобы ответить на ваш запрос.

В приведенном ниже примере показано, что индексы не просто волшебным образом ускоряют ваши запросы. В вырожденных случаях, как показано ниже, где каждый документ имеет одинаковый firstName, наличие плохого индекса может быть хуже, чем отсутствие индекса вообще.

let schema = Schema({ firstName: String, lastName: String });
// Querying by { firstName, lastName } will be slow, because
// there's only an index on `firstName` and every document
// has the same `firstName`.
schema.index({ firstName: 1 });
const User = mongoose.model('User', schema);
const docs = [];
for (let i = 0; i < 100000; ++i) {
  docs.push({ firstName: 'Agent', lastName: 'Smith' });
}
docs.push({ firstName: 'Agent', lastName: 'Brown' });
await User.insertMany(docs);
const start = Date.now();
let res = await User.find({ firstName: 'Agent', lastName: 'Brown' });
const elapsed = Date.now() - start;
// Approximately 315 on my laptop, 3x slower than if no index!
elapsed;

С другой стороны, если вы создадите составной индекс для { firstName, lastName }, MongoDB сможет почти мгновенно найти документ «Агент Браун».

let schema = Schema({ firstName: String, lastName: String });
schema.index({ firstName: 1, lastName: 1 });
const User = mongoose.model('User', schema);
const start = Date.now();
let res = await User.find({ firstName: 'Agent', lastName: 'Brown' });
const elapsed = Date.now() - start;
elapsed; // Approximately 10 on my laptop

Уникальные индексы

Индекс unique означает, что MongoDB выдаст ошибку, если есть несколько документов с одинаковым значением индексированного свойства. Одним из преимуществ уникальных индексов является невозможность плохой специфичности индекса.

Есть несколько способов объявить индекс unique. Во-первых, вы можете установить unique: true для свойства в определении схемы:

let schema = Schema({
  email: {
    type: String,
    unique: true
  }
});
const User = mongoose.model('User', schema);
await User.init();
// Unique index means MongoDB throws an 'E11000 duplicate key
// error' if there are two documents with the same `email`.
const err = await User.create([
  { email: '[email protected]' },
  { email: '[email protected]' }
]).catch(err => err);
err.message; // 'E11000 duplicate key error...'

Вы также можете установить для параметра unique значение true при вызове Schema.index(). Это позволяет определить составной уникальный индекс:

let schema = Schema({ firstName: String, lastName: String });
// A compound unique index on { firstName, lastName }
schema.index({ firstName: 1, lastName: 1 }, { unique: true });
const indexes = schema.indexes();
indexes.length; // 1
indexes[0][0]; // { firstName: 1, lastName: 1 }
indexes[0][1].unique; // true

Важное замечание о составных уникальных индексах: в приведенном выше примере могут быть повторяющиеся firstName и lastName, но комбинация firstName и lastName должна быть уникальной.

Например, может быть только один документ, соответствующий { firstName: 'Agent', lastName: 'Smith' }. Но документов с firstName 'Агент' или lastName 'Кузнец' может быть много.

Индекс _id

MongoDB автоматически создает индекс _id всякий раз, когда создает новую коллекцию. Этот индекс unique под капотом. К сожалению, функция listIndexes() не сообщает индекс _id как уникальный: это известная особенность MongoDB.

const User = mongoose.model('User', Schema({ name: String }));
await User.createCollection();
// MongoDB always creates an index on `_id`. Even though
// `listIndexes()` doesn't say that the `_id` index is unique,
// the `_id` index **is** a unique index.
const indexes = await User.listIndexes();
indexes.length; // 1
indexes[0].key; // { _id: 1 }
indexes[0].unique; // undefined
// Try to create 2 users with the exact same `_id`
const _id = new mongoose.Types.ObjectId();
const users = [{ _id }, { _id }];
const err = await User.create(users).catch(err => err);
err.message; // 'E11000 duplicate key error...'

Индекс _id гарантирует, что Model.findOne({ _id }) и Model.findById(id) почти всегда будут быстрыми запросами.