Comment puis-je renseigner "composants" dans l'exemple de document:
{
"__v": 1,
"_id": "5252875356f64d6d28000001",
"pages": [
{
"__v": 1,
"_id": "5252875a56f64d6d28000002",
"page": {
"components": [
"525287a01877a68528000001"
]
}
}
],
"author": "Book Author",
"title": "Book Title"
}
C'est mon JS où je reçois un document de Mongoose:
Project.findById(id).populate('pages').exec(function(err, project) {
res.json(project);
});
Mongoose 4.5 supporte cette
Project.find(query)
.populate({
path: 'pages',
populate: {
path: 'components',
model: 'Component'
}
})
.exec(function(err, docs) {});
Ça marche pour moi:
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);
});
});
Documentation: Model.populate
Comme d'autres l'ont noté, Mongoose 4
supporte cela. Il est très important de noter que vous pouvez également accéder à plus d'un niveau, si cela est nécessaire - bien que cela ne soit pas noté dans la documentation:
Project.findOne({name: req.query.name})
.populate({
path: 'threads',
populate: {
path: 'messages',
model: 'Message',
populate: {
path: 'user',
model: 'User'
}
}
})
Vous pouvez remplir plusieurs documents imbriqués comme ceci.
Project.find(query)
.populate({
path: 'pages',
populate: [{
path: 'components',
model: 'Component'
},{
path: 'AnotherRef',
model: 'AnotherRef',
select: 'firstname lastname'
}]
})
.exec(function(err, docs) {});
J'ai trouvé cela très utile en créant une plume avant le crochet pour peupler une relation profonde à 2 niveaux de référence. Les modèles de mangouste ont simplement
tables = new Schema({
..
tableTypesB: { type: Schema.Types.ObjectId, ref: 'tableTypesB' },
..
}
tableTypesB = new Schema({
..
tableType: { type: Schema.Types.ObjectId, ref: 'tableTypes' },
..
}
puis en plumes avant crochet
module.exports = function(options = {}) {
return function populateTables(hook) {
hook.params.query.$populate = {
path: 'tableTypesB',
populate: { path: 'tableType' }
}
return Promise.resolve(hook)
}
}
Si simple comparé à d'autres méthodes, j'essayais d'y parvenir.
J'ai trouvé cette question dans une autre question, spécifique à KeystoneJS, mais marquée comme étant en double. Si quelqu'un ici peut être à la recherche d'une réponse Keystone, voici comment j'ai traité ma requête de remplissage en profondeur dans Keystone.
population de Mongoose à deux niveaux utilisant 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,
});
});
};
Vous pouvez le faire en utilisant $lookup
agrégation également et probablement la meilleure façon de peupler est de disparaître du 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"
}}
])
C'est la meilleure solution:
Car
.find()
.populate({
path: 'pages.page.components'
})
Pour quelqu'un qui a le problème avec populate
et qui veut aussi le faire:
clients
, users
, rooms
, messasges
.refPath
ou référence dynamiquepopulate
avec les options path
et model
findOneAndReplace
/replaceOne
avec $exists
[~ # ~] contexte [~ # ~]
But
clients
, users
& bot
.clients
ou users
avec ses modèles Mongoose. Les modèles client de type _sender sont clients
, pour l'utilisateur, users
.Schéma du message :
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);
[~ # ~] solution [~ # ~]
Demande d'API côté serveur
Mon code
Fonction utilitaire (sur le fichier chatUtils.js
) Pour obtenir le type de message que vous souhaitez enregistrer:
/**
* 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
};
Mon côté serveur (utilisant Nodejs) pour obtenir la demande de sauvegarde du message:
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);
}
});
CONSEILS :
Pour la base de données:
refPath
, nous utilisons le util getSenderModel
qui est utilisé sur populate()
. C'est à cause du bot. Le sender.type
Peut être: users
avec sa base de données, clients
avec sa base de données et bot
sans base de données. Le refPath
a besoin de vrai Référence de modèle, sinon Mongooose renvoie une erreur.sender._id
Peut être de type ObjectId
pour les utilisateurs et les clients, ou null
pour le bot.Pour la logique de demande d'API:
quickReply
(la base de données de messages ne doit comporter qu’une seule réponse rapide, mais autant de messages texte simples que vous le souhaitez). Nous utilisons le findOneAndUpdate
au lieu de replaceOne
ou findOneAndReplace
.findOneAndUpdate
) et l'opération populate
avec le callback
de chacun. Ceci est important si vous ne savez pas si utiliser async/await
, then()
, exec()
ou callback(err, document)
. Pour plus d'informations, regardez le Populate Doc .overwrite
et sans opérateur de requête $set
.upsert
.findOneAndUpdate
et pour la populate()
.populate
, nous créons une référence de modèle dynamique personnalisée avec le getSenderModel
. Nous pouvons utiliser la référence dynamique Mongoose car le sender.type
Pour bot
n'a pas de modèle Mongoose. Nous utilisons un Populating Across Database avec model
et path
optins.J'ai passé beaucoup d'heures à résoudre de petits problèmes ici et là et j'espère que cela aidera quelqu'un! ????