Labithiotis
Labithiotis

Reputation: 4139

No Special Indexes with MongoDB using mongoose

Trying to use GeoJson and $near to get players near user using location, however I get no indexes set error from mongo, using mongoose that has the coordinates set to index. Code looks correct to all examples I have seen.

I want to have multiple locations field too so using $near instead of GeoNear.

Error I'm getting from mongoDB

 MongoError: can't find any special indices: 2d (needs index), 2dsphere (needs index),  for: { location: { $near: { $geometry: { coordinates: [ 52, -0.4 ], type: "Point" }, $maxDistance: 10 } } }    

Player and Users model

/** =========== Player Model =========== **/

var mongoose                    = require('mongoose');
var _                           = require('underscore');
var Schema                      = mongoose.Schema;
var ObjectId                    = Schema.ObjectId;

var geoJSONSchema = {
    type: {
        type: String,
        enum: ['Point', 'LineString', 'Polygon'],
        default: 'Point'
    },
    coordinates:{
        type: mongoose.Schema.Types.Mixed,
        index: '2dsphere',
        default: [0,0]
    }
};

.../\/\/\... other code .../\/\/\...

var playerSchema = new Schema({
    created: { type: Date, default: Date.now },
    controller_id: {
        type: ObjectId,
        required: true
    },
    controller: {
        type: String,
        required: true,
        enum: ['ai', 'user'],
        default: 'ai'
    },
    state:{
        type: String,
        required: true,
        enum: ['survivor', 'zombie'],
        default: 'survivor'
    },
    timestamps: {
        created: { type: Date, default: Date.now },
        last_active: { type: Date },
        ai_last_spawned_at: { type: Date }
    },
    stats: playerStatsSchema,
    profile: profileSchema,
    location: geoJSONSchema,
    //locations: geoJSONSchema, // Locations player has been
    items: [{ type: Schema.Types.ObjectId, ref: 'items' }]

},{ collection : 'player' });


.../\/\/\... other code .../\/\/\...


playerSchema.pre('init', function (next) {
    // do stuff
    next();
});

// Compile Model
var Player = mongoose.model('Player', playerSchema);



/** =========== User Controller =========== **/

// Player is created with any new user, called inside another method.

                var user = new User({

                    auth: options.auth,
                    contact: options.contact

                }),

                playerLocation  = _.extend({ type: 'Point', coordinates: [0,0] }, options.location),

                player          = new Player({
                    controller_id: user._id,
                    controller: 'user',
                    state: 'survivor',
                    location: playerLocation,
                    //locations: playerLocation,
                    profile: {
                        name: (options.contact.first_name || '')
                    }
                });

            player.save(function (err, playerDoc) {

                if (err) {
                    console.warn('[cUser:player.save]', err);
                    if (options.error) options.error(err);
                    return;
                }

                user.player_id = playerDoc._id;
                user.player = playerDoc;

                user.save(function (err, userDoc) {

                    if (err) {
                        console.error('[cUser:user.save]', err);
                        return;
                    }

                    if (options.success instanceof Function) options.success();
                    console.info('[cUser:user.save]created a new user (' + options.name + ') and player');

                });

            });

/** =========== Player GeoNear (User Method) =========== **/

userSchema.methods.getPlayersNear = function(distance){

    if(!Player)Player = mongoose.model('Player');

    return Player.find({ location: { $near: { $geometry: this.player.location , $maxDistance: distance||100 /* In Meters */ } }}, function(err,docs){
        if(err) console.error('[mUser:player]',err);
        return docs;
    });

};

Upvotes: 2

Views: 752

Answers (2)

user1820536
user1820536

Reputation: 109

"location" did not work for me, but it worked when I changed it to "loc".

Upvotes: 0

Neil Lunn
Neil Lunn

Reputation: 151112

The problem here is that your index is defined on the wrong field. What you actually did here was define the index on the "coordinates" field:

var geoSchema = {
  "type": {
    "type": String,
    "enum": ["Point","LineString", "Polygon"],
    "default": "Point"
  },
  "coordinates": {
    "type": Schema.Types.Mixed,
    "index": "2dsphere",
    "default": [0,0]
  }
};

Really what you want to do is define that on the "location" field. That might seem difficult to do by "path" definition, but you can also define indexes on the "schema". So as a minimal working example:

var async = require("async");
    mongoose = require("mongoose"),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/geo');

var geoSchema = {
  "type": {
    "type": String,
    "enum": ["Point","LineString", "Polygon"],
    "default": "Point"
  },
  "coordinates": {
    "type": Schema.Types.Mixed,
    "default": [0,0]
  }
};

var playerSchema = new Schema({
  "name": String,
  "location": geoSchema
});

playerSchema.index({ "location": "2dsphere"});

var Player = mongoose.model("Player", playerSchema, "player");

var player = new Player({
  "name": "bill",
  "location": {
    "type": "Point",
    "coordinates": [1,2]
  }
});

async.series(
  [
    function(callback) {
      player.save(function(err,player) {
        if (err) throw err;
        console.log(player);
        callback();
      });
    },
    function(callback) {
      Player.find(
        {
          "location": {
            "$near": {
              "$geometry": {
                "type": "Point",
                "coordinates": [1,2]
              }
            }
          }
        },
        function(err,players) {
          if (err) throw err;
          console.log(players);
          callback();
        }
      );
    }
  ]
);

And then the query works just fine.

Upvotes: 0

Related Questions