Trax
Trax

Reputation: 1538

Mongoose Populate Returns Some Empty Objects

I have 1 main collection and 1 collection with a ref to the main one. Code looks like :

// Ref schema
    const onlineSchema = mongoose.Schema({
        _id: {
            type: Number,
            ref: 'Player',
            unique: true
        }
    }, {
        timestamps: true
    });

//main schema

const playerSchema = mongoose.Schema({

_id: { // User ID
    type: Number,
    required: true,
    unique: true,
    default: 0
},
firstname: {
    type: String
},
name: {
    type: String,
    required: true
},
lastname: {
    type: String
},
barfoo: {
   type: Boolean
}
...

})

I populate it with this code :

var baz = bar;
...
        Online.find().populate({
            path: '_id',
            match: {
                [ baz + 'foo']: true
            }
        }).exec(function(err, online) {
            if (err) {
                winston.error(err);
            } else {
                winston.error(util.inspect(online, {
                    showHidden: false,
                    depth: null
                }));

            }
        });

If there are 10 elements in online and only 7 match [ baz + 'foo']: true I get 7 proper arrays and 3 empty arrays that look like this:

    { updatedAt: 2016-12-23T18:00:32.725Z,
createdAt: 2016-12-23T18:00:32.725Z,
_id: null,
 __v: 0 },

Why is this happening and how to I filter the final result so it only shows the matching elements?

I can use filter to remove the null arrays after I get the result but I'd like to know how to prevent the the query from passing null arrays in the first place.

Upvotes: 1

Views: 1305

Answers (3)

hossein derakhshan
hossein derakhshan

Reputation: 771

dont use _id to ref field.. because its default filed in mongoDB to create index unique.. change you're field name

Upvotes: 0

Bertrand Martel
Bertrand Martel

Reputation: 45352

Why is this happening ?

This is happening because you get all the documents with Online.find() but the player will be populated only for records that match your condition. Your match is for the populate, not for the find() query.

How do I filter the final result so it only shows the matching elements ?

You cant find a nested elements of a referenced collections since there is no join in MongoDB. But you can :

  • keep your schema and use aggregation with $lookup :

    Online.aggregate(
        [{
            $lookup: {
                from: "players",
                localField: "_id",
                foreignField: "_id",
                as: "players"
            }
        }, {
            $unwind: "$players"
        }, {
            $match: {
                'players.barfoo': true
            }
        }],
        function(err, result) {
            console.log(result);
        });
    
  • change your schema to include Player as a subdocument :

    const playerSchema = new mongoose.Schema({
        //...
    });
    
    const onlineSchema = new mongoose.Schema({
        player: playerSchema
    }, {
        timestamps: true
    });
    
    var Online = mongoose.model('Online', onlineSchema);
    
    Online.find({'player.barfoo':true}).exec(function(err, online) {
        console.log(online);
    });
    

Upvotes: 2

Ravi Shankar Bharti
Ravi Shankar Bharti

Reputation: 9268

Dont make _id the reference of another schema, instead make another field name player and give reference through that.

const onlineSchema = mongoose.Schema({
    player: {
        type: Number,
        ref: 'Player',
        unique: true
    }
}, {
    timestamps: true
});

Population:

Online.find().populate({
    path: 'player',
    match: {
        [ baz + 'foo']: true
    }
}).exec(...);

Upvotes: 0

Related Questions