MongoDB Conditional Projection - mongodb

I would like to conditonally leave out fields in a response.
I have an aggregation query which uses geoNear to find the nearest POI and I would like to only retreive all the information if the distance between the query point and the POI is less than 500.
Let's assume I want to leave out "someField" if the distance is less than or equal to 500.
Here is what I have come up with:
db.pois.aggregate([
{
"$geoNear": {
"near": {
type: "Point",
coordinates: [49.607857, 6.129143]
},
"maxDistance": 0.5 * 1000,
"spherical": true,
"distanceField": "distance"
}
}, {
$project: {
_id:0,
"someField": {
$cond: [{$lte: ["$distance", 500]}, 1, 0 ]
}
}
}
]).pretty()
But instead of leaving the field out of the response, this query somehow replaces the value of "distance" with 0 or 1.
I would appreciate any help.

Starting in MongoDB 3.6, you can use the variable REMOVE in aggregation expressions to conditionally suppress a field.
$$REMOVE
Query:
db.pois
.aggregate([
{
$geoNear: {
near: {
type: "Point",
coordinates: [49.607857, 6.129143]
},
maxDistance: 0.5 * 1000,
spherical: true,
distanceField: "distance"
}
},
{
$project: {
_id: 0,
someField: {
$cond: {
if: { $lte: ["$distance", 500] },
then: "$$REMOVE",
else: "$distance"
}
}
}
}
])
.pretty();

Related

mongodb - use geoNear without omitting non 2dsphere indexed rows

When using geoNear, all rows which do not have location (not a part of 2dsphere index) are omitted.
How to use geoNear to get nearest results but also show other rows that do not have location? The rows which do not have a location should be ranked lower in the results.
.collection('users')
.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [userLocArray[0], userLocArray[1]]},
distanceField: "dist.calculated",
spherical: true,
//maxDistance: matchDistance,
}
},
Example documents (In the example below, the users xyz and mmm are omitted by geoNear because they do not have location field. I just wanted them to be ranked lowest but not completely omitted.
db={
users: [
{
_id: "abc",
name: "abc",
group: 1,
location: {
type: "Point",
coordinates: [
53.23,
67.12
]
},
calculatedDist: 112
},
{
_id: "xyz",
name: "xyyy",
group: 1,
calculatedDist: 13
},
{
_id: "123",
name: "yyy",
group: 1,
location: {
type: "Point",
coordinates: [
54.23,
67.12
]
},
calculatedDist: 13
},
{
_id: "rrr",
name: "tttt",
group: 1,
location: {
type: "Point",
coordinates: [
51.23,
64.12
]
},
calculatedDist: 14
},
{
_id: "mmm",
name: "mmmm",
group: 1,
calculatedDist: 14
},
]
}
You can simply add another $match stage at the end of pipeline to choose those documents without location
db.users.aggregate([
{
"$geoNear": {
"near": {
"type": "Point",
"coordinates": [
53.23,
67.12
]
},
"distanceField": "d"
}
},
{
"$unionWith": {
"coll": "users",
"pipeline": [
{
"$match": {
"location": null
}
}
]
}
}
])
Yes, you are right that Mongo Playground cannot demonstrate 2d sphere index behaviour. But I put it here nevertheless.

How to use a field as a parameter in geospatial query on MongoDB?

this is the query I would like to perform:
db.cells.find(
{
loc: {
$near: {
$geometry: {
type : "Point",
coordinates : [ 31.0, 31.0 ]
},
$minDistance: 0,
$maxDistance: this.range
}
}
}
)
The problem is with this.range. I would like to use the field range of cells to set the max distance. This parameter can vary or maxDistance must be a fixed value for every document I try to match?
One of the workarounds to your issue is computing a distance field using $geoNear in aggregation pipeline. Then, use the computed distance field to compare with the max Distance you want(i.e. this.range in your case).
db.cells.aggregate([
{
"$geoNear": {
"near": {
"type": "Point",
"coordinates": [
31.0,
31.0
]
},
"distanceField": "dist"
}
},
{
$match: {
$expr: {
lte: [
"$dist",
"$range"
]
}
}
}
])
Mongo Playground

MongoDB: Try to get explanation on aggregation

I try to get information on an aggregate I do on MongoDB 3.2.5 using {explain:true} in order to see if my indices are used :
db.mycollection.aggregate([{
"$geoNear": {
"near": {
type: "Point",
coordinates: [2.48043, 49.14128]
},
"spherical": true,
"distanceField": "distance",
"maxDistance": 500
}
}, {
"$match": {
"date": {
$gt: new ISODate("2016-01-01T01:01:01Z")
}
}
}, {
"$sort": {
"score": -1,
"distance": 1
}
}], {
explain: true
});
As a result I only got the stages aggregate :
{
"waitedMS": NumberLong(0),
"stages": [{
"$geoNear": {
"near": {
"type": "Point",
"coordinates": [
2.48043,
49.14128
]
},
"distanceField": "distance",
"limit": NumberLong(100),
"maxDistance": 500,
"query": {
},
"spherical": true,
"distanceMultiplier": 1
}
}, {
"$match": {
"date": {
"$gt": ISODate("2016-01-01T01:01:01Z")
}
}
}, {
"$sort": {
"sortKey": {
"score": -1,
"distance": 1
}
}
}],
"ok": 1
}
I don't have any information about doc scanned, index used, etc...
Can someone help me, please ?
as per manual
The operation returns a cursor with the document that contains
detailed information regarding the processing of the aggregation
pipeline. For example, the document may show, among other details,
which index, if any, the operation used.
So there is a kind of ambiguity, but probably if you swap $geoNear with $match - explain should show something more.

$geoNear (aggregate pipeline) not returning correct documents

I am not getting the correct results returned when using $geoNear in the aggregate pipeline. The same query using a typical find() query (using $near) does in fact return the right results.
BUT, when removing the equality condition (on schedule.key), both queries return the correct data.
$geoNear using aggregate pipeline:
db.place.aggregate(
[
{
$geoNear: {
spherical: true,
near: { type: "Point", coordinates: [ 18.416145, -33.911973 ] },
distanceField: "dist"
}
},
{
$match: {
"schedule.key": { "$eq": "vo4lRN_Az0uwOkgBzOERyw" }
}
}
])
$near find query:
db.place.find(
{
"point" : {
$near: {
type: "Point",
coordinates: [ 18.416145,-33.911973 ]
}
},
"schedule.key" : {
$eq : "vo4lRN_Az0uwOkgBzOERyw"
}
})
A document in this collection looks something like this:
{
"_id" : UUID("da6ccbb1-3c7a-45d7-bc36-a5e6007cd919"),
"schedule" : {
"_id" : UUID("587de5b7-a744-4b28-baa8-e6efb5f7f921"),
"key" : "vo4lRN_Az0uwOkgBzOERyw"
},
"point" : {
"type" : "Point",
"coordinates" : [
18.425102,
-33.922153
]
},
"name" : "Cape Town"
}
I have created the appropriate index on the point field:
db.place.ensureIndex( { "point" : "2dsphere" } );
It's not the "same" query at all. There is a distinct difference in using a separate $match stage, since the "filtering" is only done "after" the "nearest resuts" are found. This means that you potentially return "less" results since the criteria is not issued in combination.
That's why there is a "query" option in $geoNear:
db.place.aggregate(
[
{
$geoNear: {
spherical: true,
near: { type: "Point", coordinates: [ 18.416145, -33.911973 ] },
distanceField: "dist",
query: {
"schedule.key": { "$eq": "vo4lRN_Az0uwOkgBzOERyw" }
}
}
}
])
Now that's the same query. Or it would be exactly the same if you used $nearSphere. Since $near does not account for the curvature of the earth in distance calcuations. $nearSphere and $geoNear does.
But the main point is combining with the "query" option, since that's the only way you truly get both criteria considered in the initial search.

MongoDB Sorting on GeoNear Results

I'm trying to sort the values in the collection "Vehicle" by the text field "condition", although it seems that the sorting is having no effect on the result set. I believe the syntax is correct, so what would be the cause of this problem?
db.Vehicle.aggregate([
{
$geoNear: {
near:[26.243640,-80.265397],
maxDistance: 2500/111.12,
query: { isActive: true, condition: {$in: ['New','Used']} },
distanceField: "distance",
limit: 10
}
},
{
$project: {condition: 1, distance: 1}
},
{
$sort: {condition: -1}
}
])