0

I am new to MongoDB and working on small tutorial to create associations between tables in MongoDB.

As per the tutorial, we need to create association between three tables. 1. User table 2. BlogPost table 3. Comment table

User may have multiple blogpost and a blogpost may have list of comments and comment is also mapped with user.

User -> BlogPost -> Comment

I have written following test case to create three records and to test the association :

const assert = require('assert');
const User = require('../src/users');
const BlogPost = require('../src/blogPost');
const Comment = require('../src/comment');

describe('Association', () => {
    let ascUer, blogPost, comment;

    beforeEach( (done) => {
        ascUer = new User({name:'associationUser'});

        blogPost = new BlogPost({title:'JS is great',content:'Yep, It is !!'});

        comment = new Comment({content:'Congratulation for the great poost !!!'});

        ascUer.blogPosts.push(blogPost);
        blogPost.comments.push(comment);
        comment.user = ascUer;

        /*
        // THIS IS NOT WORKING 
        Promise.all([ascUer.save(),blogPost.save(),comment.save()])
            .then(() => done());
        */

        ascUer.save()
            .then(() => {
                console.log('***********Association user saved ***************');
                blogPost.save()
                    .then(() => {
                        console.log('***********Blogpost  saved ***************');
                        comment.save()
                            .then(() => {
                                console.log('***********Comment  saved ***************');
                                done();
                            });
                    });
            });
    });

    it.only('Saves a relation between a user and a blogpost', (done) => {
        User.findOne({name:'associationUser'})
            .populate('blogPosts')
            .then((user) => {
                console.log(user);
                done();
            });

    });
});

I am facing weird behavior while running the test case with help of mocha and nodeJs. The test case is getting executed successfully but only "user" table was created while using "Promise.All" feature of ES6. I have commented out "Promise.All" in above code snippet and one by one saving each of three records.

Below image show result of test case execution: enter image description here

Below image shows snap of RoboMongo tool where only "user" table is present : enter image description here

Updated : I also have created one more test case file "connection_helper.js" where I have written "beforeEach" block to make a DB connection and to drop all the schema before execution of any test cases.

Below image shows the project directory structure : enter image description here

And below is the code written in "connection_helper" js file :

const mongoose = require('mongoose');
let isSchemaDropped = false;

mongoose.Promise = global.Promise;

before((done) => {
    mongoose.connect('mongodb://localhost:27017/users_test');
    mongoose.connection
        .once('open', () => {
            console.log('Connected to Mongose DB !!');
            done();
        })
        .on('error', (error) => {
            console.warn('Error while connecting to Mongose DB !!',error);
        });

});

beforeEach((done) => {

        if(!isSchemaDropped){

            isSchemaDropped = true;
            console.log("Dropping database schema !!!");

            try{
                const {users,comments,blogposts,employees} = mongoose.connection.collections;

                users.drop(() => {
                    comments.drop(() => {
                        blogposts.drop(() => {
                            employees.drop(() => {
                                console.log("**********************************************");
                                console.log("**********************************************");
                                console.log("******Dropped All Schema******");
                                console.log("**********************************************");
                                console.log("**********************************************");

                            });
                        });
                    });
                });


            }catch(e){
                console.log(e);
            }

        }else{
            //console.log("Database schema is already dropped before !!!");
        }

        done();
});

Why ES6 "Promise.All" is not working properly in my system ? Any suggestions appreciated. You may also refer my code over GitHub : https://github.com/shahgunjan07/MongoDBTutorial.git for further details.

Gunjan Shah
  • 5,088
  • 16
  • 53
  • 72
  • I don't actually see why `Promise.all()` would fail. I've used the exact schema you posted and for me, `Promise.all()` works just fine, creating three collections as expected. Where is _"Dropped All Schema"_ coming from? In your log, the `User` document has an empty `blogPosts` array, but in your Robomongo screenshot, it has an associated ObjectId. – robertklep May 20 '17 at 13:55
  • I have written test_helper js file where I am dropping all the table in "beforeEach" block. where I have added log like "Dropped all Schema". The purpose of this test_helper is to make DB connection and drop all existing tables before execution of any test cases. – Gunjan Shah May 23 '17 at 02:44

1 Answers1

1

I think your issue is related to your beforeEach, which isn't properly waiting for all collections to be dropped before continuing with the tests. This could result in collections being dropped while your tests are running, causing unexpected behaviour.

Here's an alternative implementation:

beforeEach(() => {
  if (isSchemaDropped) return;
  isSchemaDropped = true;
  console.log("Dropping database schema !!!");

  const {
    users,
    comments,
    blogposts,
    employees
  } = mongoose.connection.collections;

  return Promise.all([
    users.drop(),
    comments.drop(),
    blogposts.drop(),
    employees.drop()
  ]);
});
robertklep
  • 198,204
  • 35
  • 394
  • 381
  • Suggestion which you have mentioned is working fine. But Is it possible to provide "done()" in beforeEach in this example ? – Gunjan Shah May 24 '17 at 03:38
  • As per the training program, the code which I have mentioned in the question should work fine. But it seems that I am facing so many issues due to inconsistent behavior of "beforeEach". Is this function really reliable ? Dont we have any good solution in nodeJS for synchronous execution ? – Gunjan Shah May 24 '17 at 03:40
  • @GunjanShah you don't need `done` if you return a promise. Mocha has built-in promises support, which works better when testing (and working with) promises. Also, I don't know which training program you mean, but the code in your question is _not_ fine. `beforeEach` is perfectly reliable, I think your problems stem from not fully grasping the intricacies of asynchronous programming (which is a fundamental part of Node.js). Newer versions of Node (v7 and up) provide concepts like [`async/await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)... – robertklep May 24 '17 at 05:15
  • @GunjanShah ...that will make working with asynchronous code easier. However, it's still very much a topic that is inherent to Node.js and JavaScript in general. – robertklep May 24 '17 at 05:16