Skip to content

Added near by search support #256

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

Open
wants to merge 1 commit into
base: main
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
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,60 @@ Add `q`
GET /posts?q=internet
```

### Near by search

Add `near` and `distance`

```
GET /cities?near=lat,lon&distance=meters
```

Add `cities` in db.json

```json
{
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" },
"cities": []
}
```

start posting your cities
```
POST /cities
```
with data
```json
{
"id": 1,
"city": "Jeogiwe",
"lat": -28.12785,
"lon": -145.96895
}
```
to search
```
GET /cities?near=-28.12785,-145.96895&distance=5000
```
output is
```json
[
{
"id": 1,
"city": "Jeogiwe",
"lat": -28.12785,
"lon": -145.96895,
"geohash": 344516337195371
}
]
```
When the data is posted to server a geohash property is added in data. This near search works on [geohash](https://en.wikipedia.org/wiki/Geohash) concept. To precalculate the geohash in your existing data use [node-geohash](https://github.com/sunng87/node-geohash)'s `encode_int` method.

### Relationships

To include children resources, add `_embed`
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
"cors": "^2.3.0",
"errorhandler": "^1.2.0",
"express": "^4.9.5",
"geo-nearby": "^1.1.1",
"got": "^3.3.0",
"lodash": "^3.9.2",
"lowdb": "^0.10.0",
"method-override": "^2.1.2",
"morgan": "^1.3.1",
"ngeohash": "^0.6.0",
"node-uuid": "^1.4.2",
"object-assign": "^4.0.1",
"pluralize": "^1.1.2",
Expand Down Expand Up @@ -61,7 +63,10 @@
"rest",
"data",
"dummy",
"sandbox"
"sandbox",
"geo",
"near",
"spatial"
],
"author": "Typicode <[email protected]>",
"license": "MIT",
Expand Down
15 changes: 15 additions & 0 deletions src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ module.exports = function () {
alias: 'c',
description: 'Path to config file',
default: 'json-server.json'
},
lattitude: {
alias: 'y',
description: 'Latitude property',
default: 'lat'
},
longitude: {
alias: 'x',
description: 'Longitude property',
default: 'lon'
},
geohash: {
alias: 'g',
description: 'Geohash property',
default: 'geohash'
}
})
.boolean('watch')
Expand Down
3 changes: 3 additions & 0 deletions src/cli/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ function createApp (source, object, routes, argv) {
}

router.db._.id = argv.id
router.db._.geohash = argv.geohash || 'geohash'
router.db._.lat = argv.lat || 'lat'
router.db._.lon = argv.lon || 'lon'
app.db = router.db
app.use(router)

Expand Down
15 changes: 14 additions & 1 deletion src/server/mixins.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
var uuid = require('node-uuid')
var pluralize = require('pluralize')
var geo = require('geo-nearby')
var ngeohash = require('ngeohash')

module.exports = {
getRemovable: getRemovable,
createId: createId,
deepQuery: deepQuery
deepQuery: deepQuery,
nearBy: nearBy,
calGeohash: calGeohash
}

// Returns document ids that have unsatisfied relations
Expand Down Expand Up @@ -75,3 +79,12 @@ function deepQuery (value, q) {
}
}
}
// extends lowdb instance with nearBy function
function nearBy (db, lat, lon, distance) {
var _ = this
return geo(db, {geo: _.geohash || 'geohash'}).nearBy(lat, lon, distance)
}
// extends lowdb instace with geohash function
function calGeohash (lat, lon) {
return ngeohash.encode_int(lat, lon)
}
5 changes: 5 additions & 0 deletions src/server/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ module.exports = function (source) {
// Add specific mixins
db._.mixin(mixins)

// Defualt near properties
db._.lat = 'lat'
db._.lon = 'lon'
db._.geohash = 'geohash'

// Expose database
router.db = db

Expand Down
34 changes: 34 additions & 0 deletions src/server/router/plural.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ module.exports = function (db, name) {

// GET /name
// GET /name?q=
// GET /name?near=lat,lon&distance=
// GET /name?attr=&attr=
// GET /name?_end=&
// GET /name?_start=&_end=&
Expand All @@ -47,6 +48,8 @@ module.exports = function (db, name) {
// Remove q, _start, _end, ... from req.query to avoid filtering using those
// parameters
var q = req.query.q
var near = req.query.near
var distance = req.query.distance
var _start = req.query._start
var _end = req.query._end
var _sort = req.query._sort
Expand All @@ -55,6 +58,8 @@ module.exports = function (db, name) {
var _embed = req.query._embed
var _expand = req.query._expand
delete req.query.q
delete req.query.near
delete req.query.distance
delete req.query._start
delete req.query._end
delete req.query._sort
Expand Down Expand Up @@ -97,6 +102,21 @@ module.exports = function (db, name) {

}

// nearBy query
if (near && distance) {
var latLong = near.split(',').map(function (val) {
return parseFloat(val)
})
if (
latLong.length === 2
&& !isNaN(latLong[0])
&& !isNaN(latLong[1])
&& !isNaN(distance)
) {
chain = chain.nearBy(latLong[0], latLong[1], distance)
}
}

Object.keys(req.query).forEach(function (key) {
// Don't take into account JSONP query parameters
// jQuery adds a '_' query parameter too
Expand Down Expand Up @@ -209,6 +229,12 @@ module.exports = function (db, name) {
req.body[key] = utils.toNative(req.body[key])
}

// check if it has location data then create geohash
if (req.body[db._.lat] && req.body[db._.lon]) {
req.body[db._.geohash] = db._.calGeohash(
req.body[db._.lat], req.body[db._.lon])
}

var resource = db(name)
.insert(req.body)

Expand All @@ -226,6 +252,14 @@ module.exports = function (db, name) {

var id = utils.toNative(req.params.id)

// check if it has location data then create geohash
if (req.body[db._.lat] || req.body[db._.lon]) {
var old = db(name).getById(id)
var lat = req.body[db._.lat] || old.lat
var lon = req.body[db._.lon] || old.lon
req.body[db._.geohash] = db._.calGeohash(lat, lon)
}

var resource = req.method === 'PATCH' ?
db(name).updateById(id, req.body) :
db(name).replaceById(id, req.body)
Expand Down
12 changes: 12 additions & 0 deletions src/server/router/singular.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,24 @@ module.exports = function (db, name) {
}

function create (req, res, next) {
// check if it has location data then create geohash
if (req.body[db._.lat] && req.body[db._.lon]) {
req.body[db._.geohash] = db._.calGeohash(
req.body[db._.lat], req.body[db._.lon])
}
res.locals.data = db.object[name] = req.body
res.status(201)
next()
}

function update (req, res, next) {
// check if it has location data then create geohash
if (req.body[db._.lat] || req.body[db._.lon]) {
var old = db.object[name]
var lat = req.body[db._.lat] || old.lat
var lon = req.body[db._.lon] || old.lon
req.body[db._.geohash] = db._.calGeohash(lat, lon)
}
if (req.method === 'PUT') {
delete db.object[name]
db.object[name] = {}
Expand Down
Loading