Spec-Zone.ru › Mongoose

Типы схем

Типы схем обрабатывают определение значений по умолчанию для пути, валидацию, получатели, установщики, значения по умолчанию для выбора полей для запросов и другие общие характеристики свойств документов Mongoose.

  • Что такое SchemaType?
  • Ключ type
  • Параметры SchemaType
  • Примечания к использованию
  • Получатели
  • Пользовательские типы
  • Функция schema.path()
  • Дополнительное чтение

Что такое SchemaType?

Вы можете рассматривать схему Mongoose как объект конфигурации для модели Mongoose. SchemaType — это, в свою очередь, объект конфигурации для отдельного свойства. SchemaType определяет, какой тип должен иметь данный путь, имеет ли он какие-либо получатели/установщики и какие значения допустимы для этого пути.

const schema = new Schema({ name: String });
schema.path('name') instanceof mongoose.SchemaType; // true
schema.path('name') instanceof mongoose.Schema.Types.String; // true
schema.path('name').instance; // 'String'

SchemaType отличается от типа. Другими словами, mongoose.ObjectId !== mongoose.Types.ObjectId. SchemaType — это просто объект конфигурации для Mongoose. Экземпляр типа mongoose.ObjectId SchemaType фактически не создаёт ObjectId MongoDB, это просто конфигурация для пути в схеме.

Ниже приведены все допустимые SchemaTypes в Mongoose. Плагины Mongoose также могут добавлять пользовательские SchemaTypes, такие как int32. Просмотрите поиск плагинов Mongoose, чтобы найти плагины.

  • Строка
  • Число
  • Дата
  • Буфер
  • Булево
  • Смешанный
  • ObjectId
  • Массив
  • Decimal128
  • Карта
  • Схема
  • UUID
  • BigInt

Пример

const schema = new Schema({
  name: String,
  binary: Buffer,
  living: Boolean,
  updated: { type: Date, default: Date.now },
  age: { type: Number, min: 18, max: 65 },
  mixed: Schema.Types.Mixed,
  _someId: Schema.Types.ObjectId,
  decimal: Schema.Types.Decimal128,
  array: [],
  ofString: [String],
  ofNumber: [Number],
  ofDates: [Date],
  ofBuffer: [Buffer],
  ofBoolean: [Boolean],
  ofMixed: [Schema.Types.Mixed],
  ofObjectId: [Schema.Types.ObjectId],
  ofArrays: [[]],
  ofArrayOfNumbers: [[Number]],
  nested: {
    stuff: { type: String, lowercase: true, trim: true }
  },
  map: Map,
  mapOfString: {
    type: Map,
    of: String
  }
});

// example use

const Thing = mongoose.model('Thing', schema);

const m = new Thing;
m.name = 'Statue of Liberty';
m.age = 125;
m.updated = new Date;
m.binary = Buffer.alloc(0);
m.living = false;
m.mixed = { any: { thing: 'i want' } };
m.markModified('mixed');
m._someId = new mongoose.Types.ObjectId;
m.array.push(1);
m.ofString.push('strings!');
m.ofNumber.unshift(1, 2, 3, 4);
m.ofDates.addToSet(new Date);
m.ofBuffer.pop();
m.ofMixed = [1, [], 'three', { four: 5 }];
m.nested.stuff = 'good';
m.map = new Map([['key', 'value']]);
m.save(callback);

Ключ type

type — это специальное свойство в схемах Mongoose. Когда Mongoose находит вложенное свойство с именем type в вашей схеме, Mongoose предполагает, что необходимо определить SchemaType с заданным типом.

// 3 string SchemaTypes: 'name', 'nested.firstName', 'nested.lastName'
const schema = new Schema({
  name: { type: String },
  nested: {
    firstName: { type: String },
    lastName: { type: String }
  }
});

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

const holdingSchema = new Schema({
  // You might expect `asset` to be an object that has 2 properties,
  // but unfortunately `type` is special in Mongoose so mongoose
  // interprets this schema to mean that `asset` is a string
  asset: {
    type: String,
    ticker: String
  }
});

Однако, когда Mongoose видит type: String, он предполагает, что вы имеете в виду, что asset должна быть строкой, а не объектом со свойством type. Правильный способ определения объекта со свойством type показан ниже.

const holdingSchema = new Schema({
  asset: {
    // Workaround to make sure Mongoose knows `asset` is an object
    // and `asset.type` is a string, rather than thinking `asset`
    // is a string.
    type: { type: String },
    ticker: String
  }
});

Параметры SchemaType

Вы можете объявить тип схемы, используя сам тип напрямую или объект с свойством type

const schema1 = new Schema({
  test: String // `test` is a path of type String
});

const schema2 = new Schema({
  // The `test` object contains the "SchemaType options"
  test: { type: String } // `test` is a path of type string
});

Помимо свойства type, вы можете указать дополнительные свойства для пути. Например, если вы хотите привести строку к нижнему регистру перед сохранением:

const schema2 = new Schema({
  test: {
    type: String,
    lowercase: true // Always convert `test` to lowercase
  }
});

Вы можете добавить любое свойство, которое вам нужно, в параметры SchemaType. Многие плагины полагаются на пользовательские параметры SchemaType. Например, плагин mongoose-autopopulate автоматически заполняет пути, если вы установили autopopulate: true в параметрах SchemaType. Mongoose поддерживает несколько встроенных параметров SchemaType, таких как lowercase в приведённом выше примере.

Параметр lowercase работает только для строк. Существуют определённые параметры, которые применяются ко всем типам схем, и некоторые, которые применяются к конкретным типам схем.

Все типы схем

  • required: boolean или функция, если true, добавляет валидатор обязательности для этого свойства
  • default: Любое или функция, устанавливает значение по умолчанию для пути. Если значение является функцией, возвращаемое значение функции используется в качестве значения по умолчанию.
  • select: boolean, указывает значения по умолчанию для проекций запросов
  • validate: функция, добавляет валидирующую функцию для этого свойства
  • get: функция, определяет пользовательского получателя для этого свойства с использованием Object.defineProperty().
  • set: функция, определяет пользовательского установщика для этого свойства с использованием Object.defineProperty().
  • alias: строка, только mongoose >= 4.10.0. Определяет виртуальное свойство с данным именем, которое получает/устанавливает этот путь.
  • immutable: boolean, определяет путь как неизменяемый. Mongoose предотвращает изменение неизменяемых путей, если у родительского документа нет isNew: true.
  • transform: функция, Mongoose вызывает эту функцию при вызове Document#toJSON() функции, включая при JSON.stringify() документа.
const numberSchema = new Schema({
  integerOnly: {
    type: Number,
    get: v => Math.round(v),
    set: v => Math.round(v),
    alias: 'i'
  }
});

const Number = mongoose.model('Number', numberSchema);

const doc = new Number();
doc.integerOnly = 2.001;
doc.integerOnly; // 2
doc.i; // 2
doc.i = 3.001;
doc.integerOnly; // 3
doc.i; // 3

Индексы

Вы также можете определить индексы MongoDB с помощью параметров типа схемы.

  • index: boolean, определяет, нужно ли создавать индекс для этого свойства.
  • unique: boolean, определяет, нужно ли создавать уникальный индекс для этого свойства.
  • sparse: boolean, определяет, нужно ли создавать разреженный индекс для этого свойства.
const schema2 = new Schema({
  test: {
    type: String,
    index: true,
    unique: true // Unique index. If you specify `unique: true`
    // specifying `index: true` is optional if you do `unique: true`
  }
});

Валедаторы для строк

  • lowercase: boolean, нужно ли всегда вызывать .toLowerCase() на значении
  • uppercase: boolean, нужно ли всегда вызывать .toUpperCase() на значении
  • trim: boolean, нужно ли всегда вызывать .trim() на значении
  • match: RegExp, создаёт валидатор, проверяющий, соответствует ли значение заданному регулярному выражению
  • enum: Массив, создаёт валидатор, проверяющий, принадлежит ли значение заданному массиву.
  • minLength: Число, создаёт валидатор, проверяющий, не меньше ли длина значения заданного числа
  • maxLength: Число, создаёт валидатор, проверяющий, не больше ли длина значения заданного числа
  • populate: Объект, устанавливает значения по умолчанию для параметров популяции

Валедаторы для чисел

  • min: Число, создаёт валидатор, проверяющий, больше ли или равно ли значение заданному минимуму.
  • max: Число, создаёт валидатор, проверяющий, меньше ли или равно ли значение заданному максимуму.
  • enum: Массив, создаёт валидатор, проверяющий, строго ли равно значение одному из значений в заданном массиве.
  • populate: Объект, устанавливает значения по умолчанию для параметров популяции

Дата

  • min: Дата, создаёт валидатор, проверяющий, больше ли или равно ли значение заданному минимуму.
  • max: Дата, создаёт валидатор, проверяющий, меньше ли или равно ли значение заданному максимуму.
  • expires: Число или строка, создаёт индекс TTL со значением, выраженным в секундах.

ObjectId

  • populate: Объект, устанавливает значения по умолчанию для параметров популяции

Примечания к использованию

Строка

Для объявления пути как строки можно использовать как глобальный конструктор String, так и строку 'String'

const schema1 = new Schema({ name: String }); // name will be cast to string
const schema2 = new Schema({ name: 'String' }); // Equivalent

const Person = mongoose.model('Person', schema2);

Если вы передаёте элемент с функцией toString(), Mongoose её вызовет, если элемент не является массивом, или если функция toString() строго равна Object.prototype.toString()

new Person({ name: 42 }).name; // "42" as a string
new Person({ name: { toString: () => 42 } }).name; // "42" as a string

// "undefined", will get a cast error if you `save()` this document
new Person({ name: { foo: 42 } }).name;

Число

Для объявления пути как числа можно использовать как глобальный конструктор Number, так и строку 'Number'

const schema1 = new Schema({ age: Number }); // age will be cast to a Number
const schema2 = new Schema({ age: 'Number' }); // Equivalent

const Car = mongoose.model('Car', schema2);

Существует несколько типов значений, которые будут успешно преобразованы в число.

new Car({ age: '15' }).age; // 15 as a Number
new Car({ age: true }).age; // 1 as a Number
new Car({ age: false }).age; // 0 as a Number
new Car({ age: { valueOf: () => 83 } }).age; // 83 as a Number

Если вы передаёте объект с функцией valueOf(), которая возвращает число, Mongoose её вызовет и присвоит возвращаемое значение пути.

Значения null и undefined не преобразуются.

NaN, строки, которые преобразуются в NaN, массивы и объекты, не имеющие функции valueOf(), приведут к ошибке преобразования (CastError) при валидации, то есть не бросят исключение во время инициализации, а только при валидации.

Даты

Встроенные Date методы не привязаны к механизму отслеживания изменений mongoose. Это означает, что если вы используете Date в вашем документе и изменяете его методом, таким как setMonth(), mongoose не будет знать об этом изменении, и doc.save() не сохранит это изменение. Если вам необходимо изменить Date типы, используя встроенные методы, сообщите mongoose об изменении с помощью doc.markModified('pathToYourDate') перед сохранением.

const Assignment = mongoose.model('Assignment', { dueDate: Date });
const doc = await Assignment.findOne();
doc.dueDate.setMonth(3);
await doc.save(); // THIS DOES NOT SAVE YOUR CHANGE

doc.markModified('dueDate');
await doc.save(); // works

Буфер

Для объявления пути как буфера можно использовать как глобальный конструктор Buffer, так и строку 'Buffer'

const schema1 = new Schema({ binData: Buffer }); // binData will be cast to a Buffer
const schema2 = new Schema({ binData: 'Buffer' }); // Equivalent

const Data = mongoose.model('Data', schema2);

Mongoose успешно преобразует указанные ниже значения в буферы.

const file1 = new Data({ binData: 'test'}); // {"type":"Buffer","data":[116,101,115,116]}
const file2 = new Data({ binData: 72987 }); // {"type":"Buffer","data":[27]}
const file4 = new Data({ binData: { type: 'Buffer', data: [1, 2, 3]}}); // {"type":"Buffer","data":[1,2,3]}

Смешанный

Тип схемы «все разрешено» SchemaType. Mongoose не будет выполнять преобразование типов для смешанных путей. Вы можете определить смешанный путь, используя Schema.Types.Mixed или передав пустой объект. Следующие примеры эквивалентны.

const Any = new Schema({ any: {} });
const Any = new Schema({ any: Object });
const Any = new Schema({ any: Schema.Types.Mixed });
const Any = new Schema({ any: mongoose.Mixed });

Поскольку Mixed — это тип без схемы, вы можете изменить значение на любое другое, но Mongoose потеряет возможность автоматически обнаруживать и сохранять эти изменения. Чтобы сообщить Mongoose, что значение типа Mixed изменилось, вам необходимо вызвать doc.markModified(path), передав путь к типу Mixed, который вы только что изменили.

Чтобы избежать этих побочных эффектов, можно использовать путь документа-поддокумента вместо этого.

person.anything = { x: [3, 4, { y: 'changed' }] };
person.markModified('anything');
person.save(); // Mongoose will save changes to `anything`.

ObjectIds

ObjectId — это специальный тип, обычно используемый для уникальных идентификаторов. Вот как вы объявляете схему с путем driver , который является ObjectId:

const mongoose = require('mongoose');
const carSchema = new mongoose.Schema({ driver: mongoose.ObjectId });

ObjectId — это класс, а ObjectIds — объекты. Однако они часто представляются в виде строк. При преобразовании ObjectId в строку с помощью toString(), вы получите 24-символьную шестнадцатеричную строку:

const Car = mongoose.model('Car', carSchema);

const car = new Car();
car.driver = new mongoose.Types.ObjectId();

typeof car.driver; // 'object'
car.driver instanceof mongoose.Types.ObjectId; // true

car.driver.toString(); // Something like "5e1a0651741b255ddda996c4"

Boolean

Булевы значения в Mongoose — это простые JavaScript булевы значения. По умолчанию Mongoose преобразует следующие значения в true:

  • true
  • 'true'
  • 1
  • '1'
  • 'yes'

Mongoose преобразует следующие значения в false:

  • false
  • 'false'
  • 0
  • '0'
  • 'no'

Любое другое значение приводит к CastError. Вы можете изменить значения, преобразуемые Mongoose в true или false, используя свойства convertToTrue и convertToFalse, которые являются JavaScript множествами.

const M = mongoose.model('Test', new Schema({ b: Boolean }));
console.log(new M({ b: 'nay' }).b); // undefined

// Set { false, 'false', 0, '0', 'no' }
console.log(mongoose.Schema.Types.Boolean.convertToFalse);

mongoose.Schema.Types.Boolean.convertToFalse.add('nay');
console.log(new M({ b: 'nay' }).b); // false

Массивы

Mongoose поддерживает массивы SchemaTypes и массивы поддокументов. Массивы SchemaTypes также называются примитивными массивами, а массивы поддокументов — массивами документов.

const ToySchema = new Schema({ name: String });
const ToyBoxSchema = new Schema({
  toys: [ToySchema],
  buffers: [Buffer],
  strings: [String],
  numbers: [Number]
  // ... etc
});

Массивы особенны, потому что они неявно имеют значение по умолчанию [] (пустой массив).

const ToyBox = mongoose.model('ToyBox', ToyBoxSchema);
console.log((new ToyBox()).toys); // []

Чтобы переопределить это значение по умолчанию, необходимо установить значение по умолчанию в undefined

const ToyBoxSchema = new Schema({
  toys: {
    type: [ToySchema],
    default: undefined
  }
});

Примечание: указание пустого массива эквивалентно Mixed. Все следующие примеры создают массивы Mixed:

const Empty1 = new Schema({ any: [] });
const Empty2 = new Schema({ any: Array });
const Empty3 = new Schema({ any: [Schema.Types.Mixed] });
const Empty4 = new Schema({ any: [{}] });

Карты

MongooseMap — это подкласс класса JavaScript Map. В этих документах мы будем использовать термины «карта» и MongooseMap взаимозаменяемо. В Mongoose карты используются для создания вложенных документов с произвольными ключами.

Примечание: в картах Mongoose ключи должны быть строками, чтобы хранить документ в MongoDB.

const userSchema = new Schema({
  // `socialMediaHandles` is a map whose values are strings. A map's
  // keys are always strings. You specify the type of values using `of`.
  socialMediaHandles: {
    type: Map,
    of: String
  }
});

const User = mongoose.model('User', userSchema);
// Map { 'github' => 'vkarpov15', 'twitter' => '@code_barbarian' }
console.log(new User({
  socialMediaHandles: {
    github: 'vkarpov15',
    twitter: '@code_barbarian'
  }
}).socialMediaHandles);

В приведенном выше примере явно не объявляются github или twitter как пути, но поскольку socialMediaHandles — это карта, вы можете хранить произвольные пары ключ/значение. Однако, поскольку socialMediaHandles — это карта, вы обязательно должны использовать .get() для получения значения ключа и .set() для установки значения ключа.

const user = new User({
  socialMediaHandles: {}
});

// Good
user.socialMediaHandles.set('github', 'vkarpov15');
// Works too
user.set('socialMediaHandles.twitter', '@code_barbarian');
// Bad, the `myspace` property will **not** get saved
user.socialMediaHandles.myspace = 'fail';

// 'vkarpov15'
console.log(user.socialMediaHandles.get('github'));
// '@code_barbarian'
console.log(user.get('socialMediaHandles.twitter'));
// undefined
user.socialMediaHandles.github;

// Will only save the 'github' and 'twitter' properties
user.save();

Типы карт хранятся в MongoDB как объекты BSON. Ключи в объекте BSON упорядочены, поэтому свойство порядка вставки карт сохраняется.

Mongoose поддерживает специальный синтаксис $* для заполнения всех элементов карты. Например, предположим, что ваша карта socialMediaHandles содержит ref:

const userSchema = new Schema({
  socialMediaHandles: {
    type: Map,
    of: new Schema({
      handle: String,
      oauth: {
        type: ObjectId,
        ref: 'OAuth'
      }
    })
  }
});
const User = mongoose.model('User', userSchema);

Для заполнения свойства socialMediaHandles каждого элемента oauth вы должны выполнить заполнение по socialMediaHandles.$*.oauth:

const user = await User.findOne().populate('socialMediaHandles.$*.oauth');

UUID

Mongoose также поддерживает тип UUID, который хранит экземпляры UUID как буферы Node.js. Мы рекомендуем использовать ObjectIds вместо UUID для уникальных идентификаторов документов в Mongoose, но вы можете использовать UUID, если это необходимо.

В Node.js UUID представляется как экземпляр типа bson.Binary с геттером, который преобразует двоичное представление в строку при обращении к нему. Mongoose хранит UUID как двоичные данные с типом 4 в MongoDB.

const authorSchema = new Schema({
  _id: Schema.Types.UUID, // Can also do `_id: 'UUID'`
  name: String
});

const Author = mongoose.model('Author', authorSchema);

const bookSchema = new Schema({
  authorId: { type: Schema.Types.UUID, ref: 'Author' }
});
const Book = mongoose.model('Book', bookSchema);

const author = new Author({ name: 'Martin Fowler' });
console.log(typeof author._id); // 'string'
console.log(author.toObject()._id instanceof mongoose.mongo.BSON.Binary); // true

const book = new Book({ authorId: '09190f70-3d30-11e5-8814-0f4df9a59c41' });

Для создания UUID мы рекомендуем использовать встроенный генератор UUIDv4 Node .

const { randomUUID } = require('crypto');

const schema = new mongoose.Schema({
  docId: {
    type: 'UUID',
    default: () => randomUUID()
  }
});

BigInt

Mongoose поддерживает JavaScript BigInt как тип схемы. BigInt хранятся как 64-битные целые числа в MongoDB (тип BSON «long»).

const questionSchema = new Schema({
  answer: BigInt
});
const Question = mongoose.model('Question', questionSchema);

const question = new Question({ answer: 42n });
typeof question.answer; // 'bigint'

Геттеры

Геттеры — это виртуальные пути, определённые в вашей схеме. Например, предположим, что вы хотите хранить изображения профиля пользователей как относительные пути, а затем добавлять хост в своём приложении. Ниже показано, как следует структурировать вашу userSchema:

const root = 'https://s3.amazonaws.com/mybucket';

const userSchema = new Schema({
  name: String,
  picture: {
    type: String,
    get: v => `${root}${v}`
  }
});

const User = mongoose.model('User', userSchema);

const doc = new User({ name: 'Val', picture: '/123.png' });
doc.picture; // 'https://s3.amazonaws.com/mybucket/123.png'
doc.toObject({ getters: false }).picture; // '/123.png'

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

const schema = new Schema({
  arr: [{ url: String }]
});

const root = 'https://s3.amazonaws.com/mybucket';

// Bad, don't do this!
schema.path('arr').get(v => {
  return v.map(el => Object.assign(el, { url: root + el.url }));
});

// Later
doc.arr.push({ key: String });
doc.arr[0]; // 'undefined' because every `doc.arr` creates a new array!

Вместо объявления геттера для массива, как показано выше, вы должны объявить геттер для строки url , как показано ниже. Если вам необходимо объявить геттер для вложенного документа или массива, будьте очень осторожны!

const schema = new Schema({
  arr: [{ url: String }]
});

const root = 'https://s3.amazonaws.com/mybucket';

// Good, do this instead of declaring a getter on `arr`
schema.path('arr.0.url').get(v => `${root}${v}`);

Схемы

Чтобы объявить путь как другую схему, установите type в экземпляр подсхемы.

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

const subSchema = new mongoose.Schema({
  // some schema definition here
});

const schema = new mongoose.Schema({
  data: {
    type: subSchema,
    default: {}
  }
});

Создание пользовательских типов

Mongoose также можно расширить с помощью пользовательских SchemaTypes. Ищите совместимые типы на сайте плагинов, такие как mongoose-long, mongoose-int32 и mongoose-function.

Подробнее о создании пользовательских SchemaTypes читайте здесь.

Функция `schema.path()`

Функция schema.path() возвращает экземпляр типа схемы для данного пути.

const sampleSchema = new Schema({ name: { type: String, required: true } });
console.log(sampleSchema.path('name'));
// Output looks like:
/**
 * SchemaString {
 *   enumValues: [],
  *   regExp: null,
  *   path: 'name',
  *   instance: 'String',
  *   validators: ...
  */

Вы можете использовать эту функцию для проверки типа схемы для данного пути, включая используемые валидаторы и тип.

Дополнительные материалы

  • Введение в Mongoose SchemaTypes
  • Типы схемы Mongoose

Следующее

Теперь, когда мы разобрались с SchemaTypes, давайте рассмотрим соединения.

© 2010 LearnBoost
Licensed under the MIT License.
https://mongoosejs.com/docs/schematypes.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API