Search code examples
node.jsmongodbmongoose

Second mongoose populate causes an empty json result


I am trying to get a proper response from three collections (I have simplified them and I can't change my collections). Without second then() with second populate() I get the proper result, however I need to do this for getting information from my third collection. What am I doing wrong - where is my fallacy? How to fix it?

api.js

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

mongoose.connect('mongodb://localhost/books');
var db = mongoose.connection;
db.on('connected', function() {
    console.log('MongoDB connection successful');
});

Author = require('./models/books');
Book = require('./models/books');
Genre = require('./models/books');

app.post('/api/Books', function (req, res) { 
    var param = req.body.name;

    Author.getAuthor({ name: param }, 10) 
        .then(populate_author => { 
            return Book.getBook({ id_a: populate_author.id_a }); 
        })
        .then(populate_books => {  //this then() with populate() causes empty result
            return Genre.getGenre({ id_g: populate_books.id_g }); 
        })
        .then(results => {
            console.log(results); 
            res.json(results.map(result => ({
                id_a : result.populate_author.id_a,
                name : result.populate_author.name,
                gender : result.populate_author.gender,
                title : result.title,
                pub_date : result.pub_date,
                id_g: result.populate_books.id_g,
                genre : result.populate_books.genre,
                description : result.populate_books.description
            })));
        })
        .catch(error => { 
            console.log(error);
        }); 
});

app.listen(3000);
console.log('Server started and waits on port 3000');

books.js

var mongoose = require('mongoose');

var authorSchema = mongoose.Schema({
    id_a:{
        type: Number,
        ref: 'books',
        required: true
    },
    name:{
        type: String,
        required: true
    },
    gender:{
        type: String,
        required: true
    },
    born:{
        type: Number,
        required: true
    },
    birthplace:{
        type: String,
        required: true
    }},
    { collection: 'author'}
);

var booksSchema = mongoose.Schema({
    id_b:{
        type: Number,
        required: true
    },
    title:{
        type: String,
        required: true
    },
    id_a:{
        type: Number,
        required: true

    },
    pub_date:{
        type: Number,
        required: true
    },
    publisher:{
        type: String,
        required: true
    },
    pages:{
        type: Number,
        required: true
    },
    id_g:{
        type: Number,
        ref: 'genres',
        required: true
    }},
    { collection: 'books', toJSON: { virtuals: true }}
);

var genresSchema = mongoose.Schema({
    id_g:{
        type: Number,
        required: true
    },
    genre:{
        type: String,
        required: true
    },
    description:{
        type: String,
        required: true
    }},
    { collection: 'genres', toJSON: { virtuals: true }}
);

booksSchema.virtual('populate_author', {
    ref: 'author',
    localField: 'id_a',
    foreignField: 'id_a',
    justOne: true
});

genresSchema.virtual('populate_books', {
    ref: 'books',
    localField: 'id_g',
    foreignField: 'id_g',
    justOne: true
});

var Author = module.exports = mongoose.model('author', authorSchema);
var Book = module.exports = mongoose.model('books', booksSchema);
var Genre = module.exports = mongoose.model('genres', booksSchema);

module.exports.getAuthor = function (query) { 
    return Author.findOne(query)
    .exec(); 
} 

module.exports.getBook = function (query) { 
    return Book.find(query) 
    .populate('populate_author') 
    .exec(); 
}

module.exports.getGenre = function (query) { 
    return Genre.find(query) 
    .populate('populate_books')
    .exec(); 
}

Extract from my collections:

author

{"_id" : ObjectId("59492addd80eb0f9c1b42fd9"),"id_a" : 1,"name" : "Agatha Christie","gender" : "female","born" : 1890,"birthplace" : "England"}

books

{"_id" : ObjectId("59492cd1d80eb0f9c1b42fda"),"id_b" : 1,"title" : "Murder on the Orient Express","id_a" : 1,"pub_date" : 1934,"publisher" : "Collins Crime Club","pages" : 256,"id_g" : 100}

genres

{"_id" : ObjectId("59553065062b59078d808c5e"),"id_g" : 100,"genre" : "crime","description" : "Crime fiction is the literary genre that..."}

My json-result:

[]

Solution

  • So, I have solved my question after reading this article. It helped me to understand how populate and virtuals work. Also I renamed some functions and rewrote my code after I understood in which direction I have to populate. Before this article I didn't understand how it actually works.