Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Added geoNear support for mongoose
geoNear & associated tests. All unit tests are passing
  • Loading branch information
ebensing committed Jul 1, 2013
commit 176205db338d9dcb870a00168150f607455319c7
56 changes: 56 additions & 0 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -1603,6 +1603,62 @@ Model.mapReduce = function mapReduce (o, callback) {
});
}


/**
* geoNear support for Mongoose
*/

Model.geoNear = function (near, options, callback) {
if ('function' == typeof options) {
callback = options;
options = {};
}

if (!callback || !('function' == typeof callback)) {
throw new Error("Must pass a callback to geoNear");
}

if (!near) {
return callback(new Error("Must pass a near option to geoNear"));
}

var x,y;

if (Array.isArray(near)) {
if (near.length != 2) {
return callback(new Error("If using legacy coordinates, must be an array of size 2 for geoNear"));
}
x = near[0];
y = near[1];
} else {
if (!near.type || near.type != "Point" || !Array.isArray(near.coordinates)) {
return callback(new Error("Must pass either a legacy coordinate array or GeoJSON Point to geoNear"));
}

x = near.coordinates[0];
y = near.coordinates[1];
}
var self = this;
this.collection.geoNear(x, y, options, function (err, res) {
if (err || res.errmsg) return callback(err, res);

if (!options.lean) {
var count = res.results.length;

for (var i=0; i < res.results.length; i++) {
var temp = res.results[i].obj;
res.results[i].obj = new self();
res.results[i].obj.init(temp, function (err) {
if (err) return callback(err);
--count || callback(err, res);
});
}
} else {
callback(err, res);
}
});
};

/**
* Perform aggregations on collections.
*
Expand Down
194 changes: 194 additions & 0 deletions test/model.geonear.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@

var start = require('./common')
, assert = require('assert')
, mongoose = start.mongoose
, random = require('../lib/utils').random
, Schema = mongoose.Schema
, DocumentObjectId = mongoose.Types.ObjectId

/**
* Setup
*/

var schema = new Schema({
pos : [Number],
type: String
});

schema.index({ "pos" : "2dsphere"});

mongoose.model('Geo', schema, 'geo' + random());

describe('model', function(){
describe('geoNear', function () {
it('works with legacy coordinate points', function (done) {

var db = start();
var Geo = db.model('Geo');
assert.ok(Geo.geoNear instanceof Function);

var geos = [];
geos[0] = new Geo({ pos : [10,10], type : "place"});
geos[1] = new Geo({ pos : [15,5], type : "place"});
geos[2] = new Geo({ pos : [20,15], type : "house"});
geos[3] = new Geo({ pos : [1,-1], type : "house"});
var count = geos.length;

for (var i=0; i < geos.length; i++) {
geos[i].save(function () {
--count || next();
});
}

function next() {
Geo.geoNear([9,9], { spherical : true, maxDistance : .1 }, function (err, results, stats) {
assert.ifError(err);

assert.equal(1, results.results.length);
assert.equal(1, results.ok);

assert.equal(results.results[0].obj.type, 'place');
assert.equal(results.results[0].obj.pos.length, 2);
assert.equal(results.results[0].obj.pos[0], 10);
assert.equal(results.results[0].obj.pos[1], 10);
assert.equal(results.results[0].obj.id, geos[0].id);
assert.ok(results.results[0].obj instanceof Geo);
Geo.remove(function () {
db.close();
done();
});
});
}
});
it('works with GeoJSON coordinate points', function (done) {

var db = start();
var Geo = db.model('Geo');
assert.ok(Geo.geoNear instanceof Function);

var geos = [];
geos[0] = new Geo({ pos : [10,10], type : "place"});
geos[1] = new Geo({ pos : [15,5], type : "place"});
geos[2] = new Geo({ pos : [20,15], type : "house"});
geos[3] = new Geo({ pos : [1,-1], type : "house"});
var count = geos.length;

for (var i=0; i < geos.length; i++) {
geos[i].save(function () {
--count || next();
});
}

function next() {
var pnt = { type : "Point", coordinates : [9,9] };
Geo.geoNear(pnt, { spherical : true, maxDistance : .1 }, function (err, results, stats) {
assert.ifError(err);

assert.equal(1, results.results.length);
assert.equal(1, results.ok);

assert.equal(results.results[0].obj.type, 'place');
assert.equal(results.results[0].obj.pos.length, 2);
assert.equal(results.results[0].obj.pos[0], 10);
assert.equal(results.results[0].obj.pos[1], 10);
assert.equal(results.results[0].obj.id, geos[0].id);
assert.ok(results.results[0].obj instanceof Geo);
Geo.remove(function () {
db.close();
done();
});
});
}
});
it('works with lean', function (done) {

var db = start();
var Geo = db.model('Geo');
assert.ok(Geo.geoNear instanceof Function);

var geos = [];
geos[0] = new Geo({ pos : [10,10], type : "place"});
geos[1] = new Geo({ pos : [15,5], type : "place"});
geos[2] = new Geo({ pos : [20,15], type : "house"});
geos[3] = new Geo({ pos : [1,-1], type : "house"});
var count = geos.length;

for (var i=0; i < geos.length; i++) {
geos[i].save(function () {
--count || next();
});
}

function next() {
var pnt = { type : "Point", coordinates : [9,9] };
Geo.geoNear(pnt, { spherical : true, maxDistance : .1, lean : true }, function (err, results, stats) {
assert.ifError(err);

assert.equal(1, results.results.length);
assert.equal(1, results.ok);

assert.equal(results.results[0].obj.type, 'place');
assert.equal(results.results[0].obj.pos.length, 2);
assert.equal(results.results[0].obj.pos[0], 10);
assert.equal(results.results[0].obj.pos[1], 10);
assert.equal(results.results[0].obj._id, geos[0].id);
assert.ok(!(results.results[0].obj instanceof Geo));
Geo.remove(function () {
db.close();
done();
});
});
}
});
it('throws the correct error messages', function (done) {

var db = start();
var Geo = db.model('Geo');
var g = new Geo({ pos : [10,10], type : "place"});
g.save(function() {
var threw = false;
Geo.geoNear("1,2", {}, function (e) {
assert.ok(e);
assert.equal(e.message, "Must pass either a legacy coordinate array or GeoJSON Point to geoNear");

Geo.geoNear([1], {}, function (e) {
assert.ok(e);
assert.equal(e.message, "If using legacy coordinates, must be an array of size 2 for geoNear");

Geo.geoNear({ type : "Square" }, {}, function (e) {
assert.ok(e);
assert.equal(e.message, "Must pass either a legacy coordinate array or GeoJSON Point to geoNear");

Geo.geoNear({ type : "Point", coordinates : "1,2" }, {}, function (e) {
assert.ok(e);
assert.equal(e.message, "Must pass either a legacy coordinate array or GeoJSON Point to geoNear");

try {
Geo.geoNear({ type : "test" }, { near : [1,2] }, []);
} catch(e) {
threw = true;
assert.ok(e);
assert.equal(e.message, "Must pass a callback to geoNear");
}

assert.ok(threw);
threw = false;

try {
Geo.geoNear({ type : "test" }, { near : [1,2] });
} catch(e) {
threw = true;
assert.ok(e);
assert.equal(e.message, "Must pass a callback to geoNear");
}

assert.ok(threw);
done();
});
});
});
});
});
});
});
});