When I use $geoNear I can return a calculated distance by including the "distanceField" which is extremely useful when displaying items and their distance from a location. My code for this is:
$geoNear: {
near: {
type: "Point",
coordinates: [
req.body.position.lng ,
req.body.position.lat
]
},
distanceField: "dist.calculated",
maxDistance: req.body.distance,
spherical: true
When using Atlas search, I need the same or similar "distanceField", but havn't been able to find a solution. I am open to other workarounds. Here's my $search pipeline:
"$search": {
"index": "searchResults",
"compound": {
"must": [
{
"text": {
"query": `${req.body.query}`,
path: [
"name",
"description",
"tag", ],
"fuzzy": {
"maxEdits": 2,
"prefixLength": 4
}
}
},
{
"geoWithin": {
"circle": {
"center": {
"type": "Point",
"coordinates": [
req.body.position.lng,
req.body.position.lat
]
},
"radius": req.body.distance,
},
"path": "location",
}
}
],
"filter":[{
"text": {
"query": "Active",
"path": "active",
}
}]
}
}
}```
Related
How to use $geoNear in mongodb?
I want to use geoNear 3 times and i want to get all result.
I try to made an aggregationlike this.
but it occurs an error "$geoNear requires a 'near' option as an Array"
Thank you for help
db.getCollection('collection').aggregate([
{
"$geoNear": {
"$or": [
{
"near": {
"type": "Point",
"coordinates": [
126.7384307,
35.9672442
]
},
"maxDistance": 1000,
"query": {
"distance": 1000,
"use_noti": true,
},
"distanceField": "location",
},
{
"near": {
"type": "Point",
"coordinates": [
126.7384307,
35.9672442
]
},
"maxDistance": 5000,
"query": {
"distance": 5000,
"use_noti": true,
},
"distanceField": "location",
},
]
}
},
])
```
I have a database with two collections like so:
[
{
"name": "Person A",
"location": {"type": "Point", "coordinates": [180, 90]}
},
{
"name": "Person B",
"location": {"type": "Point", "coordinates": [-180, -90]}
}
]
[
{
"name": "Store A",
"location": {"type": "Point", "coordinates": [180, 90]}
},
{
"name": "Store B",
"location": {"type": "Point", "coordinates": [-180, -90]}
}
]
For each person, I want to find the nearest place. I could get it to find the nearest place for one specific pair of coordinates, not for the entire collection. Is there any way to do that without using foreach?
This is the closest I got following the MongoDB documentation:
// Current $geoNear:
{
// Instead of having to give constants of a single
// point, I want to compare against all coordinates
// of another collection. ↓
near: {"type":"Point","coordinates":[-180, 90]},
distanceField: 'distance',
maxDistance: 50,
spherical: true
}
This can be achieved using aggregation.
[{
$lookup: {
from: "places",
let: {
"personPoint": "$location"
},
as: "nearestPlace",
pipeline: [
{
$geoNear: {
near: "$$personPoint",
spherical: true,
distanceField: "distance",
maxDistance: 50,
}
},
{
$unwind: "$location"
},
]
}
},
{
$unwind: {
path: "$nearestPlace",
preserveNullAndEmptyArrays: true
}
}]
I couldn't test it on playground because of geo index. So might require some minor fix. If anyone can make it work on playground heres the link
https://mongoplayground.net/p/aROX976gYzC
I have an user collection:
{
"name": "David",
"age": 20,
"addresses": [
{
"radius": 10000,
"location": {
"type": "Point",
"coordinates": [106.785299, 20.999999]
}
},
{
"radius": 30000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
}
}
]
}
Each user will have one or more address. I want to calculate the distance between these addresses with a point, then using calculated distance to compare with radius of each address. If distance < radius then keep address else remove address from addresses list. I am using below query:
db.collection.aggregrate(
{
"$geoNear": {
"near": {"type": "Point", "coordinates": [ 105.823620, 21.006047 ]},
"distanceField": "distance",
"key": "addresses.location"
}
}
)
But this query only return the distance of nearest address, like this:
{
"name": "David",
"age": 20,
"addresses": [
{
"radius": 10000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
}
},
{
"radius": 30000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
}
}
],
"distance": 110000 // <--- distance is added here, just for nearest addrest
}
My expected result:
{
"name": "David",
"age": 20,
"addresses": [
{
"radius": 10000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
},
"distance": 2000``// <------ add distance here for each addesss`
},
{
"radius": 30000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
},
"distance": 30000 // <------ add distance here for each addesss
}
]
}
So next stage I can compare distance with radius and keep proper adddress
Anybody know how to do it ? thanks
You need to store each address in an individual document:
{
"_id" : ObjectId("5ec77d127df107cd889d567d"),
"name" : "David",
"age" : 20,
"addresses" : {
"radius" : 10000,
"location" : {
"type" : "Point",
"coordinates" : [
105.785299,
20.979733
]
}
}
},
{
"_id" : ObjectId("5ec77f7843732e8f9a63bf67"),
"name" : "David",
"age" : 20,
"addresses" : {
"radius" : 30000,
"location" : {
"type" : "Point",
"coordinates" : [
105.795299,
20.989733
]
}
}
}
Now, we perform $geoNear and $group stages:
db.user.aggregate([
{
"$geoNear": {
"near": {
"type": "Point",
"coordinates": [
105.823620,
21.006047
]
},
"distanceField": "distance",
"key": "addresses.location"
}
},
{
"$group": {
"_id": "$name",
"name": {
"$first": "$name"
},
"age": {
"$first": "$age"
},
"addresses": {
"$push": {
"$mergeObjects": [
"$addresses",
{
"distance": "$distance"
}
]
}
}
}
}
])
I am creating a carpooling app that offers a list of trips (Departure City -> Arrival City).
I type the desired departure and arrival coordinates. And I get back the best options
My goal is to create a mongo request that return best routes that comes closest to my departure address and my arrival address
Example:
Departure Sèvres coordinates: [ 2.210590 , 48.824169 ]
Arrival Capbreton coordinates: [ -1.431370 , 43.640080 ]
I tried to combine this with $and operator but I'm not sure that is the solution... ("Too many geoNear expressions")
db.trip.find({
$and: [
{
"departureLocation": {
$near: {
$geometry: {
type: "Point",
coordinates: [ 2.210590 , 48.824169 ]
},
}
}
},
{
"arrivalLocation": {
$near: {
$geometry: {
type: "Point",
coordinates: [ -1.431370 , 43.640080 ]
},
}
}
}
]
}, {"departure": 1, "arrival": 1})
Here my trips
[
{
"_id": "5d24d2e4ea5e2a29ea0f0e77",
"departure": "Paris",
"arrival": "Givrand",
"departureLocation": {
"type": "Point",
"coordinates": [
2.352222,
48.856613
]
},
"arrivalLocation": {
"type": "Point",
"coordinates": [
-1.88465,
46.67119
]
},
},
{
"_id": "5d3c594e6562967b8ad9e62b",
"departure": "Bordeaux",
"arrival": "Biarritz",
"departureLocation": {
"type": "Point",
"coordinates": [
-0.57918,
44.837788
]
},
"arrivalLocation": {
"type": "Point",
"coordinates": [
-1.558626,
43.48315
]
},
},
{
"_id": "5d3c59e96562967b8ad9e62c",
"departureLocation": {
"type": "Point",
"coordinates": [
2.18919,
48.810032
]
},
"departure": "Chaville",
"arrival": "Seignosse",
"arrivalLocation": {
"type": "Point",
"coordinates": [
-1.37524,
43.69011
]
},
},
{
"_id": "5d3c5a8a6562967b8ad9e62d",
"departureLocation": {
"type": "Point",
"coordinates": [
-1.553621,
47.218372
]
},
"departure": "Nantes",
"arrival": "Hossegor",
"arrivalLocation": {
"type": "Point",
"coordinates": [
-1.3952,
43.663342
]
},
}
]
Expected result :
(Chaville - Seignosse) should be placed at the first position of the result because it's the best option if I'm looking for a ride (Sèvres - Capbreton)
Unfortunately for you, there's no mongodb-only solution to resolve this as:
Only one $geonear expression can be used in a find() method.
Only the first stage of an aggregation can be a $geonear stage.
$geonear can not be used inside a $facet stage.
You have to deal with this programmatically, to run 2 separate geonear queries on your collection and find the best of the both resultsets. Before doing this, think about what is 'the best' result (closest to departure, arrival, lowest mean of both, etc...)
I am trying to fetch the documents with geometry within a certain location, however only want to return a single document per UUID. For this project, in most cases, there are many documents for each UUID that match the $near selector, hence we get many documents with the same UUID.
Can anyone assist with completing the below query so it only returns a single document per uuid (most recent "date")?
db.device.find(
{
location:
{ $near :
{
$geometry: { type: "Point", coordinates: [ -73.9667, 40.78 ] },
$minDistance: 1000,
}
}
}
)
Here's an example of the collection:
{
"_id":ObjectId("5a4f1ff0fc6ded723265e6b0"),
"uuid":"user1",
"date": "2018-01-20 11:58:29.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
},
{
"_id":ObjectId("5a62a245ce689f68245450a7"),
"uuid":"user2",
"date": "2018-01-20 11:58:07.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
},
{
"_id":ObjectId("5a62a20fce689f7a14648c62"),
"uuid":"user1",
"date": "2018-01-20 11:58:39.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
},
{
"_id":ObjectId("5a62a205ce689f7039203923"),
"uuid":"user1",
"date": "2018-01-20 11:58:49.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
},
{
"_id":ObjectId("5a62a277ce689f703a3eacb3"),
"uuid":"user2",
"date": "2018-01-20 11:58:59.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
}
When performing this kind of heavier operations, you can switch to using an aggregation pipeline.
Using this input:
{
"uuid": "user1",
"date": "2018-01-20 11:58:29.000",
"location": { "type": "Point", "coordinates":[-0.17818, 51.15609] }
},
{
"uuid": "user2",
"date": "2018-01-20 11:58:07.000",
"location": { "type": "Point", "coordinates":[2.35944, 48.72528] }
},
{
"uuid": "user1",
"date": "2018-01-20 11:58:39.000",
"location": { "type": "Point", "coordinates": [1.45414, 43.61132] }
},
{
"uuid": "user1",
"date": "2018-01-20 11:58:49.000",
"location": { "type": "Point", "coordinates":[-6.24889, 53.33306] }
},
{
"uuid": "user2",
"date": "2018-01-20 11:58:59.000",
"location": { "type": "Point", "coordinates":[-3.68248, 40.47184] }
}
Using this index:
db.device.createIndex( { location : "2dsphere" } )
This pipeline should perform what you want:
db.device.aggregate([
{ $match: { location: { $geoWithin: { $centerSphere: [ [ -0.17818, 51.15609 ], 0.1232135647961246 ] } } } },
{ $sort: { "date": -1 } },
{ $group: { _id: { uuid: "$uuid" }, users: { $push: { "uuid": "$uuid", "date": "$date", "location": "$location" } } } },
{ $project: { user: { $arrayElemAt: ["$users", 0] } } }
])
I first adapted the find/$near operator to an aggregation equivalent ($geoWithin/$centerSphere). It matches locations within 0.123 radians (488 kms (0.123*3963.2)).
I then directly sort by date, this way when documents will then be grouped by user, I will be able to easily select the first per user.
I then group by user.
And finally for each user, as I have a value produced by $group which is an array of the user documents (sorted), I just extract the first item of the array with $arrayElemAt.
This produces:
{
"_id" : { "uuid" : "user2" },
"user": {
"uuid": "user2",
"date": "2018-01-20 11:58:07.000",
"location": { "type": "Point", "coordinates": [ 2.35944, 48.72528 ] }
}
}
{
"_id": { "uuid" : "user1" },
"user": {
"uuid": "user1",
"date": "2018-01-20 11:58:49.000",
"location": { "type": "Point", "coordinates": [ -6.24889, 53.33306 ] }
}
}