4

Long time listener first time caller :-)

I've searched for a considerable amount of time and haven't quite found an answer to my problem here. I'm looking for a way, or would like to know the "proper" way, to only return a specific item within a nested mongoose Schema.

So lets say I have this example.

var mongoose = require('mongoose')
var Schema = mongoose.Schema

var conn = mongoose.connect('mongodb://localhost/testjs');

Bar = new Schema({
  text: String
});

Foo = new Schema({
  bar: [Bar]
});

var Foo = mongoose.model('Foo', Foo);

// Clean up the DB
Foo.find({}, function(err, res) {
    for (i in res) {
        res[i].remove()
    }
});

var foo = new Foo()
foo.bar.push({"text":"Hi"})
foo.bar.push({"text":"Bye"})
foo.bar.push({"text":"Hey"})

foo.save(
  function(err){

    var r = Foo
    .where('bar.text').equals('Hi')
    .select('bar.text')
    .exec(function(err, res) {
        console.log(res)
    })
  }
);

Result

[ { _id: 546c235cea0f16dc0d85a60f,
    bar: [ { text: 'Hi' }, { text: 'Bye' }, { text: 'Hey' } ] } ]

From the query I would've expected it to only return

[ { _id: 546c235cea0f16dc0d85a60f,
        bar: [ { text: 'Hi' } ] } ]

So I guess that leads me to a few questions:

  1. Is there a better way this query should be constructed?
  2. Is this typical behavior and it's up to be to loop over the results and just pull out what I need?
  3. For the original query, why would it return all fields rather than what I've specified in the 'where' statement?

1 Answer 1

2

Is this typical behavior

Yes this is how mongodb performs the projection operation.

.where("bar.text").equals("Hi"), This part of the query is called the find() part. It matches all the records, having the value of bar.text as Hi.

For the original query, why would it return all fields rather than what I've specified in the 'where' statement?

.select("bar.text"), This is the projection part. Projecting only those fields that we want.This works as expected for fields other than those inside arrays, because at a particular depth, only one unique field would be present. But in the case of arrays, there can be 'n' documents having the same field at the same depth. When an array element is projected, all the sub documents that have this particular field at that level will be shown. And that makes perfect sense.

and it's up to be to loop over the results and just pull out what I need?

No. There are better ways explained below.

Is there a better way this query should be constructed?

If you are sure that the array contains exactly one document matching the query condition you could make use of the $(positional projection) operator.

foo.save(
function(err){
var r = Foo.find({'bar.text':'Hi'},{'bar.$':1},function(err, res) {
    console.log(res)
});});

If you are unsure of how many documents are there in the array with the field value Hi, you could make use of the aggregation operator pipeline as below:

  foo.save(
  function(err){
  var r = Foo.aggregate([
  {$match:{"bar.text":"Hi"}},
  {$unwind:"$bar"},
  {$match:{"bar.text":"Hi"}},
  {$group:{"_id":"$_id","bars":{$push:"$bar"}}}
  ],function(err, res) {
        console.log(res)
    });});
Sign up to request clarification or add additional context in comments.

Comments

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.