Fred Lackey
Fred Lackey

Reputation: 2391

Mongoose / MongoDB Index Exception

Full project location:

http://github.com/FredLackey/...


Has anyone seen the following exception? Know how to get by it? My models DO have indexes, so there's no reason, that I can think of, for the indexes not to be present.

The MongoDB client code is throwing an exception within Mongoose...

C:\_\GuestbookLite\Projects\GuestbookLiteCatalog>npm start

> [email protected] start C:\_\GuestbookLite\Projects\GuestbookLiteCatalog
> nodemon ./scripts/www.js

3 Apr 17:20:45 - [nodemon] v1.3.7
3 Apr 17:20:45 - [nodemon] to restart at any time, enter `rs`
3 Apr 17:20:45 - [nodemon] watching: *.*
3 Apr 17:20:45 - [nodemon] starting `node ./scripts/www.js`

##############################################################
#
#   !!! MONGOOSE WARNING !!!
#
#   This is an UNSTABLE release of Mongoose.
#   Unstable releases are available for preview/testing only.
#   DO NOT run this in production.
#
##############################################################

GuestbookLiteCatalog db opened
GuestbookLiteCatalog db seeded

C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\connection\base.js:246
        throw message;
              ^
TypeError: Cannot read property 'length' of undefined
    at processResults (C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\db.js:1581:31)
    at C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\db.js:1619:20
    at C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\db.js:1157:7
    at C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\db.js:1890:9
    at Server.Base._callHandler (C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\connection\base.js:448:41)
    at C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\connection\server.js:481:18
    at MongoReply.parseBody (C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\responses\mongo_reply.js:68:5)
    at null.<anonymous> (C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\connection\server.js:439:20)
    at emit (events.js:95:17)
    at null.<anonymous> (C:\_\GuestbookLite\Projects\GuestbookLiteCatalog\node_modules\mongoose\node_modules\mongodb\lib\mongodb\connection\connection_pool.js:201:13)
3 Apr 17:20:45 - [nodemon] app crashed - waiting for file changes before starting...

The block of code causing the problem is the iterator in the for loop below...

/**
 * Retrieves this collections index info.
 *
 * Options
 *  - **full** {Boolean, default:false}, returns the full raw index information.
 *  - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST).
 *
 * @param {String} collectionName the name of the collection.
 * @param {Object} [options] additional options during update.
 * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occurred, or null otherwise. While the second parameter will contain the results from indexInformation or null if an error occurred.
 * @return {null}
 * @api public
 */
Db.prototype.indexInformation = function(name, options, callback) {
  if(typeof callback === 'undefined') {
    if(typeof options === 'undefined') {
      callback = name;
      name = null;
    } else {
      callback = options;
    }
    options = {};
  } 

  // Throw is no name provided
  if(name == null) throw new Error("A collection name must be provided as first argument");

  // If we specified full information
  var full = options['full'] == null ? false : options['full'];
  var self = this;

  // Process all the results from the index command and collection
  var processResults = function(indexes) {
    // Contains all the information
    var info = {};
    // Process all the indexes
      if (indexes) {
          for(var i = 0; i < indexes.length; i++) {
              var index = indexes[i];
              // Let's unpack the object
              info[index.name] = [];
              for(var name in index.key) {
                  info[index.name].push([name, index.key[name]]);
              }
          }
      }

    return info;
  }

  // Fallback to pre 2.8 getting the index information
  var fallbackListIndexes = function() {
    // Build selector for the indexes
    var selector = name != null ? {ns: (self.databaseName + "." + name)} : {};

    // Get read preference if we set one
    var readPreference = ReadPreference.PRIMARY;

    // Iterate through all the fields of the index
    var collection = self.collection(DbCommand.SYSTEM_INDEX_COLLECTION);
    // Perform the find for the collection
    collection.find(selector).setReadPreference(readPreference).toArray(function(err, indexes) {
      if(err != null) return callback(err, null);
      // if full defined just return all the indexes directly
      if(full) return callback(null, indexes);
      // Return all the indexes
      callback(null, processResults(indexes));
    });
  }

  // Attempt to execute the listIndexes command
  self.command({listIndexes: name}, function(err, result) {
    if(err) return fallbackListIndexes();
    // if full defined just return all the indexes directly
    if(full) return callback(null, result.indexes);
    // Return all the indexes
    callback(null, processResults(result.indexes));
  });
};

The exception is thrown as soon as the app is initialized once the first model is loaded. Here's one of them...

/*jslint node: true, nomen: true, es5: true */
/**
* Generated by Fred Lackey <[email protected]> on 4/3/2015 5:03:44 PM
* Copyright 2015 Fred Lackey
* Direct questions to the author: Fred Lackey <[email protected]>
*/

'use strict';

var arrays = require('dzutils').arrays,
    async = require('async'),
    booleans = require('dzutils').booleans,
    dates = require('dzutils').dates,
    moment = require('moment'),
    mongoose = require('mongoose'),
    numbers = require('dzutils').numbers,
    strings = require('dzutils').strings,
    uids = require('dzutils').uuids,
    util = require('util');

var eventSchema = new mongoose.Schema({
    _id: { type: String, trim: true, uppercase: true, default: uids.newIdentifier, validate: [uids.isIdentifier, '_id (id) is not a valid identifier'], required: true },
    nm: { type: String, trim: true, validate: [strings.isValidString, 'nm (name) is not a valid string'], required: true },
    clientName: { type: String, trim: true, validate: [strings.isValidString, 'clientName is not a valid string'], required: true },
    _v: { type: Date, validate: [dates.isDate, '_v (auditVersionDate) is not a valid date'], required: true },
    _m: { type: String, trim: true, uppercase: true, validate: [uids.isIdentifier, '_m (auditMemberUid) is not a valid identifier'], required: true },
    _k: { type: Date, default: null, validate: [dates.isDateOrNull, '_k (auditDeletedDate) is not a valid date or null value'], required: false }
});

eventSchema.index({
    nm: 1,
    clientName: 1,
    _k: 1
}, { unique: true });

var validateItem = function (item) {
    if (!item) { return 'No item to validate'; }
    if (typeof item._id !== 'undefined') { if (!uids.isIdentifier(item._id)) { return '_id (id) is not a valid String'; } else { item._id = strings.trimToNull(item._id); } }
    if (typeof item.nm !== 'undefined') { if (!strings.isValidString(item.nm)) { return 'nm (name) is not a valid String'; } else { item.nm = strings.trimToNull(item.nm); } }
    if (typeof item.clientName !== 'undefined') { if (!strings.isValidString(item.clientName)) { return 'clientName is not a valid String'; } else { item.clientName = strings.trimToNull(item.clientName); } }
    if (typeof item._v !== 'undefined') { if (!dates.isDate(item._v)) { return '_v (auditVersionDate) is not a valid Date'; } }
    if (typeof item._m !== 'undefined') { if (!uids.isIdentifier(item._m)) { return '_m (auditMemberUid) is not a valid String'; } else { item._m = strings.trimToNull(item._m); } }
    if (typeof item._k !== 'undefined') { if (!dates.isDateOrNull(item._k)) { return '_k (auditDeletedDate) is not a valid Date or null value'; } }
    return null;
};
eventSchema.methods.validateItem = validateItem;

var validateItems = function (items) {
    if (arrays.count(items) < 1) { return null; }
    var i, err;
    for (i = 0; i < items.length; i += 1) {
        err = validateItem(items(i));
        if (err){ return 'Item ' + i + ' error: ' + err; }
    }
    return null;
};
eventSchema.methods.validateItems = validateItems;

var toDto = function (item) {
    if (!item) { return null; }
    var dto = {};
    if (typeof item._id !== 'undefined') { dto.id = item._id; }
    if (typeof item.nm !== 'undefined') { dto.nm = item.nm; }
    if (typeof item.clientName !== 'undefined') { dto.clientName = item.clientName; }
    if (typeof item._v !== 'undefined') { dto.v = item._v; }
    if (typeof item._m !== 'undefined') { dto.m = item._m; }
    if (typeof item._k !== 'undefined') { dto.k = item._k; }
    return dto;
};
eventSchema.methods.toDto = toDto;

var toDtos = function (items) {
    if (arrays.count(items) < 1) { return null; }
    var i, dtos = [];
    for (i = 0; i < items.length; i += 1) {
        dtos.push(toDto(items[i]));
    }
    return dtos;
};
eventSchema.methods.toDto = toDto;

var toDtoFull = function (item) {
    if (!item) { return null; }
    var dto = {
        id: ((typeof item._id !== 'undefined') ? item._id : null),
        nm: ((typeof item.nm !== 'undefined') ? item.nm : null),
        clientName: ((typeof item.clientName !== 'undefined') ? item.clientName : null),
        v: ((typeof item._v !== 'undefined') ? item._v : null),
        m: ((typeof item._m !== 'undefined') ? item._m : null),
        k: ((typeof item._k !== 'undefined') ? item._k : null)
    };
    return dto;
};
eventSchema.methods.toDtoFull = toDtoFull;

var toDtosFull = function (items) {
    if (arrays.count(items) < 1) { return null; }
    var i, dtos = [];
    for (i = 0; i < items.length; i += 1) {
        dtos.push(toDtoFull(items[i]));
    }
    return dtos;
};
eventSchema.methods.toDtosFull = toDtosFull;

var toItem = function (dto) {
    if (!dto) { return null; }
    var item = {};
    if (typeof dto.id !== 'undefined') { item._id = dto.id; }
    if (typeof dto.nm !== 'undefined') { item.nm = dto.nm; }
    if (typeof dto.clientName !== 'undefined') { item.clientName = dto.clientName; }
    if (typeof dto.v !== 'undefined') { item._v = dto.v; }
    if (typeof dto.m !== 'undefined') { item._m = dto.m; }
    if (typeof dto.k !== 'undefined') { item._k = dto.k; }
    return item;
};
eventSchema.methods.toItem = toItem;

var toItems = function toItems(dtos) {
    if (arrays.count(dtos) < 1) { return null; }
    var i, items = [];
    for (i = 0; i < dtos.length; i += 1) {
        items.push(toItem(dtos[i]));
    }
    return items;
};
eventSchema.methods.toItems = toItems;

var toItemFull = function toItemFull(dto) {
    if (!dto) { return null; }
    var item = {
        _id: ((typeof dto.id !== 'undefined') ? dto.id : null),
        nm: ((typeof dto.nm !== 'undefined') ? dto.nm : null),
        clientName: ((typeof dto.clientName !== 'undefined') ? dto.clientName : null),
        _v: ((typeof dto.v !== 'undefined') ? dto.v : null),
        _m: ((typeof dto.m !== 'undefined') ? dto.m : null),
        _k: ((typeof dto.k !== 'undefined') ? dto.k : null)
    };
    return item;
};
eventSchema.methods.toItemFull = toItemFull;

var toItemsFull = function (dtos) {
    if (arrays.count(dtos) < 1) { return null; }
    var i, items = [];
    for (i = 0; i < dtos.length; i += 1) {
        items.push(toItemFull(dtos[i]));
    }
    return items;
};
eventSchema.methods.toItemsFull = toItemsFull;

var filterUnique = function (array) {
    if (arrays.count(array) < 1) { return null; }
    var a, r, found, result = [];
    for (a = 0; a < array.length; a += 1) {
        if (typeof array[a].id !== 'undefined') {
            found = false;
            for (r = 0; r < result.length; r += 1) {
                if (result[r].id === array[a].id) {
                    found = true;
                    break;
                }
            }
            if (found === false) { result.push(array[a]); }
        }
    }
    return result;
};
eventSchema.methods.filterUnique = filterUnique;

var getAll = function (callback) {
    var query = { _k: null };
    mongoose.model('Event').find(query, function (err, items) {
        if (err) { return callback(err); }
        return callback(null, toDtos(items));
    });
};
eventSchema.methods.getAll = getAll;

var getById = function (id, callback) {
    var query = { _id: id, _k: null };
    var err = validateItem(query);
    if (err) { return callback(new Error(err)); }
    mongoose.model('Event').findOne(query, function (err, item) {
        if (err) { return callback(err); }
        if (item) { return callback(null, toDto(item)); }
        return callback();
    });
};
eventSchema.methods.getById = getById;

var getByIds = function (ids, callback) {
    if (!util.isArray(ids)){ return callback(new Error('ids is not an array'));  }
    var query = { _id: { $in: ids }, _k: null };
    mongoose.model('Event').find(query, function (err, items) {
        if (err) { return callback(err); }
        if (items) { return callback(null, toDtos(items)); }
        return callback();
    });
};
eventSchema.methods.getByIds = getByIds;

var getOneByNameClientName = function (name, clientName, callback) {
    var query = {
        nm: name,
        clientName: clientName,
        _k: null
    };
    var err = validateItem(query);
    if (err) { return callback(new Error(err)); }
    mongoose.model('Event').findOne(query, function (err, item) {
        if (err) { return callback(err); }
        if (item) { return callback(null, toDto(item)); }
        return callback();
    });
};
eventSchema.methods.getOneByNameClientName = getOneByNameClientName;

var getOneByNameClientNameMany = function (queries, callback) {
    if (!queries || !util.isArray(queries)) { return callback(new Error('Invalid query array')); }
    if (queries.length < 1) { return callback(); }
    var result = [];
    async.forEach(queries, function (query, next){
        getOneByNameClientName(query.name, query.clientName, function (err, item) {
            if (err) {
                return callback(new Error('Error on #' + result.length + ': ' + err.message));
            } else {
                result.push(item);
            }
            next();
        });
    }, function (err) {
        if (err) {
            return callback(err);
        } else {
            return callback(null, filterUnique(result));
        }
    });
};
eventSchema.methods.getOneByNameClientNameMany = getOneByNameClientNameMany;

var createNew = function (name, clientName, auditMemberUid, callback) {
    var data = {
        nm: name,
        clientName: clientName,
        _v: new Date(),
        _m: auditMemberUid,
        _k: null
    };
    var err = validateItem(data);
    if (err) { return callback(new Error(err)); }
    var newItem = new Event(data);
    newItem.save(function (err, item) {
        if (err) { return callback(err); }
        return callback(null, toDto(item));
    });
};
eventSchema.methods.createNew = createNew;

var createNewMany = function (items, callback) {
    if (!items || !util.isArray(items)) { return callback(new Error('Invalid item array')); }
    if (items.length < 1) { return callback(); }
    var ids = [];
    async.forEach(items, function (item, next){
        createNew(item.name, item.clientName, item.auditMemberUid, function (err, id) {
            if (err) {
                return callback(new Error('Error on #' + ids.length + ': ' + err.message));
            } else {
                ids.push(id);
            }
            next();
        });
    }, function (err) {
        if (err) {
            return callback(err);
        } else {
            return callback(null, ids);
        }
    });
};
eventSchema.methods.createNewMany = createNewMany;

var createNewOrGet = function (name, clientName, auditMemberUid, suppressError, callback) {
    var dupErr = suppressError ? null : (new Error('Duplicate Event detected'));
    getOneByNameClientName(name, clientName, function (err, existing) {
        if (err) { return callback(err); }
        if (existing) { return callback(dupErr, existing); }
        return createNew(name, clientName, auditMemberUid, callback);
    });
};
eventSchema.methods.createNewOrGet = createNewOrGet;

var getCount = function (includeDeleted, callback) {
    var query = {};
    if (includeDeleted !== true) { query._k = null; }
    return mongoose.model('Event').count(query, callback);
};
eventSchema.methods.getCount = getCount;

var getCountByQuery = function (queryDto, callback) {
    var query = toItem(queryDto);
    var err = validateItem(query);
    if (err) { return callback(new Error('Invalid query: ' + err)); }
    mongoose.model('Event').count(query, callback);
};
eventSchema.methods.getCountByQuery = getCountByQuery;

var modifyMany = function (queryDto, updateDto, auditMemberUid, callback) {
    if (typeof queryDto === 'undefined') { return callback(new Error('No query supplied')); }
    if (typeof updateDto === 'undefined') { return callback(new Error('No Update Supplied')); }
    if (typeof auditMemberUid === 'undefined') { return callback(new Error('No auditMemberUid supplied')); }
    queryDto.auditDeletedDate = null;
    updateDto.auditVersionDate = moment().utc().toDate();
    updateDto.auditMemberUid = auditMemberUid;
    var query = toItem(queryDto),
        update = toItem(updateDto);
    var err = validateItem(query);
    if (err) { return callback(new Error('Invalid query: ' + err)); }
    err = validateItem(update);
    if (err) { return callback(new Error('Invalid update: ' + err)); }
    return mongoose.model('Event').update(query, update, callback);
};
eventSchema.methods.modifyMany = modifyMany;

var modifyOne = function (queryDto, updateDto, auditMemberUid, callback) {
    if (typeof queryDto === 'undefined') { return callback(new Error('No query supplied')); }
    if (typeof updateDto === 'undefined') { return callback(new Error('No Update Supplied')); }
    if (typeof auditMemberUid === 'undefined') { return callback(new Error('No auditMemberUid supplied')); }
    queryDto.auditDeletedDate = null;
    updateDto.auditVersionDate = moment().utc().toDate();
    updateDto.auditMemberUid = auditMemberUid;
    var query = toItem(queryDto),
        update = toItem(updateDto);
    var err = validateItem(query);
    if (err) { return callback(new Error('Invalid query: ' + err)); }
    err = validateItem(update);
    if (err) { return callback(new Error('Invalid update: ' + err)); }
    mongoose.model('Event').findOneAndUpdate(query, update, function (err, item) {
        if (err) { return callback(err); }
        if (item) { return callback(null, toDto(item)); }
        return callback();
    });
};
eventSchema.methods.modifyOne = modifyOne;

var deleteMany = function (queryDto, auditMemberUid, callback) {
    if (typeof queryDto === 'undefined') { return callback(new Error('No query supplied')); }
    queryDto.auditDeletedDate = null;
    var updateDto = { auditDeletedDate: moment().utc().toDate() };
    return modifyMany(queryDto, updateDto, auditMemberUid, callback);
};
eventSchema.methods.deleteMany = deleteMany;

var deleteOne = function (queryDto, auditMemberUid, callback) {
    if (typeof queryDto === 'undefined') { return callback(new Error('No query supplied')); }
    var updateDto = { auditDeletedDate: moment().utc().toDate() };
    return modifyOne(queryDto, updateDto, auditMemberUid, callback);
};
eventSchema.methods.deleteOne = deleteOne;

var Event = mongoose.model('Event', eventSchema);

module.exports = Event;

Upvotes: 2

Views: 1828

Answers (1)

JohnnyHK
JohnnyHK

Reputation: 312159

Mongoose versions with an odd middle number (like 3.9.7) are unstable and shouldn't be used in production (mentioned in the 3.7.0 release notes).

I tried your event model definition code with both 3.8.23 and 4.0.1 Mongoose releases and it worked fine in both cases, with the index on clientName and nm being created as defined in the schema.

Upvotes: 2

Related Questions