0

I am setting up seed data with the goal of only creating a record if it does not already exist.

I am using the https://www.npmjs.com/package/mongoose-find-one-or-create plugin to only create records if they do not exist and it is not working.

I have also debugged into their code and written my own tests doing the following.

  • Create Record - Name = 'Abc'
  • Find Record where Name = 'Abc'
  • if found
  •     print found
  • if NOT found
  •     Create Record - Name = 'Xyz'

I was expecting to see the message 'Found', but instead I am getting a new record called 'Xyz'

I have a feeling it maybe some sort of asynchronous issue but I am new to mongo so not totally sure.

Here is all my test code, plus the code from the plugin.

Scheme Definition for Account Table

'use strict';

var mongoose = require('mongoose');
var findOneOrCreate = require('mongoose-find-one-or-create');

var Schema = mongoose.Schema;

var AccountSchema = new Schema({
  name: String,
  type: String,
  notes: String,
  locationIds: String,
});


AccountSchema.plugin(findOneOrCreate);

module.exports = mongoose.model('Account', AccountSchema);

Seed File for creating new rows

'use strict';

var Account = require('../../api/account/account.model');

Account.create({ name: 'Abc' }, function (err, small) {
});

Account.findOne({ name: 'Abc' }, function (err, row) {

  console.log("Inside Find One");
  if (row) {
    console.log("Found: Yes");
    console.log(row);
  } else {
    console.log("NOT FOUND");
    Account.create({ name: 'Xyz' }, function (err, small) {
    });
  }
});

The FindOneOrCreatePlugin code

'use strict';

/**
 * Mongoose Plugin: findOneOrCreate
 * Copyright(c) 2014 Mohammad Khan <mohammad.khan@gmx.us>
 * MIT Licensed
**/

function findOneOrCreatePlugin(schema) {
  //console.log('findOneOrCreatePlugin: SETUP');
    schema.statics.findOneOrCreate = function findOneOrCreate(condition, doc, callback) {
        var self = this;
        //console.log('CONDITION');
        //console.log('----------------------------------------------------------------------------------------------------');
        //console.log(condition);
        //console.log('----------------------------------------------------------------------------------------------------');

        self.findOne(condition, function(err, result ) {
          //console.log('ERROR');
          //console.log('----------------------------------------------------------------------------------------------------');
          //console.log(err);
          //console.log('----------------------------------------------------------------------------------------------------');
          //console.log('RESULT');
          //console.log('----------------------------------------------------------------------------------------------------');
          //console.log(result);
          //console.log('----------------------------------------------------------------------------------------------------');

          if (result) {
            //console.log('----------------------------------------------------------------------------------------------------');
            //console.log('YEEEEEEEEEEEEEEY an UPDATE');
            //console.log('----------------------------------------------------------------------------------------------------');

                callback(err, result);
          } else {
            //console.log('CREATE');
            //console.log('----------------------------------------------------------------------------------------------------');
            //console.log(doc);
            //console.log('----------------------------------------------------------------------------------------------------');

                self.create(doc, function(err, result) {
                    callback(err, result);
                });
            }
        });
    };
}

module.exports = findOneOrCreatePlugin;

Query in Mongo Shell

Mongo Shell Output

Console Output in Web Server

Console Output in Web Server

**Here is the use case that I'm attempting to deal with **

NOTE: this is part of a set of files generated by end users with a

The USE case I was attempting to resolve was if the user accidently put the same account in twice.

This code is essentially meant to be self-healing when users accidently put duplicates in, it is only a seed system for populating a new application.

I do plan to use this on the product table which has 1000's of products.

According to @RobertMoskal, I need to put the calls inside of call back functions but then I'll have huge nested callbacks, 1 for each product.

// Create account called 'Chicane'
Account.findOneOrCreate(
    { name: 'Chicane'},
    {
        name: 'Chicane',
        type: 'Orderer',
    },
    function (err, account) {
        console.log('Account.findOneOrCreate');
        console.log(account);
    }
);

// Create account called 'Campos'
Account.findOneOrCreate(
    { name: 'Campos'},
    {
        name: 'Campos',
        type: 'Supplier',
    },
    function (err, account) {
        console.log('Account.findOneOrCreate');
        console.log(account);
    }
);

// Create account called 'Chicane', this has already been created so it should NOT be created again, but it is
Account.findOneOrCreate(
    { name: 'Chicane'},
    {
        name: 'Chicane',
        type: 'Orderer',
    },
    function (err, account) {
        console.log('Account.findOneOrCreate');
        console.log(account);
    }
);
David Cruwys
  • 6,262
  • 12
  • 45
  • 91
  • possible duplicate of [How to return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) – Blakes Seven Sep 07 '15 at 23:29

1 Answers1

2

The classic node mistake. You have to call the findOne function inside the callback of the create function, otherwise it will be called before the create function is completed. So something like this:

Account.create({ name: 'Abc' }, function (err, small) {
    if(err)...;
    Account.findOne({ name: 'Abc' }, function (err, row) {...
    });
});

Naively loading thousands of items using that findOneOrCreate plugin would lead to callback hell. But you can see the repetitive nature of the process you can use something like the async library to ensure your code is run in order:

async.series([
    function(cb){ Account.findOneOrCreate({...})},
    function(cb){ { Account.findOneOrCreate({...}, cb)}
]);
Robert Moskal
  • 21,737
  • 8
  • 62
  • 86
  • If that is true, then the plugin I tried to use does not work in a node.js environment. Plus, the real goal is not to call findOne inside or Create, I only did that for testing purposes. The real goal is to findOne and if not found create a record, and then later in the seed file, account is accidently seeded, then that account should not be created. – David Cruwys Sep 07 '15 at 23:14
  • Thanks for the input Robert, just wondering about the practicality call of this code inside of callbacks as I want to use this with 1000's of seed records. I have created a use case (above) which shows the nature of the problem and how if I used call backs I would have massively nested calls. Maybe I'm missing something with this but I would have hoped that I could force a synchronise operation for what is essentially a batch job – David Cruwys Sep 07 '15 at 23:32
  • There are all sorts of fixtures packages for making loading mongo easier. Also you can wrap that findOneOrCreate function with another that takes an array of objects. – Robert Moskal Sep 07 '15 at 23:38