$near operator on a nested object list item - mongodb

I have mongo db with a collection of objects with a nested object list (events) like this:
{
"_id" : ObjectId("59db84093f2fba2bf0bcfa90"),
"progressStatus" : "NOT_STARTED",
"events" : [
{
"issueDate" : ISODate("2017-10-09T00:00:00.000Z"),
"eventType" : "xyz",
"location" : {
"point" : {
"type" : "Point",
"coordinates" : [
25.6011977000001,
45.6579755
]
}
},
"cancelled" : false,
}
]
}
Trying to make a query using $near or $nearSphere operator on events.$.location:
{
"events":{
"$elemMatch":{
"eventType":"xyz",
"$and":[
{
"cancelled":false
},
{
"location.point":{
"$nearSphere":{
"$geometry":{
type:"Point",
coordinates:[
25.601198,
45.657976
]
},
"$maxDistance":20.4
}
}
}
]
}
}
}
This query gives me an error:
Error: error: {
"waitedMS" : NumberLong(0),
"ok" : 0,
"errmsg" : "geoNear must be top-level expr",
"code" : 2
}
How should it be done?

neptune, this happens because geospatial queries with $nearSphere uses geoNear command to fetch documents, and the geoNear expression (when the command is run) needs to be at the top level on query. In your example, it happens to be at low levels (within elemMatch), raising an error when constructing the call to geoNear.
As I understand, your 2dsphere index is on "events.location.point", right ?
So, you can make this query like:
db.sample6.find({"events": {$elemMatch: {$and: [{eventType: "xyz"},{cancelled: false}]}}, "events.location.point": {"$nearSphere": {"$geometry": {type: "Point", coordinates: [25.601198, 45.657976]}, "$maxDistance": 20.4}}})
or just
db.sample6.find({"events.eventType": "xyz", "events.cancelled": false, "events.location.point": {"$nearSphere": {"$geometry": {type: "Point", coordinates: [25.601198, 45.657976]}, "$maxDistance": 20.4}}})
This last one I think is more wise, since it has already AND behaviour, without the need of $and use. Also, if it's possible, it would be cleaner and less confusing (since these queries have a lot of brackets and parentheses) to construct the geospatial info right on "location" field (if it only contains the coordinates).

Related

What is the correct way to query this document? (If the index is correct)

I've a BigChainDB docker container running in my machine and I'm trying to store and retrieve geospatial data.
I've created through the MongoDB interface a 2dsphere index "location" in the "metadata" collection.
I've checked with the command:
db.people.getIndexes()
And I think that everything it's ok, in fact the result is this:
{
"v" : 2,
"key" : {
"loc" : "2dsphere"
},
"name" : "loc_2dsphere",
"ns" : "bigchain.metadata",
"2dsphereIndexVersion" : 3
}
The document that I've inserted to try some spatial queries is (this is the result of a db.metadata.findOne() query):
{
"_id" : ObjectId("5ccab10a2ce1b70022823a0f"),
"id" : "752ee9abccf83c7fd25d86c9a7d12229ae292fa27544f6881f1dbf97ccd8b413",
"metadata" : {
"location" : {
"type" : "Point",
"coordinates" : [
22.170872,
113.578749
]
}
}
}
But when I use this spatial query nothing is retrieved:
db.metadata.find(
{
"metadata": {
"location": {
$near: {
$geometry: {
type: "Point" ,
coordinates: [ 22 , 113 ]
},
}
}
}
})
I'm doing anything wrong, or is there the possibility that the index doesn't work?
There are a couple of issues here.
The first is that the index is on the field loc whereas your query is querying metadata.location.
If you try creating a 2dsphere index on metadata.location you will see the 2nd error:
"errmsg" : "invalid point in geo near query $geometry argument: { type: \"Point\", coordinates: [ 22.0, 113.0 ] } longitude/latitude is out of bounds, lng: 22 lat: 113",
This error shows that the GEOJSON point defined in your document is invalid, as the latitude value of 113 is outside the acceptable range of [-90, 90].
You would need to correct the data to be valid GEOJSON before indexing.

Mongodb $near returning error

I have a basic document, like so:
{
"_id" : ObjectId("5760fe623f6d3ad25e387ffc"),
"type": 5,
"product" : {
"location" : {
"geometry" : [ 153.39999999999998, -28.016667 ],
"name" : "Gold Coast QLD, Australia",
"id" : "ChIJt2BdK0cakWsRcK_e81qjAgM"
}
}
}
I am trying to query the location using the $near method provided by Mongodb.
This is my query:
db.posts.find({
'product.location.geometry': {
$near: [ 153.39999999999998, -28.016667 ]
}
})
Within the Mongodb documentation, it states that:
To specify a point using legacy coordinates, $near requires a 2d index
and has the following syntax:
{
$near: [ <x>, <y> ],
$maxDistance: <distance in radians>
}
It even gives this example on their site:
db.legacy2d.find({
location : { $near : [ -73.9667, 40.78 ], $maxDistance: 0.10 }
})
This is the error it is producing:
Error: error: {
"waitedMS" : NumberLong(0),
"ok" : 0,
"errmsg" : "error processing query: ns=mytestnodedb.postsTree: GEONEAR field=product.location.geometry maxdist=1.79769e+308 isNearSphere=0\nSort: {}\nProj: {}\n planner returned error: unable to find index for $geoNear query",
"code" : 2
}
I am unable to identify anything that is wrong with my query. Mongo states that the $near must be longitude followed by latitude, which I am definitely doing. I am purposefully leaving out $maxDistance since Mongo states that it will return results sorted from nearest to farthest.
Well error is pretty much self explainatory. Query requires 2d index which it can't find.
I'd create index as:
db.collection.createIndex({"product.location.geometry":"2d"})
Now if I run your query on sample data, I get
{
"_id" : ObjectId("5760fe623f6d3ad25e387ffc"),
"type" : 5.0,
"product" : {
"location" : {
"geometry" : [
153.39999999999998,
-28.016667
],
"name" : "Gold Coast QLD, Australia",
"id" : "ChIJt2BdK0cakWsRcK_e81qjAgM"
}
}
}

MongoDB: Using $geoIntersects or $geoWithin with $near in one query

I would like to query for all documents that have a polygon that a point is contained in and then for that result set, order it based on closeness of that point to the location of the document.
So imagine I have a database of friends because I'm just that cool, and would like to see which friends are within the my range and would be willing to come play. (each friend has a play-date polygon which is the range they are willing to travel for a play-date)
For all matches I would like to them proceed to see which friend I should call to come based on his actual address and its distance to my point (which is my address) so that I can determine if I am ok with them coming from far away. (lets say 300 meters)
So far I have below a query to find polygons that my point is contained within but I do not know how to include the $near operator of mongodb
For JSON:
{
"_id" : "objid",
"FRIEND_NAME" : "Bobby",
"GEOMETRY" : {
"type":"Polygon",
"coordinates":[[
[-73.98779153823898,40.718233223261],
[-74.004946447098,40.723575517498],
[-74.006771211624,40.730592217474],
[-73.99010896682698,40.746712376146],
[-73.973135948181,40.73974615047701],
[-73.975120782852,40.736128627654],
[-73.973997695541,40.730787341083],
[-73.983317613602,40.716639396436],
[-73.98779153823898,40.718233223261]
]]},
"FRIEND_POSITON" : {"lon" : -73.992188, "lat" : 40.729359 }
}
This works:
db.friends.find({
"PLAYDATE_RANGE":{
"$geoIntersects":{
"$geometry":{
"type":"Point",
"coordinates":[-73.98652, 40.752044]
}
}
}
})
This does not:
db.friends.find([
{
"PLAYDATE_RANGE":{
"$geoIntersects":{
"$geometry":{
"type":"Point",
"coordinates":[-73.98652, 40.752044]
}
}
}
},
{
"FRIEND_POSITON":{
"$geoNear":{
"near":{
"type":"Point",
"coordinates": [-73.98652, 40.752044]
},
"maxDistance":300
}
}
}
])
Please help me with the query above that does not work.
This requires an aggregate pipeline. As per mogodb doc for $geoNear, You can only use $geoNear as the first stage of a pipeline. The aggregate function has an entry for an additional query which is where the polygon query will be used to narraw down results based on inclusion in the PLAYDATE_RANGE field of the document.
db.friends.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [-73.98652, 40.752044] },
maxDistance: 300,
distanceField: "friends.calculated_distance",
query: {
"PLAYDATE_RANGE": {
"$geoIntersects": {
"$geometry": {
"type": "Point",
"coordinates":[-73.98652, 40.752044]
}
}
}
},
spherical: true
}
}
])
P.S. note that only one geospatial index can be used so put it on the FRIEND_POSITION field. If adding a 2sphere index that requires a correctly formed GeoJSON value, specifically,
"FRIEND_POSITION" : { "type" : "Point", "coordinates" : [ -73.992188, 40.729359 ] }
So the document should look like:
{
"_id" : "objid",
"FRIEND_NAME" : "Bobby",
"GEOMETRY" : {
"type": "Polygon",
"coordinates":[[
[-73.98779153823898,40.718233223261],
[-74.004946447098,40.723575517498],
[-74.006771211624,40.730592217474],
[-73.99010896682698,40.746712376146,
[-73.973135948181,40.73974615047701],
[-73.975120782852,40.736128627654],
[-73.973997695541,40.730787341083],
[-73.983317613602,40.716639396436],
[-73.98779153823898,40.718233223261]
]]},
"FRIEND_POSITION" : {
"type" : "Point",
"coordinates" : [ -73.992188, 40.729359 ]
}
}

$and with $nearSphere in mongodb

I have a collection having from and to point locations. Now I wish to find documents which have both, to and from locations nearby the given source and destinations.
Here's the setup:
collection: db.t2.find():
{
"_id" : ObjectId("5..4"),
"uid" : "sdrr",
"valid_upto": 122334,
"loc" : {
"from" : {
"type" : "Point",
"coordinates" : [ 77.206672, 28.543347 ]
},
"to" : {
"type" : "Point",
"coordinates" : [ 77.1997687, 28.5567278 ]
}
}
}
Indices: db.t2.getIndices():
{
"v" : 1,
"name" : "_id_",
"key" : {
"_id" : 1
},
"ns" : "mydb.t2"
},
{
"v" : 1,
"name" : "uid_1_loc.from_2dsphere_loc.to_2dsphere_valid_upto_1",
"key" : {
"uid" : 1,
"loc.from" : "2dsphere",
"loc.to" : "2dsphere",
"valid_upto" : 1
},
"ns" : "mydb.t2"
}
Single queries for either to or from work good with the current settings give nice results. However, when I use to and from together in a single query with $and clause:
db.t2.find({
"$and" : [
{
"loc.from" : {
"$nearSphere" : [ 77.5454589,28.4621213 ],
"$maxDistance" : 0.18
}
},
{
"loc.to" : {
"$nearSphere" : [ 77.206672, 28.543347 ],
"$maxDistance" : 0.18
}
}
]
})
it throws the following error:
error: {
"$err" : "can't find any special indices: 2d (needs index), 2dsphere (needs index), for: { $and: [ { loc.from: { $nearSphere: [ 77.5454589, 28.4621213 ], $maxDistance: 0.18 } }, { loc.to: { $nearSphere: [ 77.206672, 28.543347 ], $maxDistance: 0.18 } } ] }",
"code" : 13038
}
I suppose the data has been indexed as evident from getIndices(), but still its unable to find indices! Where is the problem then and how can I fix it to have effect of a $and-ed operation?
The error appears to be present from a MongoDB 2.4 version where there indeed was a bug that would not allow a $near type of query within and $and operation that accessed another field.
But your particular problem here is that you just cannot do this.
The code and comments to test this can be vied on GitHub but essentially:
// There can only be one NEAR. If there is a NEAR, it must be either the root or the root
// must be an AND and its child must be a NEAR.
size_t numGeoNear = countNodes(root, MatchExpression::GEO_NEAR);
if (numGeoNear > 1) {
return Status(ErrorCodes::BadValue, "Too many geoNear expressions");
}
So that is an error that would be emitted from MongoDB 2.6 you tried to do this.
A brief look at all the surrounding code within the method will show you that "geo" queries are not alone in this and the other "special" index type of "text" is included in the same rules.
Part of the reason for this is the $meta "scoring" that is required, as in this case is $maxDistance. There really is no valid way to combine or discern which value would actually apply in combined results such as this.
On a bit more of a technical note, the other issue is with being able to "intersect" indexes in a query such as this. The required fuzzy matching makes this a very different prospect to something like the basic "Btree" index intersection.
For now at least, your best approach is to perform each query by itself and manually "union/intersect" your results in code, with of course your own tagging as to which results are for your origin and which are for your destination.
This was a known issue in version 2.4 and prior of MongoDB, fixed in version 2.5.5:
https://jira.mongodb.org/browse/SERVER-4572
Core ServerSERVER-4572 Geospatial index cannot be used in $and
criteria of a query?
Should be fixed as of 2.6 - if you're running 2.4 or previous I'd upgrade, if you're running 2.6.X I'd report it as a bug.

mongodb 2.4.9 $geoWithin query on very simple dataset returning no results. Why?

Here is the output from my mongodb shell of a very simple example of a $geoWithin query. As you can see, I have only a single GeoJson Polygon in my collection, and each of its coordinates lies within the described $box. Furthermore, the GeoJson seems valid, as the 2dsphere index was created without error.
> db.Townships.find()
{ "_id" : ObjectId("5310f13c9f3a313af872530c"), "geometry" : { "type" : "Polygon", "coordinates" : [ [ [ -96.74084500000001, 36.99911500000002 ], [ -96.74975600000002, 36.99916100000001 ], [ -96.74953099999998, 36.99916000000002 ], [ -96.74084500000001, 36.99911500000002 ] ] ] }, "type" : "Feature" }
> db.Townships.ensureIndex( { "geometry" : "2dsphere"})
> db.Townships.find( { "geometry" : { $geoWithin : { "$box" : [[-97, 36], [-96, 37]] } } } ).count()
0
Thanks for any advice.
From documentation:
The $box operator specifies a rectangle for a geospatial $geoWithin query. The query returns documents that are within the bounds of the rectangle, according to their point-based location data. The $box operator returns documents based on grid coordinates and does not query for GeoJSON shapes.
If you insert this document...
db.Townships.insert(
{ "geometry" : [ -96.74084500000001, 36.99911500000002 ],
"type" : "Feature"
})
...your query will found it (but without index support).