MongoDB geospatial index, how to use it with array elements? - node.js

I would like to get Kevin pub spots near a given position. Here is the userSpots collection :
{ user:'Kevin',
spots:[
{ name:'a',
type:'pub',
location:[x,y]
},
{ name:'b',
type:'gym',
location:[v,w]
}
]
},
{ user:'Layla',
spots:[
...
]
}
Here is what I tried :
db.userSpots.findOne(
{ user: 'Kevin',
spots: {
$elemMatch: {
location:{ $nearSphere: [lng,lat], $maxDistance: d},
type: 'pub'
}
}
},
},
function(err){...}
)
I get a strange error. Mongo tells me there is no index :2d in the location field. But when I check with db.userSpots.getIndexes(), the 2d index is there. Why doesn't mongodb see the index ? Is there something I am doing wrong ?
MongoError: can't find special index: 2d for : { spots: { $elemMatch: { type:'pub',location:{ $nearSphere: [lng,lat], $maxDistance: d}}}, user:'Kevin'}
db.userSpots.getIndexes() output :
{
"0" : {
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "mydb.userSpots",
"name" : "_id_"
},
"1" : {
"v" : 1,
"key" : {
"spots.location" : "2d"
},
"ns" : "mydb.usersBoxes",
"name" : "spots.location_2d",
"background" : true,
"safe" : null
}
}

For a similar geospatial app, I transformed the location into GeoJSON:
{
"_id" : ObjectId("5252cbdd9520b8b18ee4b1c3"),
"name" : "Seattle - Downtown",
"location" : {
"type" : "Point",
"coordinates" : [
-122.33145,
47.60789
]
}
}
(the coordinates are in longitude / latitude format. Mongo's use of GeoJSON is described here.).
The index is created using:
db.userSpots.ensureIndex({"location": "2dsphere"})
In my aggregation pipeline, I find matches using:
{"$match":{"location":{"$geoWithin": {"$centerSphere":[[location.coordinates[0], location.coordinates[1]], radius/3959]}}}}
(where radius is measured in miles - the magic number is used to convert to radians).

To index documents containing array of geo data MongoDB uses multi-key index. Multi-key index unwinds document to some documents with single value instead of array before indexing. So the index consider that key field as single value field not array.
Try query it without $elemMatch operator.

Related

What if some documents don't have a field that is part of an index?

A collection has an indexed involved field_A. But field_A is not required. So what happens if some documents do not have this field? Will the index still work for documents that do have this field?
Yes it works, here is a test:
db.collection.createIndex({ field_A: 1 });
for (let i = 0; i < 100; i++)
db.collection.insertOne({ field_B: i });
db.collection.stats(1024).indexSizes
{ "_id_" : 20, "field_A_1" : 20 }
You see index field_A_1 has a size of 20 kiByte. This behavior is different to most relational DBMS database where such index would have a size of zero.
The index is also used by your query, if you use the field:
db.collection.find({ field_B: 1 }).explain().queryPlanner.winningPlan;
{
"stage" : "COLLSCAN",
"filter" : {
"field_B" : {
"$eq" : 1
}
}
}
db.collection.find({ field_A: null, field_B: 1 }).explain().queryPlanner.winningPlan;
{
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"field_A" : 1
},
"indexName" : "field_A_1",
"indexBounds" : {
"field_A" : [
"[undefined, undefined]",
"[null, null]"
]
}
}
}
Yes , index will work for the documents that have the field available and indexed , but you may look on the options to create sparse or partial type of indices which add some additional optimisation in certain cases ...
P.S.
In regular indices for documents that miss the field in the index this is seen as null value ... , so if you search by field_A: null you will find those documents missing the field and those that are equal to null ...

$add,$subtract aggregation-framework in mongodb

Hi i am mentioning the sample data
///collection - test////
{
"_id" : {
"date" : ISODate("2020-02-11T17:00:00Z"),
"userId" : ObjectId("5e43e5cdc11f750864f46820"),
"adminId" : ObjectId("5e43de778b57693cd46859eb")
},
"outstanding" : 212.39999999999998,
"totalBill" : 342.4,
"totalPayment" : 130
}
{
"_id" : {
"date" : ISODate("2020-02-11T17:00:00Z"),
"userId" : ObjectId("5e43e73169fe1e3fc07eb7c5"),
"adminId" : ObjectId("5e43de778b57693cd46859eb")
},
"outstanding" : 797.8399999999999,
"totalBill" : 797.8399999999999,
"totalPayment" : 0
}
I need to structure a query which does following things-
I need to calculate the actualOutstanding:[(totalBill+outstanding)-totalPayment],
I need to save this actualOutstanding in the same collection & in the same document according to {"_id" : {"date","userId", "adminId" }}
NOTE: userId is different in both the documents.
Introduced in Mongo version 4.2+ pipelined updates, meaning we can now use aggregate expressions to update documents.
db.collection.updateOne(
{
"adminId" : ObjectId("5e43de778b57693cd46859eb")
'_id."userId" : ObjectId("5e43e73169fe1e3fc07eb7c5"),
'_id.date': ISODate("2020-02-11T18:30:00Z"),
},
[
{ '$set': {
actualOutstanding: {
$subtract:[ {$add: ['$totalBill','$outstanding']},'$totalPayment']
}
} }
]);
For any other Mongo version you have to split it into 2 actions, first query and calculate then update the document with the calculation.

Mongoose get all Elements where a precalculation matches [duplicate]

When I am firing this query on MongoDB, I am getting all the places in the proximity of 500 miles to the specified co-ordinates. But I want to know the exact distance between the specified co-ordinates and the result location.
db.new_stores.find({ "geometry": { $nearSphere: { $geometry: { type: "Point", coordinates: [ -81.093699, 32.074673 ] }, $maxDistance: 500 * 3963 } } } ).pretty()
My Output looks like:
{
"_id" : ObjectId("565172058bc200b0db0f75b1"),
"type" : "Feature",
"geometry" : {
"type" : "Point",
"coordinates" : [
-80.148826,
25.941116
]
},
"properties" : {
"Name" : "Anthony's Coal Fired Pizza",
"Address" : "17901 Biscayne Blvd, Aventura, FL"
}
}
I also want to know the distance of this place from the specified co-ordinate. I created 2dsphere index on geometry.
You can use the $geoNear aggregate pipeline stage to produce a distance from the queried point:
db.new_stores.aggregate([
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": [ -81.093699, 32.074673 ]
},
"maxDistance": 500 * 1609,
"key" : "myLocation",
"spherical": true,
"distanceField": "distance",
"distanceMultiplier": 0.000621371
}}
]).pretty()
This allows you to specify "distanceField" which will produce another field in the output documents containing the distance from the queried point. You can also use "distanceMultiplier" to apply any conversion to the output distance as required ( i.e meters to miles, and noting that all GeoJSON distances are returned in meters )
There is also the geoNear command with similar options, but it of course does not return a cursor as output.
if you have more than one 2dsphere, you should specify a "key".
MongoDB provides a $geoNear aggregator for calculating the distance of documents in a collection with GeoJson coordinates.
Let us understand it with a simple example.
Consider a simple collection shops
1. Create Collection
db.createCollection('shops')
2. Insert documents in shops collections
db.shops.insert({name:"Galaxy store",address:{type:"Point",coordinates:[28.442894,77.341299]}})
db.shops.insert({name:"A2Z store",address:{type:"Point",coordinates:[28.412894,77.311299]}})
db.shops.insert({name:"Mica store",address:{type:"Point",coordinates:[28.422894,77.342299]}})
db.shops.insert({name:"Full Stack developer",address:{type:"Point",coordinates:[28.433894,77.334299]}})
3. create GeoIndex on "address" fields
db.shops.createIndex({address: "2dsphere" } )
4. Now use a $geoNear aggregator
to find out the documents with distance.
db.shops.aggregate([{$geoNear:{near:{type:"Point",coordinates:[28.411134,77.331801]},distanceField: "shopDistance",$maxDistance:150000,spherical: true}}]).pretty()
Here coordinates:[28.411134,77.331801] is the center position or quired position from where documents will be fetched.
distanceField:"shopDistance" , $geoNear Aggregator return shopDistance as fields in result.
Result:
{ "_id" : ObjectId("5ef047a4715e6ae00d0893ca"), "name" : "Full Stack developer", "address" : { "type" : "Point", "coordinates" : [ 28.433894, 77.334299 ] }, "shopDistance" : 621.2848190449148 }
{ "_id" : ObjectId("5ef0479e715e6ae00d0893c9"), "name" : "Mica store", "address" : { "type" : "Point", "coordinates" : [ 28.422894, 77.342299 ] }, "shopDistance" : 1203.3456146763526 }
{ "_id" : ObjectId("5ef0478a715e6ae00d0893c7"), "name" : "Galaxy store", "address" : { "type" : "Point", "coordinates" : [ 28.442894, 77.341299 ] }, "shopDistance" : 1310.9612119555288 }
{ "_id" : ObjectId("5ef04792715e6ae00d0893c8"), "name" : "A2Z store", "address" : { "type" : "Point", "coordinates" : [ 28.412894, 77.311299 ] }, "shopDistance" : 2282.6640175038788 }
Here shopDistance will be in meter.
maxDistance -> Optional. The maximum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall within the specified distance from the center point.
Specify the distance in meters if the specified point is GeoJSON and in radians if the specified point is legacy coordinate pairs.
In the docs it says if you use legacy pairs , eg : near : [long , lat] , then specify the maxDistance in radians.
If you user GeoJSON , eg : near : { type : "Point" , coordinates : [long ,lat] },
then specify the maxDistance in meters.
Use $geoNear to get the distance between a given location and users.
db.users.aggregate([
{"$geoNear": {
"near": {
"type": "Point",
"coordinates": [ longitude, latitude]
},
"distanceField": "distance",
"distanceMultiplier": 1/1000,
"query": {/* userConditions */},
}}
]).pretty()

Mongodb aggregate project string as number

I have a mongo script which retrieves a value from an array and creates a new document. However, the value which it retrieves is a string. I need the value to be added to the new document as a number instead of a string because it is read by a graphing engine which ignores the value if it is a string.
From the script below, it is "value": {$arrayElemAt: ["$accountBalances", 1]} which needs to be a number instead of a string. Thanks.
db.std_sourceBusinessData.aggregate(
{ $match : {objectType: "Account Balances"}},
{ $project: {_id: 1,entity_ID: 1,objectOrigin: 1,accountBalances: 1}},
{ $unwind: "$accountBalances" },
{ $match: {"accountBalances": "Sales"}}
,
{$project: {
_id: 1
, "value": {$arrayElemAt: ["$accountBalances", 1]}
,"key": {$literal: "sales"}
,"company": "$entity_ID"
,"objectOrigin" : "$objectOrigin"
}}
,{$out: "entity_datapoints"}
)
This is what I currently get:
{
"_id" : ObjectId("5670961f910e1f54662c1d9d"),
"objectOrigin" : "Xero",
"Value" : "500.00",
"key" : "grossprofit",
"company" : "e56e09ef-5c7c-423e-b699-21469bd2ea00"
}
what I want is:
{
"_id" : ObjectId("5670961f910e1f54662c1d9d"),
"objectOrigin" : "Xero",
"Value" : 500.0000000000000,
"key" : "grossprofit",
"company" : "e56e09ef-5c7c-423e-b699-21469bd2ea00"
}

MongoDB query using $near not working when nested

I have several documents in my Articles collection. Every document has a location value and some extra data. The location value looks like this:
"loc" : {
"type" : "Point",
"coordinates" : [
4,
54
]
}
I can build an index by executing the following command:
db.articles.ensureIndex({loc:"2dsphere"});
And I can query documents based on their location and a $maxDistance with the following query:
db.articles.find({ loc : { $near : {$geometry : {type : "Point" , coordinates : [4, 54] }, $maxDistance : 1000 } } });
This works perfectly!
However when I change the location of my "loc" object in my document, my query always returns zero results. My document should look like this (This is a minimized version):
{
"articledata" {
"content": {
"contact": {
"loc" : {
"type" : "Point",
"coordinates" : [
4.1,
54
]
}
}
}
}
}
When I rebuild my index query:
db.articles.ensureIndex({"articledata.content.contact.loc":"2dsphere"});
and execute my query again after changing my 'loc' location in the document:
db.articles.find({ "articledata.content.contact.loc" : { $near : {$geometry : {type : "Point" , coordinates : [4, 54] }, $maxDistance : 10000 } } });
There are no results.
It's probably some stupid mistake but I really can't find the problem...
Is there anyone who can help me out?
Thanks in advance!

Resources