如何在示例文档中填充“组件”:
{
"__v": 1,
"_id": "5252875356f64d6d28000001",
"pages": [
{
"__v": 1,
"_id": "5252875a56f64d6d28000002",
"page": {
"components": [
"525287a01877a68528000001"
]
}
}
],
"author": "Book Author",
"title": "Book Title"
}
这是我的JS,我收到了Mongoose的文档:
Project.findById(id).populate('pages').exec(function(err, project) {
res.json(project);
});
答案 0 :(得分:179)
Mongoose 4.5支持此
Project.find(query)
.populate({
path: 'pages',
populate: {
path: 'components',
model: 'Component'
}
})
.exec(function(err, docs) {});
答案 1 :(得分:104)
这对我有用:
Project.find(query)
.lean()
.populate({ path: 'pages' })
.exec(function(err, docs) {
var options = {
path: 'pages.components',
model: 'Component'
};
if (err) return res.json(500);
Project.populate(docs, options, function (err, projects) {
res.json(projects);
});
});
答案 2 :(得分:28)
正如其他人所说,Mongoose 4
支持这一点。非常重要的是要注意,如果需要,你也可以递归到一个级别以上 - 尽管在文档中没有注明:
Project.findOne({name: req.query.name})
.populate({
path: 'threads',
populate: {
path: 'messages',
model: 'Message',
populate: {
path: 'user',
model: 'User'
}
}
})
答案 3 :(得分:17)
您可以像这样填充多个嵌套文档。
Project.find(query)
.populate({
path: 'pages',
populate: [{
path: 'components',
model: 'Component'
},{
path: 'AnotherRef',
model: 'AnotherRef',
select: 'firstname lastname'
}]
})
.exec(function(err, docs) {});
答案 4 :(得分:3)
这是最好的解决方案:
Car
.find()
.populate({
path: 'pages.page.components'
})
答案 5 :(得分:2)
我发现这非常有用,在钩子之前创建一个featherjs来填充2 ref级别的深度关系。猫鼬模型只有
tables = new Schema({
..
tableTypesB: { type: Schema.Types.ObjectId, ref: 'tableTypesB' },
..
}
tableTypesB = new Schema({
..
tableType: { type: Schema.Types.ObjectId, ref: 'tableTypes' },
..
}
然后在pearjs之前挂钩:
module.exports = function(options = {}) {
return function populateTables(hook) {
hook.params.query.$populate = {
path: 'tableTypesB',
populate: { path: 'tableType' }
}
return Promise.resolve(hook)
}
}
与我试图实现这一目标的其他方法相比,这么简单。
答案 6 :(得分:1)
您也可以使用$lookup
聚合来完成此操作,可能现在最好的方法是从mongo中淘汰
Project.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(id) } },
{ "$lookup": {
"from": Pages.collection.name,
"let": { "pages": "$pages" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$pages" ] } } },
{ "$lookup": {
"from": Component.collection.name,
"let": { "components": "$components" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$components" ] } } },
],
"as": "components"
}},
],
"as": "pages"
}}
])
答案 7 :(得分:1)
我通过另一个特定于KeystoneJS的问题找到了这个问题,但被标记为重复。如果这里有人在寻找Keystone的答案,这就是我在Keystone中进行深度填充查询的方式。
Mongoose two level population using KeystoneJs [duplicate]
exports.getStoreWithId = function (req, res) {
Store.model
.find()
.populate({
path: 'productTags productCategories',
populate: {
path: 'tags',
},
})
.where('updateId', req.params.id)
.exec(function (err, item) {
if (err) return res.apiError('database error', err);
// possibly more than one
res.apiResponse({
store: item,
});
});
};
答案 8 :(得分:1)
猫鼬5.4支持此
Project.find(query)
.populate({
path: 'pages.page.components',
model: 'Component'
})
答案 9 :(得分:1)
如果您想更深入地填充另一个级别,您需要执行以下操作:
Airlines.findById(id)
.populate({
path: 'flights',
populate:[
{
path: 'planeType',
model: 'Plane'
},
{
path: 'destination',
model: 'Location',
populate: { // deeper
path: 'state',
model: 'State',
populate: { // even deeper
path: 'region',
model: 'Region'
}
}
}]
})
答案 10 :(得分:0)
对于populate
有问题并且还想这样做的人:
clients
,users
,rooms
,messasges
。refPath
或dynamic reference populate
和path
和model
选项findOneAndReplace
/ replaceOne
与$exists
一起使用上下文
目标
clients
,users
和bot
。clients
或users
的邮件。 _sender类型的客户端模型为clients
,用户为users
。 消息架构:
const messageSchema = new Schema({
room: {
type: Schema.Types.ObjectId,
ref: 'rooms',
required: [true, `Room's id`]
},
sender: {
_id: { type: Schema.Types.Mixed },
type: {
type: String,
enum: ['clients', 'users', 'bot'],
required: [true, 'Only 3 options: clients, users or bot.']
}
},
timetoken: {
type: String,
required: [true, 'It has to be a Nanosecond-precision UTC string']
},
data: {
lang: String,
// Format samples on https://docs.chatfuel.com/api/json-api/json-api
type: {
text: String,
quickReplies: [
{
text: String,
// Blocks' ids.
goToBlocks: [String]
}
]
}
}
mongoose.model('messages', messageSchema);
解决方案
我的服务器端API请求
我的代码
实用程序功能(在chatUtils.js
文件上)以获取要保存的消息类型:
/**
* We filter what type of message is.
*
* @param {Object} message
* @returns {string} The type of message.
*/
const getMessageType = message => {
const { type } = message.data;
const text = 'text',
quickReplies = 'quickReplies';
if (type.hasOwnProperty(text)) return text;
else if (type.hasOwnProperty(quickReplies)) return quickReplies;
};
/**
* Get the Mongoose's Model of the message's sender. We use
* the sender type to find the Model.
*
* @param {Object} message - The message contains the sender type.
*/
const getSenderModel = message => {
switch (message.sender.type) {
case 'clients':
return 'clients';
case 'users':
return 'users';
default:
return null;
}
};
module.exports = {
getMessageType,
getSenderModel
};
我的服务器端(使用Nodejs)获得保存消息的请求:
app.post('/api/rooms/:roomId/messages/new', async (req, res) => {
const { roomId } = req.params;
const { sender, timetoken, data } = req.body;
const { uuid, state } = sender;
const { type } = state;
const { lang } = data;
// For more info about message structure, look up Message Schema.
let message = {
room: new ObjectId(roomId),
sender: {
_id: type === 'bot' ? null : new ObjectId(uuid),
type
},
timetoken,
data: {
lang,
type: {}
}
};
// ==========================================
// CONVERT THE MESSAGE
// ==========================================
// Convert the request to be able to save on the database.
switch (getMessageType(req.body)) {
case 'text':
message.data.type.text = data.type.text;
break;
case 'quickReplies':
// Save every quick reply from quickReplies[].
message.data.type.quickReplies = _.map(
data.type.quickReplies,
quickReply => {
const { text, goToBlocks } = quickReply;
return {
text,
goToBlocks
};
}
);
break;
default:
break;
}
// ==========================================
// SAVE THE MESSAGE
// ==========================================
/**
* We save the message on 2 ways:
* - we replace the message type `quickReplies` (if it already exists on database) with the new one.
* - else, we save the new message.
*/
try {
const options = {
// If the quickRepy message is found, we replace the whole document.
overwrite: true,
// If the quickRepy message isn't found, we create it.
upsert: true,
// Update validators validate the update operation against the model's schema.
runValidators: true,
// Return the document already updated.
new: true
};
Message.findOneAndUpdate(
{ room: roomId, 'data.type.quickReplies': { $exists: true } },
message,
options,
async (err, newMessage) => {
if (err) {
throw Error(err);
}
// Populate the new message already saved on the database.
Message.populate(
newMessage,
{
path: 'sender._id',
model: getSenderModel(newMessage)
},
(err, populatedMessage) => {
if (err) {
throw Error(err);
}
res.send(populatedMessage);
}
);
}
);
} catch (err) {
logger.error(
`#API Error on saving a new message on the database of roomId=${roomId}. ${err}`,
{ message: req.body }
);
// Bad Request
res.status(400).send(false);
}
});
提示:
对于数据库:
refPath
上使用的工具getSenderModel
来代替populate()
。这是因为机器人。 sender.type
可以是:带有数据库的users
,带有数据库的clients
和没有数据库的bot
。 refPath
需要真实的Model参考,否则,Mongooose会抛出错误。sender._id
可以为用户和客户端输入ObjectId
,或为漫游器是null
。对于API请求逻辑:
quickReply
消息(消息数据库必须只有一个quickReply,但是您可以根据需要添加许多简单的文本消息)。我们使用findOneAndUpdate
代替replaceOne
或findOneAndReplace
。findOneAndUpdate
执行查询操作(populate
)和callback
操作。如果您不知道是否使用async/await
,then()
,exec()
或callback(err, document)
,则这一点很重要。有关更多信息,请查看Populate Doc。overwrite
选项取代了快速回复消息,而没有$set
查询运算符。upsert
选项告诉Mongoose。findOneAndUpdate
和populate()
保存的消息是什么。populate
中,我们使用getSenderModel
创建自定义动态模型引用。我们可以使用Mongoose动态参考,因为sender.type
的{{1}}没有任何Mongoose模型。我们将Populating Across Database与bot
和model
选择素一起使用。我已经花了很多时间在这里和那里解决小问题,希望对您有所帮助!
答案 11 :(得分:0)
我为此奋斗了整整一天。上述解决方案均无效。在我的案例中,唯一有效的示例如下:
{
outerProp1: {
nestedProp1: [
{ prop1: x, prop2: y, prop3: ObjectId("....")},
...
],
nestedProp2: [
{ prop1: x, prop2: y, prop3: ObjectId("....")},
...
]
},
...
}
要执行以下操作:(假定在提取后进行填充-但在从Model类中调用填充(在exec之后)也可以使用)
await doc.populate({
path: 'outerProp1.nestedProp1.prop3'
}).execPopulate()
// doc is now populated
换句话说,最外面的路径属性必须包含完整路径。似乎没有部分完整的路径与填充属性结合使用(并且似乎不需要模型属性;因为它包含在架构中,所以很有意义)。花了我整天的时间来解决这个问题!不确定为什么其他示例不起作用。
(使用猫鼬5.5.32)
答案 12 :(得分:0)
我使用以下简洁的语法。这个代码块来自我的项目
const result = await Result.find(filter).populate('student exam.subject')
说明
假设您有两个架构
考试架构
const ExamSchema = new mongoose.Schema({
...
type: String,
...
})
结果架构
const resultSchema = new mongoose.Schema({
...
exam: ExamSchema,
student: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
}
})
如果我想从结果中查询和填充
仅通过学生证
const result = await Result.find(filter).populate('student')
仅按考试类型
const result = await Result.find(filter).populate('exam.type')
按学生证和考试类型
const result = await Result.find(filter).populate('student exam.type')
如果您需要更多说明,请在评论中提问
答案 13 :(得分:-3)
删除文档参考
if (err) {
return res.json(500);
}
Project.populate(docs, options, function (err, projects) {
res.json(projects);
});
这对我有用。
if (err) {
return res.json(500);
}
Project.populate(options, function (err, projects) {
res.json(projects);
});