title: Number TypeError with Mongoose Model?
body: <p>There's a 500 internal server error, for <code>Error: TypeError: path must be a string</code></p>
  
  <p>The browser console points to ItemCtrl.js:35</p>
  
  <pre><code>console.log('Error: ' + data);
  </code></pre>
  
  <p>The stack trace for the error looks like this:</p>
  
  <blockquote>
    <p>TypeError: path must be a string
        at Query.where (/Users/Name/Downloads/dev/v16/node_modules/mongoose/lib/query.js:593:11)
        at Function.where (/Users/Name/Downloads/dev/v16/node_modules/mongoose/lib/model.js:1040:18)
        at model. (/Users/Name/Downloads/dev/v16/app/models/item.js:44:35) ...</p>
  </blockquote>
  
  <p>It seems the error derives from a line in <em>/app/models/item.js</em>
  specifically in the <code>else if</code> statement..
  because with the <code>else if</code> commented out, both the <code>if</code> works and the <code>else</code> works..</p>
  
  <pre><code>// load mongoose since we need it to define a schema and model
  var mongoose = require('mongoose');
  
  var ItemSchema = mongoose.Schema({
      title : String,
      position: Number
  });
  
  // before validation starts, the number of Items is counted..afterwards, the position is set
  ItemSchema.pre("validate", function(next) {
  
      var doc = this;
  
      // if 'position' is not filled in, fill it in
      // not using !position because 0 might be a valid value
      if(typeof doc.position !== "number") {
          // count the number of Items *
          // use mongoose.model to fetch the model because it is not compiled yet
          mongoose.model("Item").count(function(err, num) {
              // if there was an error, pass it to next()
              if(err)
                  return next(err);
  
              // set the position, then call next();
              doc.position = num;
              return next();
          });
      } else if(typeof doc.position == "number") {
  
          console.log('yes, a number');
  
          // check if there is an existing document with the same position
          // use mongoose.model to fetch the model because the model is not compiled yet
          mongoose.model("Item").where({_id: {$ne: doc._id}, position: doc.position}).count( function (err, count) {
  
              // if there was an error, pass it to next()
              if(err)
                  return next(err);
  
              // if there is a doc with the SAME position, execute an update to MOVE DOWN all the $gte docs
              if(count &gt; 0) {
                  // use mongoose.model to fetch the model because the model is not compiled yet
                  mongoose.model("Item").update({position: {$gte: doc.position}}, {position: {$inc: 1}}, {multi: 1}, function(err, numAffected) {
  
                      console.log(numAffected);
  
                      // Call next() (with or without an error)
                      next(err);
                  });
  
              } else {
                  //  there are no docs that need to move down, so call next()
                  next();
              }
          });
      } else {
          //  there is no need to count or update positions, so call next()
          next();
      }
  });
  
  module.exports = mongoose.model('Item', ItemSchema);
  </code></pre>
  
  <blockquote>
    <ul>
    <li>The path of the data is this: </li>
    <li>form field in /public/views/CreateItem.html</li>
    <li>to /public/js/controllers/ItemCtrl.js</li>
    <li>to /public/js/services/ItemService.js</li>
    <li>to /app/routes.js</li>
    <li>to /app/models/item.js</li>
    <li>and the data can be viewed in json at localhost:8080/api/items</li>
    </ul>
  </blockquote>
  
  <p>/public/views/CreateItem.html</p>
  
  <pre><code>&lt;form&gt;
      &lt;div class="form-group"&gt;
          &lt;!-- BIND THESE VALUES TO formData.value IN ANGULAR --&gt;
          &lt;input type="text" class="form-control input-lg text-center" placeholder="Item" ng-model="formData.title"&gt;
          &lt;input type="number" class="form-control input-lg text-center" placeholder="0" ng-model="formData.position"&gt;
      &lt;/div&gt;
      &lt;!-- createItem() WILL CREATE THE NEW ITEM VIA ANGULAR THROUGH NODE API TO MONGOOSE --&gt;
      &lt;button type="submit" class="btn btn-primary btn-lg" ng-click="createItem()"&gt;Add Item&lt;/button&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p>/public/js/controllers/ItemCtrl.js</p>
  
  <pre><code>angular.module('ItemCtrl', [])
  
  // inject the Item service.factory into our controller
  .controller('ItemController', function($scope, $http, Items, isEmptyObjectFilter) {
  
          $scope.formData = {};
  
          // CREATE ==================================================================
          // when submitting the add form, send the text to the node API
          $scope.createItem = function() {
  
                  // validate the formData (using our exentions.js .filter) to make sure that something is there..if form is empty, nothing will happen
                  if (!isEmptyObjectFilter($scope.formData)) {
  
                          // call the create function from our service (returns a promise object)
                          Items.create($scope.formData)
  
                                  // if successful creation, call our get function to get all the new items
                                  .success(function(data) {
                                          $scope.formData = {}; // clear the form so our user is ready to enter another
                                          $scope.items = data; // assign our new list of items
                                  })
                                  .error(function(data) {
                                          console.log('Error: ' + data);
                                  });
                  }
          };
  });
  </code></pre>
  
  <p>/public/js/services/ItemService.js</p>
  
  <pre><code>angular.module('ItemService', [])
  
  // super simple service
  // each function returns a promise object 
  .factory('Items', function($http) {
      return {
          get : function() {
              return $http.get('/api/items');
          },
          create : function(itemData) {
              return $http.post('/api/items', itemData);
          }
      }
  });
  </code></pre>
  
  <p>/app/routes.js</p>
  
  <pre><code>// load the item model
  var Item = require('./models/item');
  
  // expose the routes to our app with module.exports
  module.exports = function(app) {
  
      // create item and send back all items after creation
      app.post('/api/items', function(req, res) {
  
          console.log(req.body.position);
  
          // create an item, information comes from AJAX request from Angular
          Item.create({
              title : req.body.title,
              position : req.body.position
          }, function(err, item) {
              if (err)
                  res.send(err);
  
              // get and return all the items after you create another
              Item.find(function(err, items) {
                  if (err)
                      res.send(err)
                  res.json(items);
              });
          });
  
      });
  };
  </code></pre>