Search code examples
node.jsmongodbmongoosemongoose-populate

Populate from two collections


I have 2 Schemas defined for 2 different collections, and I need to populate one of them into the other:

stationmodel.js

var stationSchema = new Schema({ 
    StationName: 'string', 
    _id: 'number', 
    Tripcount: [{ type: Schema.Types.ObjectId, ref: 'Tripcount'}]
},
    {collection: 'stations'}
);
module.exports = mongoose.model('Station', stationSchema);

tripmodel.js

var tripSchema = new Schema({
    _id: { type: Number, ref: 'Station'},
    Tripcount: 'number'
},
    {collection: 'trips'}
);
module.exports = mongoose.model('Tripcount', tripSchema);

According to the mongoose populate documentation, this is the way to go. I have the problem that "Tripcount" remains as [] when I use Postman to GET the stations.

My DB Structure for the 'stations' collection:

{
    "_id": 1,
    "StationName": "Station A",
}

And for the 'trips' collection:

{
    "_id": 1,
    "Tripcount": 6
}

My routes.js:

module.exports = function(app) {

    app.get('/stations', function(req,res) {
        var query = Station.find().populate('Tripcount');
        query.exec(function(err, stations){
            if(err)
                res.send(err);
            res.json(stations);
        });
    });

};

I can't seem to find the error, maybe someone here can spot a mistake I made.


Solution

  • You are enclosing the mongoose SchemaTypes in single quotes, you either need to reference the SchemaTypes directly when you define a property in your documents which will be cast to its associated SchemaType.

    For example, when you define the Tripcount in the tripSchema it should be cast to the Number SchemaType as

    var tripSchema = new Schema({
        _id: Number,
        Tripcount: Number
    }, {collection: 'trips'});
    
    module.exports = mongoose.model('Tripcount', tripSchema);
    

    and the station schema

    var stationSchema = new Schema({ 
        _id: Number, 
        StationName: String, 
        Tripcount: [{ type: Number, ref: 'Tripcount'}]
    }, {collection: 'stations'});
    
    module.exports = mongoose.model('Station', stationSchema);
    

    Then in your stations collection, the documents would ideally have the structure

    {
        "_id": 1,
        "StationName": "Station A",
        "Tripcount": [1]
    }
    

    for the populate method to work, of which when applied as

    Station.find().populate('Tripcount').exec(function(err, docs){
        if (err) throw err;
        console.log(docs);  
        // prints { "_id": 1, "StationName": "Station A",   "Tripcount": [{"_id": 1, Tripcount: 6 }] }
    });
    

    Alternative Approach

    Another approach that you could take if the station collection does not have Tripcount field is to use the $lookup operator found in the aggregation framework as:

    Station.aggregate([
        { 
            "$lookup": {
                "from": "tripcollection",
                "localField": "_id",
                "foreignField": "_id",
                "as": "trips"
            }
        },
        {
            "$project": {
                "StationName": 1,
                "trips": { "$arrayElemAt": ["$trips", 0] }
            }
        },
        {
            "$project": {
                "StationName": 1,
                "Tripcount": "$trips.Tripcount"
            }
        }
    ]).exec(function(err, docs){
        if (err) throw err;
        console.log(docs);  
        // prints [{ "_id": 1, "StationName": "Station A",  "Tripcount": 6 }] }
    });