当前位置: 动力学知识库 > 问答 > 编程问答 >

mongodb - Populate nested array in mongoose - Node.js

问题描述:

These are my schemas (Topic is parent and contains a list of 'Thought's):

var TopicSchema = new mongoose.Schema({

title: { type: String, unique: true },

category: String,

thoughts: [ThoughtSchema]

}, {

timestamps: true,

toObject: {virtuals: true},

toJSON: {virtuals: true}

});

var ThoughtSchema = new mongoose.Schema({

text: String,

author: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},

votes:[{

_id:false,

voter: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},

up: Boolean,

date: {type: Date, default: Date.now}

}]

}, {

timestamps: true,

toObject: {virtuals: true},

toJSON: {virtuals: true}

});

....

I am trying to read the thought's author and change my get Topic api like this:

...

var cursor = Topic.find(query).populate({

path: 'thoughts',

populate: {

path: 'author',

model: 'User'

}

}).sort({popularity : -1, date: -1});

return cursor.exec()

.then(respondWithResult(res))

.catch(handleError(res));

...

But the author is null.. i also do not get any error in the console. What is wrong here?

Edit: Actually i do not need the Thought as a schema, it does not have its own collection in database. It will be saved in topics. But in order to use timestamps option with thoughts, i needed to extract its contents to a new local schema ThoughtSchema. But i have now defined the contents of thoughtSchema directly in the thoughts array of topics, it still does not work.

Edit2: This is the cursor object just before it is executed. Unfortunately i cannot debug in Webstorm, this is a screenshot from node inspector:

网友答案:

How about

Topic.find(query).populate('thoughts')
.sort({popularity : -1, date: -1})
.exec(function(err, docs) {
   // Multiple population per level
  if(err) return callback(err);
  Topic.populate(docs, {
    path: 'thoughts.author',
    model: 'User'
  },
  function(err, populatedDocs) {
    if(err) return callback(err);
    console.log(populatedDocs);
  });
});
网友答案:

Did you try using Model.populate?

Topic.find(query).populate('thoughts')
.sort({popularity : -1, date: -1})
.exec(function(err, docs) {
   // Multiple population per level
  if(err) return callback(err);
  Thought.populate(docs, {
    path: 'thoughts.author',
    model: 'User'
  },
  function(err, populatedDocs) {
    if(err) return callback(err);
    console.log(populatedDocs);
  });
});

UPDATE:
You can try with deep populate like this:

Topic.find(query).populate({
  path: 'thoughts',
  populate: {
    path: 'author',
    model: 'User'
  }
})
.sort({popularity : -1, date: -1})
.exec(function(err, docs) {
  if(err) return callback(err);
  console.log(docs);
});
网友答案:

These are the schemas :

var TopicSchema = new mongoose.Schema({
  title: { type: String, unique: true },
  category: String,
  thoughts: [ThoughtSchema]
}, {
  timestamps: true,
  toObject: {virtuals: true},
  toJSON: {virtuals: true}
});

var ThoughtSchema = new mongoose.Schema({
  text: String,
  author: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
  votes:[{
    _id:false,
    voter: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
    up: Boolean,
    date: {type: Date, default: Date.now}
  }]
}, {
  timestamps: true,
  toObject: {virtuals: true},
  toJSON: {virtuals: true}
});

Did you try Aggregation Instead of Populate. Aggregate Makes much easier for populating the embedded data using $lookup. Try the below code.

UPDATE

Topic.aggregate([{$unwind: "$thoughts"},{ $lookup: {from: 'users', localField: 'thoughts.author', foreignField: '_id', as: 'thoughts.author'}},{$sort:{{popularity : -1, date: -1}}}],function(err,topics){
 console.log(topics) // `topics` is a cursor.
 // Perform Other operations here.
})

Explanation:

$unwind: Deconstructs an array field from the input documents to output a document for each element.

$lookup: The $lookup stage does an equality match between a field from the input documents with a field from the documents of the “joined” collection. The lookup does the population's job.

$lookup works like

from : this says from which collection the data needs to be populated.(users in this scenario).

localField : this is the local field which needs to be populated. (thoughts.author in this scenario).

foreignField : this is the foreign field present in the collection from which data needs to be populated (_id field in users collection in this scenario).

as : this is the field as what you want to display the joined value as. (this will project thoughts.author's id as thoughts.author document).

Hope this works.

分享给朋友:
您可能感兴趣的文章:
随机阅读: