Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB, Mongoose: How to find subdocument in found document?

I'm stuck on trying to get subdocument by _id in found document.

Example Schema

var User = mongoose.Schema({
        name:       String,
        photos:    [{src: String, title: String}]
    });
var Team = db.model('Team', Team);

Now I'm getting one user:

myUser = User.findOne(...)...

How can I get now src of his photo by it's _id (or title)?

Something like:

myUser.photos.findOne({'_id': myId})
like image 531
gial Avatar asked Jan 15 '14 16:01

gial


People also ask

What is Mongoose subdocument?

In Mongoose, subdocuments are documents that are nested in other documents. You can spot a subdocument when a schema is nested in another schema. Note: MongoDB calls subdocuments embedded documents.

What does find by id return Mongoose?

findById returns the document where the _id field matches the specified id . If the document is not found, the function returns null .

What is __ V in MongoDB?

The __v field is called the version key. It describes the internal revision of a document. This __v field is used to track the revisions of a document. By default, its value is zero ( __v:0 ).

What is a sub document?

subdocument (plural subdocuments) (computing, wordprocessing) A document making up part of a larger document.


2 Answers

You need to either create a NEW Schema for your embedded documents, or leave the type declaration as a blank array so mongoose interprets as a Mixed type.

var userSchema = new mongoose.Schema({
  name: String,
  photos: []
});
var User = mongoose.model('User', userSchema);

-- OR --

var userSchema = new mongoose.Schema({
  name: String,
  photos: [photoSchema]
});

var photoSchema = new mongoose.Schema({
  src: String,
  title: String
});

var User = mongoose.model('User', userSchema);

And then you can save thusly:

var user = new User({
  name: 'Bob',
  photos: [ { src: '/path/to/photo.png' }, { src: '/path/to/other/photo.png' } ]
});

user.save();

From here, you can simply use array primitives to find your embedded docs:

User.findOne({name: 'Bob'}, function (err, user) {

  var photo = user.photos.filter(function (photo) {
    return photo.title === 'My awesome photo';
  }).pop();

  console.log(photo); //logs { src: '/path/to/photo.png', title: 'My awesome photo' }
});

-- OR --

You can use the special id() method in embedded docs to look up by id:

User.findOne({name: 'Bob'}, function (err, user) {
    user.photos.id(photo._id);
});

You can read more here: http://mongoosejs.com/docs/subdocs.html

Make sure you DON'T register the schema with mongoose, otherwise it will create a new collection. Also keep in mind that if the child documents are searched for often, it would be a good idea to use refs and population like below. Even though it hits the DB twice, its much faster because of indexing. Also, mongoose will bonk on double nesting docs (i.e. The children have children docs as well)

var user = mongoose.Schema({
  name: String,
  photos: [{ type: Schema.Types.ObjectId, ref: 'Photo' }]
});

var photo = mongoose.Schema({
  src: String,
  title: String
});

User
  .findOne({ name: 'foo' })
  .populate('photos')
  .exec(function (err, user) {
    console.log(user.photos[0].src);
  });

Relevant docs can be found here http://mongoosejs.com/docs/populate.html

like image 181
srquinn Avatar answered Oct 26 '22 11:10

srquinn


Adding to srquinn's answer, from my limited experience I thought populate was for joining documents from different collections together?

I think here you could just do User.findOne({ name: 'foo' }, 'photos') which is shorthand for:

const query = User.findOne({ name: 'foo' })
query.select('photos')
like image 36
Dominic Avatar answered Oct 26 '22 11:10

Dominic