I'm using CosmosDB with MongoDb API and I'd like to use $nearSphere to find Documents.
Here is the exemple of one document I have in my collection "locations":
{
"_id": {
"$oid": "60523b3dd72e4d4aa21f473b"
},
"data": [{
"Indicateur": "pr50mm",
"Value": 0,
"score": 0,
"Exposure": "low"
}],
"Domain": "EU-CORDEX",
"GCMS": "ICHEC-EC-EARTH",
"RCMS": "RACMO22E",
"Scenario": "rcp85",
"Horizon": "Medium (1941-1970)",
"location": {
"type": "Point",
"coordinates": [26.32, 27.25]
}
}
I would like to find the Document with location the nearest from [26, 27].
When I execute the following request, it returns nothing. However, this same command works fine we a MongoDB database : It returns the documents order by distance with point with coordonates [26, 27].
db.locations.find({
'location': {
$nearSphere: {
$geometry: {
type: 'Point',
coordinates: [
26, 27
]
}
}
}
})
Do you know how I can make it work for Azure CosmosDB?
Thank you in advance.
It looks like for Azure CosmosDB I have to specify location.coordinates in my query. Here is the working query:
db.locations.find({
'location.coordinates': {
$nearSphere: {
$geometry: {
type: 'Point',
coordinates: [
26, 27
]
}
}
}
})
This exemple about $nearSphere helped me: https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/cosmos-db/mongodb-feature-support.md
I hope this will help other people!
Related
I'm working with mongodb aggregations using mongoose and a I'm doubt what am I doing wrong in my application.
Here is my document:
{
"_id": "5bf6fe505ca52c2088c39a45",
"loc": {
"type": "Point",
"coordinates": [
-43.......,
-19......
]
},
"name": "......",
"friendlyName": "....",
"responsibleName": "....",
"countryIdentification": "0000000000",
"categories": [
"5bf43af0f9b41a21e03ef1f9"
]
"created_at": "2018-11-22T19:06:56.912Z",
"__v": 0
}
At the context of my application I need to search documents by GeoJSON, and I execute this search using geoNear. Ok it works fine! But moreover I need to "match" or "filter" specific "categories" in the document. I think it's possible using $match but certainly I'm doing the things wrong. Here is the code:
CompanyModel.aggregate(
[
{
"$geoNear": {
"near": {
"type": "Point",
"coordinates": [pageOptions.loc.lng, pageOptions.loc.lat]
},
"distanceField": "distance",
"spherical": true,
"maxDistance": pageOptions.distance
}
},
{
"$match": {
categories: { "$in": [pageOptions.category] }
}
}
]
).then(data => {
resolve({ statusCode: 200, data: data });
}).catch(err => {
console.log(err);
reject({ statusCode: 500, error: "Error getting documents", err: err });
})
pageOptions:
var pageOptions = {
loc: {
lat: parseFloat(req.query.lat),
lng: parseFloat(req.query.lng)
},
distance: parseInt(req.query.distance) || 10000,
category: req.params.category || ""
}
If I remove $match I get all the documents by location, but I need to filter specific categories... I don't believe that I need to filter it manually, I believe it can be possible with aggregation functions...
So anyone can help me with this mongoose implementation?
Thanks for all help
In MongoDB you need to make sure that data type in your document matches the type in your query. In this case you have a string stored in the database and you're trying to use ObjectId to build the $match stage. To fix that you can use valueOf() operator on pageOptions.category, try:
{
"$match": {
categories: { "$in": [pageOptions.category.valueOf()] }
}
}
I'm getting crazy to return closest Venues from a specific point using MongoDB. It is the first time I work on it so I'm totally new to this practice.
What I did at the beginning is to create a 2DIndex of my Venue collection.
Now I'm trying to get Venues in a range of 500 meters from a specific point and the code is this:
Venue.find({ location:
{
$near: [ 52.3835443 , 4.8353073 ],
$maxDistance: 0.5 / 6371
}
}, function (err, venues) {
return venues;
});
Unfortunately it return all documents.
The Venue Model has the field for location like this:
"location": {
"type": {
"type": "string"
},
"coordinates": [{ "type": "Number" }]
}
And all my Venues are like this:
{
"name": "name",
"address": "address",
"location": {
"type": "Point",
"coordinates": [50.1981668, 7.9943994999]
}
}
I also tried using $geoNear but I always receive all documents and not only those in 500 meters distance.
EDIT:
Mongo version is 3.2;
index:
{
"v": 1,
"key": {
"location": "2dsphere"
},
"name": "location_2dsphere",
"ns": "mydb.Venue",
"2dsphereIndexVersion": 2
}
document as wrote above:
{
"name": "A name",
"address": "An address",
"location": {
"type": "Point",
"coordinates": [50.1981668, 7.9943994999]
}
}
The $maxDistance operator constrains the results of a geospatial $near or $nearSphere query to the specified distance. The measuring units for the maximum distance are determined by the coordinate system in use. For GeoJSON point object, specify the distance in meters, not radians. ref here
When I was executing this query I got:
planner returned error: unable to find index for $geoNear query
so added **$geometry ** into query body
Venue.find({ location:
{
$near: {
$geometry : {
type : "Point" ,
coordinates : [ 52.3835443 , 4.8353073 ]},
$maxDistance : 500}}}
}, function (err, venues) {
return venues;
});
I have a places collection that store location plainly as
place = {
name : "",
latitude: "",
longitude:""
}
Is there any way using mongo shell or spring data mongo where I can query places like this :
select all places with coordinates(places.longitude, place.latitude) near a point(x,y) and within a distance z . Something like:
db.places.find( {
{
"type" : "Point",
"coordinates" : [
places.longitude,
places.latitude
]
}:
{ $geoWithin:
{ $centerSphere: [ [ x, y ] ,z / 3963.2 ]
}
}
})
Or will I have to modify my collection to
place = {
name : "",
"loc" : {
"type" : "Point",
"coordinates" : [
longitude,
latitude
]
}
}
You really should change your data. MongoDB supports either a legacy coordinate pairs format or GeoJSON for geopatial indexes and queries only. You cannot use different fields for the data or "transform" in any way, as the supported field format is required by the "index" that is necessary for operations using $near or $nearSphere.
Best to to the transformation in the shell, since writing other API code for a "one off" operation is unnecesesary. And yes moving forward you really should be using the GeoJSON format:
var bulk = db.places.initializeUnorderedBulkOp(),
count = 0;
db.places.find().forEach(function(doc) {
bulk.find({ "_id": doc._id }).updateOne({
"$set": {
"location": {
"type": "Point",
"coordinates": [parseFloat(doc.longitude),parseFloat(doc.latitude)]
}
},
"$unset": { "latitude": "", "longitude": "" }
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.places.initializeUnorderedBulkOp();
}
});
if ( count % 1000 !=0 )
bulk.execute();
Now the data is fixed and compatible with an index, create the index. What makes sense here with GeoJSON data is a "2sphere" index:
db.places.createIndex({ "location": "2dsphere" })
After that then you can query on the document as normal:
db.places.find({
"location": {
"$geoWithin": {
"$centerSphere": [ [ x, y ] ,z]
}
}
})
I should also note that a $centreSphere operation in a $geoWithin actually works out to be the same operation as $nearSphere with the $maxDistance modifier. The exception being that the latter should both process "faster" as well as produce "ordered" results for the "nearest" locations, which is something $geoWithin does not do:
db.places.find({
"$nearSphere": {
"$geometry": {
"type": "Point",
"coordinates": [x,y]
},
"$maxDistance": z
}
})
The only way you can do this on your existing data is for a $geoWithin only. This is because that operation does not require an geospatial index, so you are alowed to "transform" the document first.
You can do this using the .aggregate() method and it's $project pipeline stage along with the $map operator:
db.places.aggregate([
{ "$project": {
"name": 1
"location": {
"type": "Point",
"coordinates": {
"$map": {
"input": ["A","B"],
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el", "A" ] },
"$longitude",
"$latitude"
]
}
}
}
}
}},
{ "$match": {
"location": {
"$geoWithin": {
"$centerSphere": [ [ x, y ] ,z]
}
}
}}
])
However your longitude and latitude data must be numeric already as this is something you cannot transform in the aggregation framework. And you must remember that this cannot be used for operations such as $nearSphere as the required index is not available after the initial pipeline stage.
So it can be done, but it is not advisable. It's going to add processing time, and things are going to be better, more flexible and "faster" if you fix the data and add the appropriate index instead.
Also note that all distances with GeoJSON data will be in kilometers rather than radians.
I'm trying to use $geoWithin and $centerSpehere to return a list of items within a radius, but no luck.
This is my item's schema:
var ItemSchema = new Schema({
type : String,
coordinates : []
});
ItemSchema.index({coordinates: '2dsphere'});
This is my database item that I should be seeing:
{
"_id": {
"$oid": "552fae4c13f82d0000000002"
},
"type": "Point",
"coordinates": [
6.7786656,
51.2116958
],
"__v": 0
}
This is running on the server currently just to test, the coordinates seen here will eventually be variable.
Item.find( {
coordinates: { $geoWithin: { $centerSphere: [ [ 51, 6 ], 100/6378.1 ] } }
}, function(err, items) {
console.log(items); // undefined
});
Items are always undefined, even though that coordinate is within 100Km from the other coordinate.
I get no errors in the console.
Any ideas of what's happening? Is the schema wrong?
Thanks.
The format's wrong. The GeoJSON needs to live under one field:
{
"location" : {
"type": "Point",
"coordinates": [6.7786656, 51.2116958]
}
}
See e.g. create a 2dsphere index.
I have the following document in my MongoDB database:
{
"_id": {
"$oid": "55002a7916157e6021de5708"
},
"city": "Wellington New Zealand",
"location": [
174.77623600000004,
-41.2864603
]
}
When I try to find it out by the following geospatial query I don't get it:
db.collection.find({
"location": {
"$geoWithin": {
"$box": [
[165.8694369, -52.61941849999999],
[-175.831536, -29.2313419]
]
}
}
});
Could you explain why the query above does not work?
Those coordinates are not valid since the longitude of the "upper right" is reversed from what it should be:
db.collection.find({
"location": {
"$geoWithin": {
"$box": [
[165.8694369, -52.61941849999999],
[175.831536, -29.2313419]
]
}
}
})
That returns the desired location correctly. I recommend you use something like http://geojsonlint.com/ when testing features.
Your "box" looks like this:
Which of course covers all of the earth between the latitudes "except" the desired area. So that is why it fails.