NodeJs : $nin array query in $geoNear in MongoDb doesn't work - node.js

I don't understand why my query doesn't work with MongoDb $geoNear ?
I checked this issue without success : mongodb geoNear command with filter
My environment :
NodeJs : V6.6.0
Mongoose : 4.8.6
this works
db.collection.geoNear(
coords,
{
query : { status: "1" } }, // I have the good filtered results
...
I already tested
...
query : { _id: { $in: itemIds } }, //no error and empty result
query : { "_id": "5639ce8c01f7d527089d2a74" }, //no error and empty result
query : { "_id" : 'ObjectId("5649e9f35f0b360300cad022")' }, //no error and empty result
query : { "_id" : ObjectId("5649e9f35f0b360300cad022") }, //error ObjectId not defined
...
I want this
db.collection.geoNear(
coords,
{
query : { _id: { $nin: poiIds } }, // no error but I have not the good results because I obtain all results geolocalized
...
Thank you ;-)

For this issue:
query : { "_id" : ObjectId("5649e9f35f0b360300cad022") }, //error ObjectId not defined
Try:
const ObjectID = require('mongodb').ObjectID

In my case, I did it like this:
Add index to coords in your model, then request with query.
modelSchema.index({ coords: '2dsphere' });
query = {
_id: {
$nin: poiIds
},
coords: {
$near: {
$geometry: { type: "Point", coordinates: position }
}
}
}
where position is an array [Lat, Lon].
Hope this helps. ;)

Related

mongoose query result missing field [duplicate]

This question already has an answer here:
How to add data to array in Mongoose Schema
(1 answer)
Closed 6 years ago.
I have just started using node with express framework and mongo as a database.
I created the schema like this :
var JsonSchema = new Schema({
type: String,
properties: {
OBJECTID: Number,
AREA: Number,
PERIMETER: Number,
ESYE_CODE: Number,
Descriptio: String
},
geometry: {
type: String,
coordinates: [Number, Number]
}
});
and then I query :
router.get('/mapjson/:OBJECTID', function(req, res) {
if(req.params.OBJECTID) {
Json.findOne({OBJECTID: req.params.OBJECTID }, {}, function(err, docs){
res.json(docs);
} else {
console.log("THERE WAS AN ERROR HERE!!!");
}
});
But the results I get are missing the geometry field.
Sample of results I get :
{
"_id":"57e43ec60534d33ccc13099b",
"type":"Feature",
"properties":{
"OBJECTID":212428,
"AREA":131.001991421,
"PERIMETER":49.9141344212,
"ESYE_CODE":147,
"Descriptio":"Ελληνικά"
}
}
and what I get when I query the collection from mongo shell :
db.points.findOne({'properties.OBJECTID': 212428})
{
"_id" : ObjectId("57e43ec60534d33ccc13099b"),
"type" : "Feature",
"properties" : {
"OBJECTID" : 212428,
"AREA" : 131.001991421,
"PERIMETER" : 49.9141344212,
"ESYE_CODE" : 147,
"Descriptio" : "Ελληνικά"
},
"geometry" : {
"type" : "Point",
"coordinates" : [
23.812561006040106,
38.093951650544334
]
}
}
In coordinates you can set type as Array:
geometry: {
type: String,
coordinates: Array
}

Create GeoJson from coordinates array in the same Mongodb collection

I want to modify my mongodb collection from 2d to 2dsphere.
I have this structure in my db.users:
{
"_id" : "1c6930387123e960eecd65e8ade28488",
"interests" : [
{
"_id" : ObjectId("56a8b2a72f2c2a4c0250e896"),
"coordinates" : [-3.6833, 40.4]
}
],
}
I would like to have something like this:
{
"_id" : "1c6930387123e960eecd65e8ade28488",
"interests" : [
{
"_id" : ObjectId("56a8b2a72f2c2a4c0250e896"),
"loc":{
"type":"Point",
"coordinates" : [-3.6833, 40.4]
}
}
],
}
I tried this:
db.users.update( {interests:{$elemMatch:{coordinates : { $exists: true } }}}, { $set: { 'interests.$.place':{"type":"Point", "coordinates":'interests.$.coordinates'} } }, { upsert: false, multi: false });
Obviously, it insert literally "'interests.$.coordinates'
And tried this in Node:
users.find({interests:{$elemMatch:
{coordinates :
{ $exists: true } }}} ,
{"interests.coordinates":1,"interests._id":1,"_id":0 }).forEach( function( doc ){
users.update( {interests:
{$elemMatch:
{_id : doc.interests[0]['_id'] }}},
{ $set: { 'interests.$.loc':{"type":"Point", "coordinates":doc.interests[0]['coordinates']} } },
{ upsert: false, multi: false },function(err, numberAffected, rawResponse) {
var modified=numberAffected.result.nModified;
console.log(modified)
});
});
But coordinates were inserted with mixed values.
Thoughts?
Thanks!!
I didn't test this piece of code but I think this would shed light on this issue.
users.find(
{interests:{ $elemMatch: {coordinates : { $exists: true } }}}).forEach( function( doc ){
for(var c = 0; c < doc.interests.length; c++) {
var orig = doc.interests[c].coordinates;
doc.interests[c].loc: { type:"Point", coordinates: orig };
//delete doc.interests[c].coordinates;
};
users.update({ _id: doc._id }, doc);
});
Try to iterate over all documents in your collection modifying the whole document and then update it in a single line.
Warning: If you have a huge quantity of documents in your collection that match the 'find' condition, you should use pagination (skip/limit) to avoid performance and memory issues in the first load.

Sort Embedded Documents in Array and fetch specifics

I have a mongoose model like this:
var activityItem = mongoose.Schema({
timestampValue: Number,
xabc: String,
full: Boolean,
comp: Boolean
});
var ABC = mongoose.Schema({
activity: [activityItem],
user: {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
username: String
});
I want to get the activityItem array elements that have a timestampValue less than a specific value. Also, I want to sort the activity array first according to the timestampValue
This is the code that I currently have. And it doesn't work.
UserActivity.findOne({
'user': current_user,
'activity' : {
$all: [
{
"$elemMatch": {
timestampValue: {
$lte: time
}
}
}
]
}
},
function(err, user){
})
Sample Document structure:
{
"_id" : ObjectId("56d5e88adfd14baf1848a7c6"),
"user" : ObjectId("56bf225342e662f4277ded73"),
"notifications" : [],
"completed" : [],
"activity" : [
{
"timestampValue": 1456902600000,
"xabc": "Some value",
"full": true,
"comp": false,
"_id" : ObjectId("56d5e88adfd14baf1848a7d2")
},
{
"timestampValue": 1456702600000,
"xabc": "Some other value",
"full": true,
"comp": false,
"_id" : ObjectId("56d5e88adfd14baf1848a7d3")
}
],
"__v" : 1
}
The POST call has the following params
hash: "2e74aaaf42aa5ea733be963cb61fc5ff"
time: 1457202600000
hash comes into the picture once i have the docs from mongo
time is a unix timestamp value.
Instead of returning only the elements that are less than the time value, it is returning all the array elements. I tried the aggregation framework to sort the array before querying, but couldn't get the hang of it.
Any help would be greatly appreciated.
Please try to do it through aggregation as below
ABS.aggregate([
// filter the document by current_user
{$match: {user: ObjectId(current_user)}},
// unwind the activity array
{$unwind: '$activity'},
// filter the timestampValue less than time
{$match: {'activity.timestampValue': {$lte: time}}},
// sort activity by timestampValue in ascending order
{$sort: {'activity.timestampValue': 1}},
// group by _id, and assemble the activity array.
{$group: {_id: '$_id', user: {$first: '$user'},activity: {$push: '$activity'}}}
], function(err, results){
if (err)
throw err;
// populate user to get details of user information if needed
//ABS.populate( results, { "path": "user" }, function(err, rets) {
//
//});
});
Well, it seems little bit tricky with MongoDb aggregation pipeline unless you have MongoDB 3.2, but you can definitely
achieve your result with help of map-reduce.
e.g.
MongoDB version < 3.2
var findActivities = function (time) {
db.col.mapReduce(function () {
var item = Object.assign({}, this);
delete item.activity;
item.activity = [];
for (var i = 0; i < this.activity.length; i++) {
if (this.activity[i].timestampValue <= time) {
item.activity.push(this.activity[i]);
}
}
emit(item._id, item);
}, function (k, v) {
return {items: v};
}, {
out: {"inline": true},
scope: {time: time}
}).results.forEach(function (o) {
printjson(o); // Or perform action as appropriate
});
};
Based your sample data when called findActivities(1456802600000), it will find and return only those documents matching criteria.
{
"_id" : ObjectId("56d5e88adfd14baf1848a7c6"),
"value" : {
"_id" : ObjectId("56d5e88adfd14baf1848a7c6"),
"user" : ObjectId("56bf225342e662f4277ded73"),
"notifications" : [
],
"completed" : [
],
"__v" : NumberInt(1),
"activity" : [
{
"timestampValue" : NumberLong(1456702600000),
"xabc" : "Some other value",
"full" : true,
"comp" : false,
"_id" : ObjectId("56d5e88adfd14baf1848a7d3")
}
]
}
}
MongoDB version 3.2+
db.col.aggregate([
{$project:{user:1, notifications:1, completed:1, activity:{
$filter:{input: "$activity", as: "activity", cond:{
$lte: ["$$activity.timestampValue", 1456802600000]}}}}}
])
Both solutions will have same output.

Mongoose $or query with $near doesn't seem to work

I'm trying to run an $or query but whenever I use $near in one of the $or clauses, the query returns 0 results.
Here is my code:
params = { $or: [
{ loc: { '$near': {
'$geometry': {
type : "Point" ,
coordinates : [ -0.127500 , 51.507220 ]
},
'$maxDistance': 1000
} } },
{ categories: { $in: ['Services'] } }
] };
var query = Activity.find(params).populate({path: '_place'});
query.limit(10).exec('find', function(err, items) {
...
});

Delete object from array by given _id in in Mongoose

In Mongoose model User given document look like this:
> db.users.find().pretty()
{
/* ... */
"events" : [
{
"_id" : ObjectId("537bb2faf87f9a552c3219ea"),
"message" : "Foo"
},
{
"_id" : ObjectId("537bb436c3b9a1aa2db71b7c"),
"message" : "Bar"
},
{
"_id" : ObjectId("537bb4ab2cc503e52e24d145"),
"message" : "Biz"
},
{
"_id" : ObjectId("537bb4d02cc503e52e24d146"),
"message" : "Pop"
}
]
}
Some function takes event _id as parameter and must delete object responding to this _id from MongoDB. I tried:
User
.findByIdAndUpdate(req.user._id, {
$pull: {events: {
$elemMatch: {_id: _eventId} //_eventId is string representation of event ID
}}
}, function(err) {
// ...
});
It is not working. What am I doing wrong?
Quote from SERVER-2016:
The argument to $pull is already applied to each element of the target
array, so using $elemMatch is redundant in the update context.
So just execute your query without $elemMatch:
User
.findByIdAndUpdate(req.user._id, {
$pull: {events: {
_id: _eventId //_eventId is string representation of event ID
}}
}, function(err) {
// ...
});

Resources