Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose: How to query for a referenced object property?

I'm trying to get a list of documents from my database based on the property a sub-document. The models and schemas I'm using are:

var elementSchema = new mongoose.Schema({
    name: String,
    description: String,

    _story: { type: mongoose.Schema.Types.ObjectId, ref: 'Story' },

    date_created: Date,
    date_modified: Date,
};
var storySchema = new mongoose.Schema({
    title: {type: String, default: '', trim: true},
    isPrivate: {type: Boolean, default: false},
});

mongoose.model("Story", storySchema);
mongoose.model("Element", elementSchema);

I'm trying to get all Elements that belong to a Story that is not private, and according to some posts I saw around here (1, 2, 3) the solution would be to use _story.isPrivate with find. I'm currently doing this:

Element.find({'_story.isPrivate': false})
         .populate('_story')
         .exec(function(err, elements){
             if(err){
                 return next(err);
             }
             else if(elements.length > 0){
                 return res.send(elements);
             }
             else{
                 return res.send(404, {message: "No elements found"});
             }
});

But the result is always an empty set (returns 404). With no condition, find returns all elements and populates _story correctly. I also activated the debug output to see the queries being executed and I get this:

Mongoose: elements.find({ '_story.isPrivate': false }) { fields: undefined, safe: undefined }

Trying to execute this in MongoDB I get no results. What can be wrong here?

Thanks

like image 213
JayC Avatar asked Nov 12 '13 18:11

JayC


2 Answers

You could re-order your model and have

var storySchema = new mongoose.Schema({
    title: {type: String, default: '', trim: true},
    isPrivate: {type: Boolean, default: false},
    elements: [{type: mongoose.Schema.Types.ObjectId, ref: 'Element'}]
});

Story.distinct('elements', {'isPrivate':false}, function(error, results) { 
  /* handle callback */ 
}

In this way you can issue a single call and get an aggragate of Elements.

like image 186
Mike M Avatar answered Nov 10 '22 20:11

Mike M


Listen to @JohnnyHK. He speaks the truth. Mongodb queries use the data in one and only one and exactly one collection at a time. Since documents in the 'elements' collection don't ever have a _story.isPrivate key path, Element.find({'_story.isPrivate': false}) will never match any document. There are no joins in mongodb. Really. However, given the "no joins" constraint, it is still possible to build an application and meet use cases, but you need alternative schemas and query designs. Sometimes people denormalize their data and duplicate stuff. Sometimes you run multiple related queries, etc, etc.

like image 21
Peter Lyons Avatar answered Nov 10 '22 19:11

Peter Lyons