Запросы
Мongoose модели предоставляют несколько статических вспомогательных функций для операций CRUD. Каждая из этих функций возвращает объект mongoose Query.
Model.deleteMany()Model.deleteOne()Model.find()Model.findById()Model.findByIdAndDelete()Model.findByIdAndRemove()Model.findByIdAndUpdate()Model.findOne()Model.findOneAndDelete()Model.findOneAndRemove()Model.findOneAndReplace()Model.findOneAndUpdate()Model.replaceOne()Model.updateMany()Model.updateOne()
Запрос mongoose может быть выполнен двумя способами. Во-первых, если вы передаете callback функцию, Mongoose выполнит запрос асинхронно и передаст результаты в callback.
Запрос также имеет .then() функцию и, следовательно, может использоваться как обещание.
Выполнение
При выполнении запроса вы указываете запрос в виде JSON-документа. Синтаксис JSON-документа такой же, как в консоли MongoDB.
const Person = mongoose.model('Person', yourSchema);
// find each person with a last name matching 'Ghost', selecting the `name` and `occupation` fields
const person = await Person.findOne({ 'name.last': 'Ghost' }, 'name occupation');
// Prints "Space Ghost is a talk show host".
console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation);
Что person зависит от операции: для findOne() это потенциально-нулевой единственный документ, find() список документов , count() число документов , update() число затронутых документов , и т. д. Более подробную информацию можно найти в документации API для моделей.
Теперь давайте посмотрим, что происходит, когда await не используется:
// find each person with a last name matching 'Ghost'
const query = Person.findOne({ 'name.last': 'Ghost' });
// selecting the `name` and `occupation` fields
query.select('name occupation');
// execute the query at a later time
const person = await query.exec();
// Prints "Space Ghost is a talk show host."
console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation);
В приведенном выше коде переменная query имеет тип Query. Query позволяет создавать запрос с использованием синтаксиса цепочки, а не указания JSON-объекта. Ниже приведены два эквивалентных примера.
// With a JSON doc
await Person.
find({
occupation: /host/,
'name.last': 'Ghost',
age: { $gt: 17, $lt: 66 },
likes: { $in: ['vaporizing', 'talking'] }
}).
limit(10).
sort({ occupation: -1 }).
select({ name: 1, occupation: 1 }).
exec();
// Using query builder
await Person.
find({ occupation: /host/ }).
where('name.last').equals('Ghost').
where('age').gt(17).lt(66).
where('likes').in(['vaporizing', 'talking']).
limit(10).
sort('-occupation').
select('name occupation').
exec();
Полный список вспомогательных функций запросов можно найти в документации API.
Запросы не являются обещаниями
Запросы Mongoose не являются обещаниями. Запросы являются thenable, что означает, что у них есть .then() метод для async/await для удобства. Однако, в отличие от обещаний, вызов .then() запроса выполняет запрос, поэтому вызов then() несколько раз приведет к ошибке.
const q = MyModel.updateMany({}, { isDeleted: true });
await q.then(() => console.log('Update 2'));
// Throws "Query was already executed: Test.updateMany({}, { isDeleted: true })"
await q.then(() => console.log('Update 3'));
Ссылки на другие документы
В MongoDB нет объединений, но иногда нам все же нужны ссылки на документы в других коллекциях. Для этого и используется популяция. Подробнее о том, как включать документы из других коллекций в результаты запроса, можно прочитать здесь.
Потоковая обработка
Вы можете обрабатывать результаты запроса из MongoDB. Для этого необходимо вызвать функцию Query#cursor(), которая вернет экземпляр QueryCursor.
const cursor = Person.find({ occupation: /host/ }).cursor();
for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
console.log(doc); // Prints documents one at a time
}
Итерация по результатам запроса Mongoose с помощью асинхронных итераторов также создает курсор.
for await (const doc of Person.find()) {
console.log(doc); // Prints documents one at a time
}
Курсоры могут быть подвержены таймаутам курсора. По умолчанию MongoDB закроет курсор через 10 минут, и последующие next() вызовы приведут к ошибке MongoServerError: cursor id 123 not found. Для отмены этого установите опцию noCursorTimeout в вашем курсоре.
// MongoDB won't automatically close this cursor after 10 minutes.
const cursor = Person.find().cursor().addCursorFlag('noCursorTimeout', true);
Однако, курсоры все еще могут истечь из-за таймаутов бездействия сессии. Таким образом, даже курсор с установленной опцией noCursorTimeout все равно истечет через 30 минут бездействия. Более подробную информацию о работе с таймаутами бездействия сессии можно найти в документации MongoDB.
Сравнение с агрегацией
Агрегация может выполнять многие те же действия, что и запросы. Например, ниже показано, как вы можете использовать aggregate() для поиска документов, где name.last = 'Ghost':
const docs = await Person.aggregate([{ $match: { 'name.last': 'Ghost' } }]);
Однако, только потому, что вы можете использовать aggregate(), не значит, что нужно. В целом, следует использовать запросы, когда это возможно, и использовать aggregate() только в крайних случаях.
В отличие от результатов запросов, Mongoose не hydrate() результаты агрегации. Результаты агрегации всегда являются POJO, а не документами Mongoose.
const docs = await Person.aggregate([{ $match: { 'name.last': 'Ghost' } }]);
docs[0] instanceof mongoose.Document; // false
Кроме того, в отличие от фильтров запросов, Mongoose также не преобразует агрегационные конвейеры. Это означает, что вы несете ответственность за то, чтобы убедиться, что значения, которые вы передаете в агрегационный конвейер, имеют правильный тип.
const doc = await Person.findOne();
const idString = doc._id.toString();
// Finds the `Person`, because Mongoose casts `idString` to an ObjectId
const queryRes = await Person.findOne({ _id: idString });
// Does **not** find the `Person`, because Mongoose doesn't cast aggregation
// pipelines.
const aggRes = await Person.aggregate([{ $match: { _id: idString } }]);
Сортировка
Сортировка — это способ гарантировать, что результаты запроса возвращаются в нужном порядке.
const personSchema = new mongoose.Schema({
age: Number
});
const Person = mongoose.model('Person', personSchema);
for (let i = 0; i < 10; i++) {
await Person.create({ age: i });
}
await Person.find().sort({ age: -1 }); // returns age starting from 10 as the first entry
await Person.find().sort({ age: 1 }); // returns age starting from 0 as the first entry
При сортировке по нескольким полям порядок ключей сортировки определяет, по какому ключу MongoDB-сервер будет сортировать в первую очередь.
const personSchema = new mongoose.Schema({
age: Number,
name: String,
weight: Number
});
const Person = mongoose.model('Person', personSchema);
const iterations = 5;
for (let i = 0; i < iterations; i++) {
await Person.create({
age: Math.abs(2 - i),
name: 'Test' + i,
weight: Math.floor(Math.random() * 100) + 1
});
}
await Person.find().sort({ age: 1, weight: -1 }); // returns age starting from 0, but while keeping that order will then sort by weight.
Вы можете посмотреть результат одного выполнения этого блока ниже. Как видите, возраст сортируется от 0 до 2, но когда возраст одинаковый, сортируется по весу.
[
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb37'),
age: 0,
name: 'Test2',
weight: 67,
__v: 0
},
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb35'),
age: 1,
name: 'Test1',
weight: 99,
__v: 0
},
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb39'),
age: 1,
name: 'Test3',
weight: 73,
__v: 0
},
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb33'),
age: 2,
name: 'Test0',
weight: 65,
__v: 0
},
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb3b'),
age: 2,
name: 'Test4',
weight: 62,
__v: 0
}
];
Далее
Теперь, когда мы рассмотрели Queries, давайте посмотрим на валидацию.
© 2010 LearnBoost
Licensed under the MIT License.
https://mongoosejs.com/docs/queries.html