Запросите массив вложенных документов верхнего уровня с помощью MONGOOSE, чтобы вернуть только N самых последних вложенных документов в массиве
Я ищу условия, поля и т. Д., Которые мне понадобятся в запросе, или асинхронный поток для запроса одного документа, который имеет массив вложенных документов на верхнем уровне.
Модель:
let postSchema = new mongoose.Schema({
date : {type: Boolean, require: true},
message : {type: String, require: true}
});
let Post = new mongoose.Schema({
locid: {type: String, unique: {indexed: true}, require: true},
posts : {type: [postSchema], require: false}
});
По сути, я бы предоставил значение Locid, как показано выше, в функцию, которая, я надеюсь, будет выглядеть так:
Post.methods.getLastTwentyPostsForOne = function (locid) {
return new Promise(function (values, error) {
let p = new Post();
p.find({"locid" : locid}, {/*...the conditions and elements here...*/}).exec()
.then(function (found) {
//..would return the last 20 posts, or less if there are less...
}, function (err) {
//..handle errors..
})
})
};
Самым простым способом, который я могу придумать, было бы просто извлечь весь массив, и, поскольку mongo хранит записи одну за другой в хронологическом порядке, просто проанализировать массив и добавить последние 20 или менее записей, содержащихся в нем, в окончательный вариант. массив (со второй функцией, основанной на обещаниях).
это делает предыдущий метод похожим на это:
function returnLastTwenty(posts) {
return new Promise(function (results) {
if (posts.length === 0) {
return results([]);
}
if (posts.length <= 20) {
return results(posts);
}
let length = posts.length;
var next = [];
for (i = length - 21; i < (length -1); i++) {
next.append(posts[i]);
}
if (next.length === 20) {
return results(next);
} else {
console.log('Some Error: found more than 20 posts, but parsed less');
return results(next)
}
});
}
Post.methods.getLastTwentyPostsForOne = function (locid) {
return new Promise(function (values, error) {
let p = new Post();
p.find({"locid" : locid})
.then(function (found) {
if (found.length === 1) {
returnLastTwenty(found[0].posts)
.then(function (results) {
return values(results)
})
} else {
return error("Didn't find a unique document for locid: " + locid);
}
}, function (err) {
return error(err)
})
})
};
Хотя это работает... цикл for и второй асинхронный метод кажутся слишком сложными. Есть какие-нибудь подсказки относительно того, как добиться этого с помощью mongoose с помощью одного запроса?
Обновление - Ответ
Благодаря комментарию Ratan Kumar, следующее, казалось, помогло.
function getPosts(amount, asOfIndex, locid) {
return new Promise(function (posts, none) {
Post.find({'locid' : locid},
{'posts' :
{"$slice" : [asOfIndex, amount]}
}
)
.exec()
.catch(function (err) {
return none(err);
})
.then(function (found) {
if (found.length === 0 || found.length > 1) return none(); //This can be changed for more specific error handling
return posts(found[0].posts);
});
});
}
С индексом, являющимся произвольным числом, переданным через. Это может быть индекс из вызова API и т. Д. Где asOfIndex должен быть отрицательным, чтобы срезаться снизу массива, и положительным сверху ( https://docs.mongodb.com/manual/reference/operator/projection/slice/). Нарезка заданного интервала становится вопросом игры со значениями asOfIndex и amount.
Также, выполняя некоторые копания, нашел способ запросить элемент даты поддокументов внутри массива на основе интервала дат благодаря следующим сообщениям: /questions/26855888/mongodb-elemmatch-neskolko-elementov-v-massive/26855897#26855897 и /questions/5061679/mongodb-subdocument-diapazon-dat-vozvraschaet-nevernyie-rezultatyi/5061689#5061689. Для тех, кто хочет что-то подобное!
function getPostsBetween(recentDate, farthestDate, forLocid) {
return new Promise(function (posts, none) {
Post.aggregate(
{'$match': {'locid': forLocid}},
{'$unwind' : '$posts'},
{'$match' :
{'$and': [
{'posts.date': {'$gt': new Date(farthestDate.toISOString())}},
{'posts.date': {'$lt': new Date(recentDate.toISOString())}}
]
}
},
{"$group" : {
'_id' : '$_id',
"posts" : {'$push' : '$posts'}
}
})
.exec()
.catch(function (err) {
return none(err);
})
.then(function (found) {
if (found.length === 0 || found.length > 1) return none();
return found(found[0].posts);
})
})
}