#mongoose
#мангуст
Вопрос:
Я неопытный программист, и я все еще новичок в mongoose. Избавьте меня, если я задаю глупый вопрос.
Допустим, у меня есть коллекция из более чем 100 документов. Я хочу указать один документ и найти 10 документов, созданных до и 10 документов, созданных после этого документа.
Одно из решений, которое я придумал, выглядит примерно так:
let firstDoc;
let tenBefore;
let tenAfter;
// find the starting document
document.findById(id, function(err, foundDoc){
if(err) {
...
} else {
firstDoc = foundDoc;
// find the 10 documents before
document
.find({created_at: {$lt: foundDoc.created_at}})
.limit(10)
.exec(function(err, beforeDocs) {
if(err) {...} else {
tenBefore = beforeDocs;
// find the 10 documents after
document
.find({created_at: {$gt: foundDoc.created_at}})
.limit(10)
.exec(function(err, afterDocs) {
if(err) {...} else {
tenAfter = afterDocs;
}
});
}
});
}
});
Я еще не пробовал это, чтобы посмотреть, работает ли это вообще или нет, но это, очевидно, не лучшее решение (ужасное при этом) и может считаться адом обратного вызова.
Если кто-нибудь с большим опытом и знаниями может помочь мне с этой проблемой, я был бы очень признателен.
Ответ №1:
В ES7 вы можете очистить обратные вызовы с помощью async / await. Вы также можете запускать вторые запросы одновременно с Promise.all
async function() {
const foundDoc = await document.findById(id).catch(err => ...);
const [beforeDocs, afterDocs] = await Promise.all([
document
.find({created_at: {$lt: foundDoc.created_at}})
.sort({created_at: -1}) // sort descending to get latest 10
.limit(10),
document
.find({created_at: {$gt: foundDoc.created_at}})
.sort({created_at: 1}) // sort ascending to get earliest 10
.limit(10),
]);
}
AsyncAwait:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Разрушение: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
Promise.all:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Вы также можете использовать aggregate для выполнения всех трех запросов в одном запросе, но это немного сложнее. Это вернет документ, который вы запрашиваете по _id с двумя дополнительными полями массива beforeDocs
и afterDocs
.
Пример:https://mongoplayground.net/p/6KeNaCpaifO
db.collection.aggregate([
{$match: {
_id: 5 // use mongoose.Types.ObjectId('YOUROBJECTID')
}},
{$lookup: {
from: "collection",
as: "beforeDocs",
let: {
"parent_created_at": "$created_at"
},
pipeline: [
{$match: {
// Need to use $expr to access parent created at date
$expr: {$lt: ["$created_at", "$$parent_created_at"]}
}},
{$sort: {created_at: -1}},
{$limit: 10}
]
}},
{$lookup: {
from: "collection",
as: "afterDocs",
let: {
"parent_created_at": "$created_at"
},
pipeline: [
{$match: {
$expr: {$gt: ["$created_at", "$$parent_created_at"]}
}},
{$sort: {created_at: 1}},
{$limit: 10}
]
}},
])