geoNear not returning any results - node.js

I'm trying to use geoNear to return documents in my collection that have latitude and longitude coordinates, but not getting any results even though there are documents in the collection that have latitude and longitude coordinates. I'm using mlab to store my data, for example, here is a document in my collection.
var trucker = db.collection('trucker');
{
"_id": {
"$oid": "581e82d00f192a694bb56679"
},
"latitude": 77.0551642746301,
"longitude": 10.825842664395019,
"loc": [
77.0551642746301,
10.825842664395019
],
}
I have the coordinates in two different ways, because I was trying to see if geoNear would pick up the location, and return the result, but still can't find it. This is how I'm using geoNear currently, to find results
exports.geoNear = function (lat, lon ,res, next)
{
console.log("Inside geoNear");
var job = {};
job.lon = JSON.parse(lon);
job.lat = JSON.parse(lat);
console.log(job.lon);
console.log(job.lat);
trucker.geoNear([job.lon, job.lat], function(err, success){
if(success) {
res(success);
} else {
console.log('Response error' + err);
}
});
};
app.get('/geoFind', function(req, res) {
AM.geoNear('48.99759239999999', '-123.0683089', function(o){
res.send(200,o);
});
});
I am getting this result when visiting localhost:8000/geoFind:
{
"waitedMS": 0,
"results": [],
"stats": {
"nscanned": 0,
"objectsLoaded": 0,
"maxDistance": 0,
"time": 0
},
"ok": 1
}
Now, I have tried many different ways such as the following but get, Response errorMongoError: 'near' field must be point with the following:
trucker.createIndex( { loc : "2dsphere" } )
trucker.geoNear({loc: {type: "Point", coordinates: [job.lon,job.lat]}}, {spherical: true}, function(err, success) {
if(success){
res(success);
} else {
console.log('Response error '+err + job.lon +':' + job.lat);
}
});
Now, I'm wondering how do I use geoNear to return documents with latitude and longitude coordinates? Does latitude and longitude need to be stored in the database a specific way? I tried db.command() as well with geoNear and I always get db.command is not a function, same with trucker.command or trucker.db.db.command or trucker.db.command (anything with command to get data from the database which I think is because I'm using mongoose not mongoclient, because my database it set up with mlab). Nothing I've tried has worked which is why I'm now asking this question as I've tried just about every way imaginable to get results from my database using geoNear.

I got back at it this morning and did some more testing because it didn't make sense to me why I was not getting any results, so I created a brand new collection called location, and inserted 1 document to test and it worked, then 2 documents that look like the following and that worked too:
{
"_id": {
"$oid": "581cc2f430c34502e36eb148"
},
"truckerID": "b233a9eaaedc63730e71a8b542606ee82e0aa5e5",
"name": "Justin",
"email": "Justin#gmail.com",
"company": "Justins Shipping",
"user": "justin1",
"pass": "D6Mvu6rUur758f37eac7010958c14557bb4df9871a",
"phone": "1234567890",
"location": [
-73.9928,
40.7193
]
}
{
"_id": {
"$oid": "581cc2f530c34502e36eb158"
},
"truckerID": "b233a9eaaedc63731e72a8b542606ee82e0aa7a6",
"name": "Alan",
"email": "Alan#gmail.com",
"company": "Alans Shipping",
"user": "alan1",
"pass": "D6Mvu6fUur758f37eac7010958c14557bb4df9872c",
"phone": "1234567890",
"location": [
-122.4194155,
37.7749295
]
}
Apparently, location does have to be in the format,
location: [longitude, latitude]
or geoNear will not be able to find your document.
The really interesting finding to me, was that if you have a collection, with 10 documents, and there are documents in your collection that don't have:
location: [longitude, latitude]
geoNear will not get any results either. If even one document does not have that field, location: [longitude, latitude], geoNear will also not be able to find anything, or return any results after having done some more testing.
The following worked for me after creating those 2 new documents in a new collection for testing purposes using mongoose and mlab.
exports.geoNear = function (lon, lat ,res, next)
{
console.log("Inside geoNear");
var job = {};
job.lon = JSON.parse(lon);
job.lat = JSON.parse(lat);
console.log(job.lon);
console.log(job.lat);
locations.geoNear([job.lon, job.lat], {spherical: true}, function(err, success){
if(success) {
res(success);
} else {
console.log('Response error' + err);
}
});
}
var AM = require('./modules/account-manager');
app.get('/geoFind', function(req, res) {
AM.geoNear('-73.99279', '40.719296', function(o){
res.send(200,o);
});
});
and I got the following result:
{
"waitedMS": 0,
"results": [
{
"dis": 1.4957325341976439e-7,
"obj": {
"_id": "581cc2f430c34502e36eb148",
"truckerID": "b233a9eaaedc63730e71a8b542606ee82e0aa5e5",
"name": "Justin",
"email": "Justin#gmail.com",
"company": "Justins Shipping",
"user": "justin1",
"pass": "D6Mvu6rUur758f37eac7010958c14557bb4df9871a",
"phone": "1234567890",
"location": [
-73.9928,
40.7193
]
}
},
{
"dis": 0.6482546796756842,
"obj": {
"_id": "581cc2f530c34502e36eb158",
"truckerID": "b233a9eaaedc63731e72a8b542606ee82e0aa7a6",
"name": "Alan",
"email": "Alan#gmail.com",
"company": "Alans Shipping",
"user": "alan1",
"pass": "D6Mvu6fUur758f37eac7010958c14557bb4df9872c",
"phone": "1234567890",
"location": [
-122.4194155,
37.7749295
]
}
}
],
"stats": {
"nscanned": 24,
"objectsLoaded": 2,
"avgDistance": 0.3241274146244688,
"maxDistance": 0.6482546796756842,
"time": 6
},
"ok": 1
}
I hope this answer helps someone else down the line who is also wondering why geoNear isn't returning any results.
EDIT: After doing even more research, it was actually because the collection needs to have a geospatial index created from the get-go. I tried adding the location field to all documents in the collection but still could not get any results back. Once I removed my collection entirely in mlab called "trucker" and re-added it, I was finally able to search that index and get results.
This line is needed initially:
trucker.createIndex( { location : "2dsphere" } )
Then you can use geoNear to find users near your location such as the following:
trucker.geoNear([job.lon, job.lat], {maxDistance:5000, distanceMultiplier: 6378137, spherical: true}, function(err, success){
if(success) {
res(success);
} else {
console.log('Response error' + err);
}
});

Related

Find a specific object in array with Mongoose and Node.js

I am trying to return a specific object in an array with mongoose. My document is as follows:
{
"_id": {
"$oid": "577a9345ba1e2a1100624be7"
},
"name": "John Doe",
"password": "$2a$10$NzqAqxTRy8XLCHG8h3Q7IOLBSFCfBJ7R5JqHy1XHHYN.1h074bWJK",
"__v": 0,
"birthDate": "14.07.2016",
"academic": [
{
"about": "asdfasdf",
"to": "asdf",
"from": "asfdasdf",
"institute": "asdfasdf",
"qualification": "asdfasdf",
"_id": {
"$oid": "579111b3e68d489f1ff8b6dc"
}
}
]
}
I want to return that academic object in the list. I am passing in the institute name into the route my code is as follows:
getAcademicInstituteByName: function(req, name, cb){
User.findById(req.user.id, function (err, user) {
if(err) throw err;
if(user){
academic = user.academic.institute(name);
return cb(null, academic);
}
});
But this is not working since I am getting an error saying user.academic.institute is not a function. Any help would be greatly appreciated
user.academic.institute is an array, so you can use regular array operations to find the entry you're interested in:
var academic = user.academic.institute.filter(i => i.institute === name)
.pop();
return cb(null, academic);
academic = user.academic.institute;
this should work, though I haven't tested it.

Mongoose: Update does not work in nested array object

I have a document with the array of objects and one object contains multiple objects I want to update inner object with $set but didn't get any luck.
can anybody give me any hint so that I can resolve it?.
This is my object:
{
"_id": ObjectId("56fbfafdf86fa6161911d104"),
"site": "xyz",
"adsPerCategory": NumberInt(2),
"sampledAt": ISODate("2016-03-30T16:12:45.138+0000"),
"items": [
{
"id": "4563873",
"content": {
"title": "WATER DISTILLERS",
"body": "Perfect to save money.",
}
},
{
"id": "4563s23232873",
"content": {
"title": "Cola water",
"body": "Perfect for body.",
}
}
]
}
I want to update body.
for now, I have given single object but it can be multiple.
Here what I tried
models.Sample.update(
{
_id: samples._id
},
'$set': {
'items.0.content.body': body.description
},
function(err, numAffected) {
console.log(err);
console.log('Affected....', numAffected);
}
);
It's working fine if I put 0 but I want to make it dynamic.
Like 'items.index.content.body': body.description
Thank You.
I think you can do something like this.
models.Sample.find({ _id: ObjectId(samples._id) })
.forEach(function (doc) {
doc.items.forEach(function (element, index, array) {
items[index].content.body = body.description;
});
models.Sample.save(doc);
});

Node + Mongodb. $pull not working?

I am using Node.js and MongoDB and I'm trying to setup a DELETE route. In the function responsible for handling the delete I am using Mongo's "$pull" operator. I've looked at a couple of examples now and I don't know what I am doing wrong.
Here's a sample of how the database documents are setup
{
"_id": {
"$oid": "123abc"
},
"sleepData": [
{
"date": "03/28/2016",
"hour": "11",
"minute": "11",
"meridiem": "PM",
"feeling": "7"
},
{
"date": "03/29/2016",
"hour": "3",
"minute": "41",
"meridiem": "PM",
"feeling": "1"
},
{
"date": "03/30/2016",
"hour": "1",
"minute": "29",
"meridiem": "AM",
"feeling": "5"
},
{
"date": "03/30/2016",
"hour": "1",
"minute": "38",
"meridiem": "AM",
"feeling": "4"
},
]
}
*Note the near-duplicate data, thus the reason why my $pull query is so specific.
Here is my function for the route
module.exports.DELETE = function(req, res) {
var sleepDataToDelete = {
date: req.query.date,
hour: req.query.hour,
minute: req.query.minute,
meridiem: req.query.meridiem,
feeling: req.query.feeling
};
// next code block is what this console prints out
console.log("Deleting req.query:\n", sleepDataToDelete);
var sleepObjectId = req.query.sleepObjectId;
var sleepDataCollection = db.get().collection('sleepData');
sleepDataCollection.update(
{
_id: sleepObjectId
},
{
$pull: {
sleepData: {
date: sleepDataToDelete.date,
hour: sleepDataToDelete.hour,
minute: sleepDataToDelete.minute,
meridiem: sleepDataToDelete.meridiem,
feeling: sleepDataToDelete.feeling
}
}
},
function(err, result) {
if(err) {
console.log("err", err);
return res.status(400).end();
} else {
console.log("Count: ", result.result.n);
console.log("Deleted! :) ");
return res.status(200).end();
}
}
);
};
This is what the console.log("Deleting req.query:\n", sleepDataToDelete); prints out, which also matches the third index in the sleepData array.
Deleting req.query:
{
date: '03/30/2016',
hour: '1',
minute: '29',
meridiem: 'AM',
feeling: '5'
}
I have even tried putting the json field names in double/single quotes, but that didn't work either. The number of objects modified is 0. I have also tried reducing the "$pull {...}" query to just "date" instead of having "date", "hour", "minute", "meridiem", and "feeling." This still results in 0 modified items from the print statement.
As #BlakesSeven pointed out, I was not passing in an ObjectId in my query. So, credit goes to him. Needless to say, this solved my issue.

Mongoose multi update

I want to update multiple docs with different values.
My Database looks something like this.
[
{
"_id": 1,
"value": 50
},
{
"_id": 2,
"value": 100
}
]
This Query return an error because i'm passing an array instead of an object in the $set.
Model.update({_id: {$in: ids}}, {$set: ids.value}, {multi: true};
I want my database to look like this
[
{
"_id": 1,
"value": 4
},
{
"_id": 2,
"value": 27
}
]
Supposing you had an array of objects that you wanted to update in your collection on matching ids like
var soldItems = [
{
"_id": 1,
"value": 4
},
{
"_id": 2,
"value": 27
}
];
then you could use the forEach() method on the array to iterate it and update your collection:
soldItems.forEach(function(item)){
Model.update({"_id": item._id}, {"$set": {"value": item.value }}, callback);
});
or use promises as
var updates = [];
soldItems.forEach(function(item)){
var updatePromise = Model.update({"_id": item._id}, {"$set": {"value": item.value }});
updates.push(updatePromise);
});
Promise.all(updates).then(function(results){
console.log(results);
});
or using map()
var updates = soldItems.map(function(item)){
return Model.update({"_id": item._id}, {"$set": {"value": item.value }});
});
Promise.all(updates).then(function(results){
console.log(results);
});
For larger arrays, you could take advantage of using a bulk write API for better performance. For Mongoose versions >=4.3.0 which support MongoDB Server 3.2.x,
you can use bulkWrite() for updates. The following example shows how you can go about this:
var bulkUpdateCallback = function(err, r){
console.log(r.matchedCount);
console.log(r.modifiedCount);
}
// Initialise the bulk operations array
var bulkOps = soldItems.map(function (item) {
return {
"updateOne": {
"filter": { "_id": item._id } ,
"update": { "$set": { "value": item.value } }
}
}
});
// Get the underlying collection via the native node.js driver collection object
Model.collection.bulkWrite(bulkOps, { "ordered": true, w: 1 }, bulkUpdateCallback);
For Mongoose versions ~3.8.8, ~3.8.22, 4.x which support MongoDB Server >=2.6.x, you could use the Bulk API as follows
var bulk = Model.collection.initializeOrderedBulkOp(),
counter = 0;
soldItems.forEach(function(item) {
bulk.find({ "_id": item._id }).updateOne({
"$set": { "value": item.value }
});
counter++;
if (counter % 500 == 0) {
bulk.execute(function(err, r) {
// do something with the result
bulk = Model.collection.initializeOrderedBulkOp();
counter = 0;
});
}
});
// Catch any docs in the queue under or over the 500's
if (counter > 0) {
bulk.execute(function(err,result) {
// do something with the result here
});
}
First of all your update() query is not ok..
See documentation for this here
Model.update(conditions, update, options, callback);
Suppose your current db model contains docs like this (as you described in question as well):
[
{
"_id": 1,
"value": 50
},
{
"_id": 2,
"value": 100
}
];
and you've below array which contains objects (i.e., docs) to be modified with current db's docs to like this:
idsArray: [
{
"_id": 1,
"value": 4
},
{
"_id": 2,
"value": 27
}
];
From my experience with mongodb & mongoose, i don't think you can update all docs with single line query (that's you're trying to do).. (P.S. I don't know about that so I am not sure to this..)
But to make your code work, you will be doing something like this:
Idea: Loop over each doc in docs i.e, idsArray and call update() over it..
So, Here's code to this:
idsArray.forEach(function(obj) {
Model.update({_id: obj._id}, {$set: {value: obj.value}});
});
In above code, I am supposing you've _id values in db docs as they 're written above (i.e, "_id": 1).. But if they're like this "_id": ObjectId('1')
[
{
"_id": ObjectId('1'),
"value": 50
},
.....
.....
]
then you'll be need to convert _id to ObjectId(obj._id) in update() query..
so for that you'll be doing like this.
var ObjectId = require('mongodb').ObjectID;
idsArray.forEach(function(obj) {
Model.update({_id: ObjectId(obj._id)}, {$set: {value: obj.value}});
});
P.S. Just confirm it (i.e., _id) before go forward..
Hope this helps.
Cheers,
Multi update can be used only for updating multiple documents to the same value(s) or updating with the same update operation for all documents.
If you want to update to different values you have to use several update statements.

Using $in in MongooseJS with nested objects

I've been successfully using $in in my node webservice when my mongo arrays only held ids. Here is sample data.
{
"_id": {
"$oid": "52b1a60ce4b0f819260bc6e5"
},
"title": "Sample",
"team": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
],
"tasks": [
{
"task": {
"$oid": "52af197ae4b07526a3ee6017"
},
"status": 0
},
{
"task": {
"$oid": "52af197ae4b07526a3ee6017"
},
"status": 1
}
]
}
Notice that tasks is an array, but the id is nested in "task", while in teams it is on the top level. Here is where my question is.
In my Node route, this is how I typically deal with calling a array of IDs in my project, this works fine in the team example, but obviously not for my task example.
app.get('/api/tasks/project/:id', function (req, res) {
var the_id = req.params.id;
var query = req.params.query;
models.Projects.findById(the_id, null, function (data) {
models.Tasks.findAllByIds({
ids: data._doc.tasks,
query: query
}, function(items) {
console.log(items);
res.send(items);
});
});
});
That communicates with my model which has a method called findAllByIds
module.exports = function (config, mongoose) {
var _TasksSchema = new mongoose.Schema({});
var _Tasks = mongoose.model('tasks', _TasksSchema);
/*****************
* Public API
*****************/
return {
Tasks: _Tasks,
findAllByIds: function(data, callback){
var query = data.query;
_Tasks.find({_id: { $in: data.ids} }, query, function(err, doc){
callback(doc);
});
}
}
}
In this call I have $in: data.ids which works in the simple array like the "teams" example above. Once I nest my object, as with "task" sample, this does not work anymore, and I am not sure how to specify $in to look at data.ids array, but use the "task" value.
I'd like to avoid having to iterate through the data to create an array with only id, and then repopulate the other values once the data is returned, unless that is the only option.
Update
I had a thought of setting up my mongo document like this, although I'd still like to know how to do it the other way, in the event this isn't possible in the future.
"tasks": {
"status0": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
],
"status1": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
]
}
You can call map on the tasks array to project it into a new array with just the ObjectId values:
models.Tasks.findAllByIds({
ids: data.tasks.map(function(value) { return value.task; }),
query: query
}, function(items) { ...
Have you try the $elemMatch option in find conditions ? http://docs.mongodb.org/manual/reference/operator/query/elemMatch/

Resources