I am trying to run a query on a mongoDB and use $nearin the query. This query below works, but I need to aggrate based on LAT and LNG. I am trying to aggregate or get a count based on those that are within a certain distance to pickup_location.
I am using the below in a shell, NOT code.
HERE IS A RECORD:
{
"_id": {
"$oid": "5445ab058767000062"
},
"comment": null,
"dropoff_address": "XHgwMmB7fk1lRn59YFx4MDM=U2FsdGVkX19s3u4NEtImfofJzxnGreGpsna8qA4uVrq7exRDVy+zPn5UwDOj\nzpIs",
"dropoff_location": [
-97.816991,
30.189151
],
"pickup_address": "XHgwMmB7fk1lRn59YFx4MDM=U2FsdGVkX1/23mD3Vv3Nyf4/t+AEickIgOlkaxVp5y/e/5Ia2d3Z0OXtnejw\nrOK+ZPvxQontA9SS30t+MbUIrCMhndxpYcKNFm4xfOzRVxM=",
"pickup_location": [
-97.82075191025548,
30.20993147664687
],
"scheduled_request": false,
"status": "blah",
"timestamp_requested": {
"$date": "2014-10-21T00:38:28.990Z"
},
"total_owed_in_cents": 0,
"total_received_from_in_cents": 0,
"user_id": "5445a9000057"
}
THIS WORKS:
db.thing_requests.aggregate(
[
{$match: {total_received_in_cents: {$gt:1800}, requested_type: 'Blah' }},
{
$group:
{
_id: null,
average: { $avg: "$total_received_in_cents" }
}
}
]
)
NEED THIS ADDED TO WHAT WORKS ABOVE:
{
the_location: {
$near: {
$geometry: {
type: "Point" ,
coordinates: [ <longitude> , <latitude> ]
},
$maxDistance: <distance in meters>,
$minDistance: <distance in meters>
}
}
}
UPDATE:
The top query works. What I need is to say all the items that are aggregated I also need to be sure they are NEAR a certain LAT & LNG.
UPDATE 2:
Ran this query
db.thing_requests.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [ -97.888,37.3222 ] },
distanceField: "dist.calculated",
maxDistance: 2,
minDistance :1,
query: {total_received_in_cents: {$gt:1800}, requested_type: 'Blah' },
includeLocs: "dist.location",
num: 5,
spherical: true
}
},
{
$group:
{
_id: "$user_id",
average: { $avg: "$total_received_from_requester_in_cents" }
}
}
])
RECEIVED THIS ERROR:
assert: command failed: {
"errmsg" : "exception: geoNear command failed: { ok: 0.0, errmsg: \"no geo indices for geoNear\" }",
"code" : 16604,
"ok" : 0
} : aggregate failed
Error: command failed: {
"errmsg" : "exception: geoNear command failed: { ok: 0.0, errmsg: \"no geo indices for geoNear\" }",
"code" : 16604,
"ok" : 0
} : aggregate failed
at Error (<anonymous>)
at doassert (src/mongo/shell/assert.js:11:14)
at Function.assert.commandWorked (src/mongo/shell/assert.js:254:5)
at DBCollection.aggregate (src/mongo/shell/collection.js:1278:12)
at (shell):1:23
2017-08-28T21:21:40.153-0500 E QUERY Error: command failed: {
"errmsg" : "exception: geoNear command failed: { ok: 0.0, errmsg: \"no geo indices for geoNear\" }",
"code" : 16604,
"ok" : 0
} : aggregate failed
at Error (<anonymous>)
at doassert (src/mongo/shell/assert.js:11:14)
at Function.assert.commandWorked (src/mongo/shell/assert.js:254:5)
at DBCollection.aggregate (src/mongo/shell/collection.js:1278:12)
at (shell):1:23 at src/mongo/shell/assert.js:13
Use $geoNear in first stage
db.thing_requests.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [ long,lat ] },
distanceField: "dist.calculated",
maxDistance: 2,
minDistance :1,
query: {total_received_in_cents: {$gt:1800}, requested_type: 'Blah' },
includeLocs: "dist.location",
num: 5,
spherical: true
}
},
{
//use $group here
}
])
you can use $near too
db.thing_requests.find( {
the_location: {
$near: {
$geometry: {
type: "Point" ,
coordinates: [ <longitude> , <latitude> ]
},
$maxDistance: <distance in meters>,
$minDistance: <distance in meters>
}
},total_received_in_cents: {$gt:1800}, requested_type: 'Blah'
});
but in any case you need to specify a geospatial (2d,2dsphere) index on the "the_location" field
if you are using mongoose there is a simple way to do this
specify 2d or 2dsphere index like this in your schema
the_location: {
type: [Number], // format will be [ <longitude> , <latitude> ]
index: '2dsphere' // create the geospatial index
}
or use db command
db.collection.createIndex( { <location field> : "2dsphere" } )
for more perfer https://docs.mongodb.com/manual/geospatial-queries/
Related
I'm trying to match points in one collection with regions stored in another collection.
Here are examples of documents.
Points:
{
"_id" : ObjectId("5e36d904618c0ea59f1eb04f"),
"gps" : { "lat" : 50.073288, "lon" : 14.43979 },
"timeAdded" : ISODate("2020-02-02T15:13:22.096Z")
}
Regions:
{
"_id" : ObjectId("5e49a469afae4a11c4ff3cf7"),
"type" : "Feature",
"geometry" : {
"type" : "Polygon",
"coordinates" : [
[
[ -748397.88, -1049211.61 ],
[ -748402.77, -1049212.2 ],
...
[ -748410.41, -1049213.11 ],
[ -748403.05, -1049070.62 ]
]
]
},
"properties" : {
"Name" : "Region 1"
}
}
And the query I'm trying to construct is something like this:
db.points.aggregate([
{$project: {
coordinates: ["$gps.lon", "$gps.lat"]
}},
{$lookup: {
from: "regions", pipeline: [
{$match: {
coordinates: {
$geoWithin: {
$geometry: {
type: "Polygon",
coordinates: "$geometry.coordinates"
}
}
}
}}
],
as: "district"
}}
])
I'm getting an error:
assert: command failed: {
"ok" : 0,
"errmsg" : "Polygon coordinates must be an array",
"code" : 2,
"codeName" : "BadValue"
} : aggregate failed
I've noticed the structure of $geoWithin document is same as structure of one I have for each region. So I tried such query:
db.points.aggregate([
{$project: {
coordinates: ["$gps.lon", "$gps.lat"]
}},
{$lookup: {
from: "regions", pipeline: [
{$match: {
coordinates: {
$geoWithin: "$geometry.coordinates"
}
}}
],
as: "district"
}}
])
The error was same.
I looked up for geoqueries but surprisingly all found mentions had static region document instead of one taken from a collection. So I'm wondering - is it ever possible to map points with regions having that both document collections aren't static and taken from DB?
Unfortunately not possible
You could perform query below if $geometry could deal with MongoDB Aggregation Expressions.
db.points.aggregate([
{
$lookup: {
from: "regions",
let: {
coordinates: [
"$gps.lon",
"$gps.lat"
]
},
pipeline: [
{
$addFields: {
coordinates: "$$coordinates"
}
},
{
$match: {
coordinates: {
$geoWithin: {
$geometry: {
type: "Polygon",
coordinates: "$geometry.coordinates"
}
}
}
}
}
],
as: "district"
}
}
])
Hi i have the following problem:
I would like to use $geoNear (to count distance between two points) but after $loopback (and on collection that i joined).
This is the model for companyBases collection (i would like to join it):
{
"_id" : ObjectId("5d7cfe13f42e7345d967b378"),
"location" : {
"type" : "Point",
"coordinates" : [
20.633856,
49.761268
]
},
"vehicles" : [
{
"_id" : ObjectId("5d7cfe13f42e7345d967b340"),
...other fields that doesn't matter
}
]
}
This is vehicle collection:
{
"_id" : ObjectId("5d7cfe13f42e7345d967b340"),
...other fields that doesn't matter
}
I would like to join companyBase collection in aggregation on vehicles collection:
db.vehicles.aggregate([
{
$lookup: {
from: "companybases",
let: {
vehicleId: "$_id"
},
pipeline: [
{
$match: {
$expr: { $in: ["$$vehicleId", "$vehicles._id"] }
}
}
],
as: "companyBases"
}
},
{
$unwind: "$companyBases"
},
{
$geoNear: {
near: {
type: "Point",
coordinates: [50.02485, 20.0008]
},
distanceField: "distance",
spherical: true
}
}
]);
But it returns me:
{
"message" : "$geoNear is only valid as the first stage in a pipeline.",
"operationTime" : "Timestamp(1568472833, 1)",
"ok" : 0,
"code" : 40602,
"codeName" : "Location40602",
"$clusterTime" : {
"clusterTime" : "Timestamp(1568472833, 1)",
"signature" : {
"hash" : "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"keyId" : "0"
}
},
"name" : "MongoError"
}
When i am doing the same pipeline on companybases collection it returns me documents with counted distance:
db.companybases.aggregate([
{
$geoNear: {
near: {
type: "Point",
coordinates: [50.02485, 20.0008]
},
distanceField: "distance",
spherical: true
}
}
]);
And result:
{
"_id" : ObjectId("5d7cfe13f42e7345d967b378"),
"location" : {
"type" : "Point",
"coordinates" : [
20.633856,
49.761268
]
},
"vehicles" : [
{
...some fields
},
],
...some fields
"distance" : 4209673.447019393
}
I realize that the error may be because of missing index on vehicles collection. So is there any way to calculate distance with $geoNear with $lookup ? Or maybe it's impossible and i have to do on my own ?
Simple solutions (you can put $geoNear in $lookup pipeline):
db.vehicles.aggregate([
{
$lookup: {
from: "companybases",
let: {
vehicleId: "$_id"
},
pipeline: [
{
$geoNear: {
near: {
type: "Point",
coordinates: [50.02485, 20.0008]
},
distanceField: "distance",
spherical: true
}
},
{
$match: {
$expr: { $in: ["$$vehicleId", "$vehicles._id"] }
}
}
],
as: "companyBases"
}
},
{
$unwind: "$companyBases"
}
]);
But that strongly impressed the performance (it takes at least 5 seconds), becuase $geoNear is used before match.
your error shows "message" : "$geoNear is only valid as the first stage in a pipeline.",
it is clear that you can't put '$geoNear' after $lookup
so You can only use $geoNear as the first stage of a pipeline.
just to exchange you agg order
use $geoNear at first then with $lookup
I'm trying to write a MongoDB query that searches for documents within a radius centered on a specified location.
The query below works. It finds all documents that are within searching.radius radians of searching.coordinates.
However what I would like to do is add the current documents allowed_radius value to the searching.radius value, so that the allowed sphere is actually larger.
How can I phrase this query to make this possible?
Present Query:
collection.aggregate([
{
$project:{
location: "$location",
allowed_radius: "$allowed_radius"
}
},
{
$match: {
$and:
[
{ location: { $geoWithin: { $centerSphere: [ searching.coordinates, searching.radius ] }}},
{...},
...]
...}
]);
What I am trying to do (pseudo-query):
collection.aggregate([
{
$project:{
location: "$location",
allowed_radius: "$allowed_radius"
}
},
{
$match: {
$and:
[
{ location: { $geoWithin: { $centerSphere: [ searching.coordinates, { $add: [searching.radius, $allowed_radius]} ] }}},
{...},
...]
...}
]);
I tried using $geoWithin / $centerSphere, but couldn't make it work this way.
Here is another way of doing so, using the $geoNear operator:
Given this input:
db.collection.insert({
"airport": "LGW",
"id": 1,
"location": { type: "Point", coordinates: [-0.17818, 51.15609] },
"allowed_radius": 100
})
db.collection.insert({
"airport": "LGW",
"id": 2,
"location": { type: "Point", coordinates: [-0.17818, 51.15609] },
"allowed_radius": 0
})
db.collection.insert({
"airport": "ORY",
"id": 3,
"location": { type: "Point", coordinates: [2.35944, 48.72528] },
"allowed_radius": 10
})
And this index (which is required for $geoNear):
db.collection.createIndex( { location : "2dsphere" } )
With searching.radius = 1000:
db.collection.aggregate([
{ $geoNear: {
near: { "type" : "Point", "coordinates": [7.215872, 43.658411] },
distanceField: "distance",
spherical: true,
distanceMultiplier: 0.001
}},
{ $addFields: { radius: { "$add": ["$allowed_radius", 1000] } } },
{ $addFields: { isIn: { "$subtract": ["$distance", "$radius" ] } } },
{ $match: { isIn: { "$lte": 0 } } }
])
would return documents with id 1 (distance=1002 <= radius=1000+100) and 3 (distance=676 <= radius=1000+10) and discard id 2 (distance=1002 > 1000+0).
The distanceMultiplier parameter is used to bring back units to km.
$geoNear must be the first stage of an aggregation (due to the usage of the index I think), but one of the parameters of $geoNear is a match query on other fields.
Even if it requires the geospacial index, you can add additional dimensions to the index.
$geoNear doesn't take the location field as an argument, because it requires the collection to have a geospacial index. Thus $geoNear implicitly uses as location field (whatever the name of the field) the one indexed.
Finally, I'm pretty sure the last stages can be simplified.
The $geoNear stage is only used to project the distance on each record:
{ "airport" : "ORY", "distance" : 676.5790971238937, "location" : { "type" : "Point", "coordinates" : [ 2.35944, 48.72528 ] }, "allowed_radius" : 10, "id" : 3 }
{ "airport" : "LGW", "distance" : 1002.3351814526812, "location" : { "type" : "Point", "coordinates" : [ -0.17818, 51.15609 ] }, "allowed_radius" : 100, "id" : 1 }
{ "airport" : "LGW", "distance" : 1002.3351814526812, "location" : { "type" : "Point", "coordinates" : [ -0.17818, 51.15609 ] }, "allowed_radius" : 0, "id" : 2 }
In fact, the geoNear operator requires the use of the distanceField argument, which is used to project the computed distance on each record for the next stages of the query. At the end of the aggregation, returned records look like:
{
"airport" : "ORY",
"location" : { "type" : "Point", "coordinates" : [ 2.35944, 48.72528 ] },
"allowed_radius" : 10,
"id" : 3,
"distance" : 676.5790971238937,
"radius" : 1010,
"isIn" : -333.4209028761063
}
If necessary, you can remove fields produced by the query for the query (distance, radius, isIn) with a final $project stage. For instance: {"$project":{"distance":0}}
I am trying to use a $geoWithin query in a aggregate pipeline, but I am getting a an
MongoError: exception: bad query: BadValue bad geo query: { $geoWithin: { $box: [ [ "13.618240356445312", "51.01343066212905" ], [ "13.865432739257812", "51.09662294502995" ] ] } }
My query is:
{
$match: {
'gps.coordinates.matched': {
$geoWithin: {
$box: [
[ swlng, swlat ],
[ nelng , nelat ]
]
}
}
}
},
{ $project : {shortGeohash: {$substr: ["$gps.geohash.original", 0, 11]}}},
{ $group: {_id: "$shortGeohash", count: {$sum:1}, originalDoc:{$push: "$$ROOT"}}}
The query only for $geoWithin as well $project...,$group work well on their own, but combined the error occurs.
I tried your query and it seems to actually work. I executed the query over a collection with documents such as this.
[{
"_id" : "5a2404674eb6d938c8f44856",
"code" : "M.12345",
"loc" : {
"type" : "Point",
"coordinates" : [
41.9009789,
12.5010465
]
}
},
...
]
The aggregation pipeline is this.
{
$match: {
'loc': {
$geoWithin: {
$box: [
[ 0, 0 ],
[ 5, 5 ]
]
}
}
}
},
{ $project : {subCode: {$substr: ["$code", 0, 4]}}},
{ $group: {_id: "$subCode", count: {$sum:1}, originalDoc:{$push: "$$ROOT"}}}
One of the results is this.
{
"_id" : "M.10",
"count" : 12.0,
"originalDoc" : [
{
"_id" : "5a2481c44eb6d92b6895633a",
"subCode" : "M.10"
},
.... //11 more items
]
}
Results are correctly returned with mongod v3.4.9.
It seems like $geoWithin is not one of the aggregation operators.
The reference example works, sadly, I am not aware of a way to add an aggregation to that.
I am trying to run $geoNear with mongodb aggregate and getting the below error. I tried to find the issue but I am unable to. Any help would be appreciated.
Thanks!
Error -
aggregate failed: {
"errmsg": "exception: geoNear command failed: { ok: 0.0, errmsg: \"can't get query runner\" }",
"code": 16604,
"ok": 0
} at /Users/waseemquamar/.mongorc.js:82
Query -
db.retailers.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [ -73.99279 , 40.719296 ] },
distanceField: "dist.calculated",
maxDistance: 2,
includeLocs: "dist.location",
num: 5,
spherical: true
}
}
Index - db.retailers.getIndexes();
[
{
"v": 1,
"key": {
"_id": 1
},
"name": "_id_",
"ns": "hw_development.retailers"
},
{
"v": 1,
"key": {
"geolocation": "2d"
},
"name": "geolocation_2d",
"ns": "hw_development.retailers",
"background": true,
"safe": null
}
]