Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

geoWithin support #101

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ use filters for that. You can ask the service for equality, or values greater or
| **in** | `in` | `/users?gender__in=female,male` | returns all female and male users |
| **nin** | `nin` | `/users?age__nin=18,30` | returns all users with age other than 18 or 30 |
| **Regex** | `regex` | `/users?username__regex=/^baugarten/i` | returns all users with a username starting with baugarten |
| **within** | `geoWithin box` | `/users?loc__within=1,1&loc__within=2,2` | returns all users with a loc inside of specified box |
| **within** | `geoWithin polygon` | `/users?loc__within=1,1&loc__within==1,3&loc__within=2,2` | returns all users with a loc inside of specified polygon |

### Populating a sub-entity

Expand Down
3 changes: 2 additions & 1 deletion examples/movies/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ var movie = app.movie = restful.model("movies", mongoose.Schema({
productionco: 'string',
director: { type: 'ObjectId', ref: 'users' }
},
secret: { type: 'string', select: false }
secret: { type: 'string', select: false },
loc: []
}));

movie.methods([
Expand Down
25 changes: 22 additions & 3 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ var methods = ['get', 'post', 'put', 'delete'], // All HTTP methods, PATCH not c
},
'in': query('in'),
'nin': query('nin'),
'within': query('within'),
});
defaults = function() {
return {
Expand Down Expand Up @@ -357,9 +358,15 @@ function preprocess(req, res, next) {
}

function query(key) {
return function(val, query) {
return query[key](val);
};
if (key === 'within') {
return function(val, query) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this special case really necessary? Seems redundant

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because within function requires multiple arguments. For square area there will be 2 arguments, for polygons more.

return query[key].apply(query, val);
};
} else {
return function(val, query) {
return query[key](val);
};
}
}

function haveOneModel(req) {
Expand Down Expand Up @@ -465,6 +472,18 @@ function filterable(props, subfilters) {
data = data.split(',');
}

if (filter_func === 'within') {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can leverage the existing condition for 'in' and 'nin'

i.e.

if (filter_func === 'in' || filter_func === 'nin' || filter_func === 'within') {
  data = data.split(',');
}

Actually, now that I look at this... this should all be done in coerceData, so coerceData would look like

if (filter_func === 'in' || filter_func === 'nin' || filter_func === 'within') {
  data = data.split(',');
}
if (filter_func === 'within') {
  data = _.map(data, function(item) { parseFloat(item) });
}
if (data && data.toLowerCase && data.toLowerCase() === 'true') {
  ...
} ...

I hope that makes sense. That way, the preprocessing on input happens in one place

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Within function requires here multiple coordinate pairs (2 for square search areas or more for polygons).
When I use GET "/users?loc__within=1,1&loc__within=2,2" data variable will contain ["1,1", "2,2"] - that's the reason behind my implementation.

if (!Array.isArray(data)) {
data = [data];
}

data = _.map(data, function(item) {
return _.map(item.split(','), function(item) {
return parseFloat(item);
});
});
}

return subfilters[filter_func](data, quer.where(field[0]));
},
contains: function(key, quer) {
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ exports.user = app.user;
mongoose = app.mongoose;
fixtures = fixtures.connect(mongoose.connection.name);

fixtures.load(data, function(err) {
fixtures.clearAndLoad(data, function(err) {
exports.users = data.users;
exports.movies = data.movies;
done = true;
Expand Down
16 changes: 11 additions & 5 deletions test/fixtures/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ exports.movies = [
_id: new ObjectId(),
title: "Title1",
year: 2012,
loc: [1, 2],
meta: {
productionco: "idk",
director: exports.users[1]._id,
Expand All @@ -28,27 +29,32 @@ exports.movies = [
{
_id: new ObjectId(),
title: "Title2",
year: 2011
year: 2011,
loc: [12, 1]
},
{
_id: new ObjectId(),
title: "Title3",
year: 2013
year: 2013,
loc: [1, 1]
},
{
_id: new ObjectId(),
title: "Title4",
year: 2013
year: 2013,
loc: [2, 2]
},
{
_id: new ObjectId(),
title: "Title5",
year: 2013
year: 2013,
loc: [3, 3]
},
{
_id: new ObjectId(),
title: "Title6",
year: 2010
year: 2010,
loc: [0, 0]
},
{
_id: new ObjectId(),
Expand Down
15 changes: 15 additions & 0 deletions test/model.filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,20 @@ describe('Model', function() {
done();
});
});
it('should filter using within', function(done) {
request(app)
.get('/api/movies?loc__within=0.5,1.5&loc__within=1.5,2.5')
.end(function(err, res) {
res.body.length.should.be.above(0)
res.body.forEach(function(movie) {
movie.loc.should.be.instanceof(Array).and.have.lengthOf(2);
movie.loc[0].should.be.greaterThan(.5);
movie.loc[0].should.be.lessThan(1.5);
movie.loc[1].should.be.greaterThan(1.5);
movie.loc[1].should.be.lessThan(2.5);
});
done();
});
});
});
});