7

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:

enter image description here

2
  • @Theodore answer looks correct to me: what's the content of query? Do you have any select in it? Commented Jun 14, 2016 at 8:52
  • i have uploaded a screenshot of cursor object's content just before it is executed Commented Jun 15, 2016 at 18:27

3 Answers 3

1

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);
});
Sign up to request clarification or add additional context in comments.

5 Comments

'Thought' is not exported. I don't know also how to export multiple schemas... This is the only export line: export default mongoose.model('Topic', TopicSchema);
You don't need to export it as you are using it as an embedded schema. In that case I confirm @Theodore's answer should work. Did you check your database? Is the reference to author saved correctly?
yes.. the author field has the id of the User. But i somehow could not debug the server side code in webstorm to see how the query looks.. i will try to debug in the following days. But about the "Thought" schema.. when i use Thought.populate as you suggest, application cannot be compiled.. sth like "Thought is undefined" appears in terminal
I suggested another option you can try.
that's exactly what i have written in my question
0

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);
  });
});

Comments

0

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.

1 Comment

"Did you try Aggregation Instead of Populate" Sorry.. But aggregate is normally used with "match" instead of "find" as far as i know.. so it is not an alternative to populate but to 'find' i think .. so i dont know how to integrate this to my code.. does aggregate return also a cursor object, which must be executed afterwards? Could you please update my second code in the question and write all the lines?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.