.findOne not passing results to callback - node.js

This must be a stupid question, but I've bashed my head against the wall for long enough.
Short version: I have a query that returns a result as expected in the Mongo console, but returns no results (or errors) when used through the Mongo NodeJS package.
var weatherCacheQuery = {
'location': {
'$near': {
'$geometry': {
type: 'Point',
coordinates: [location.coordinates[0], location.coordinates[1]]
},
'$maxDistance': 50000
}
},
'retrieved': {
'$gte': moment().subtract(2, 'hours').toDate()
}
};
this.db.collection('weather').findOne(
weatherCacheQuery,
function(err, doc) {
console.log(JSON.stringify(err));
console.log(JSON.stringify(doc));
}
});
As you can see, nothing overly complex.
If I dump the query object and paste it into a findOne query in the Mongo console, it returns a single result that looks exactly as it should. In the JS, though, the two console.log()s return null.
Somewhat more weirdly, find() with the same query does return a result, but not one that I can view because JSON.stringify() complains about it being circular.
Can anyone point out the doubtlessly absurd thing I'm doing wrong?

.findOne for the node MongoDB driver takes three arguments, and none of them seem to be optional. Try:
this.db.collection('weather').findOne(
weatherCacheQuery, {},
function(err, doc) {
console.log(JSON.stringify(err));
console.log(JSON.stringify(doc));
}
});
You can also try it with a promise, chain .then to .findOne and pass the callback to that.
The docs also say that .findOne is deprecated and to use.
find().limit(1).next
You can pass the callback to that.

Related

Mongo DB query in Node.js

I want to create a query that finds the last insert that I have found.
Here is my dataset in Infos collection.
{
"_id": "5c7114339624d8dd041bae18",
"user_id": "AHK",
"gps": "gps information",
"timestamp": "2010-05-30T20:07:35.000Z",
"__v": 0
},
{
"_id": "5c7114bde3075ae0b38ec0bc",
"user_id": "AHK",
"gps": "gps information2",
"timestamp": "2010-05-30T20:07:35.000Z",
"__v": 0
},
{
"_id": "5c7114c2e3075ae0b38ec0bd",
"user_id": "AHK",
"gps": "gps information3",
"timestamp": "2010-05-30T20:07:35.000Z",
"__v": 0
}
For example, I want to select the data which gps value is "gps information3".
It is the last inserted query In this DB. So I create query like below to select this.
router.get('/infos/gps/:userid/recent',function(req,res){
var temp = Info.find({user_id: req.params.userid}, function(err, info){
if(err) return res.status(500).json({error: err});
if(!info) return res.status(404).json({error: 'user not found in Info collections.'});
}).sort( {"_id": -1} ).findOne(function(err2,info2){
if(err2) return res.status(500).json({error: err2});
if(!info2) return res.status(404).json({error: 'findOne error'});
console.log(info2.user_id +" "+info2.gps+" "+info2.timestamp);
res.json(info2);
});
});
It worked.
But I don't understand the flow. I know that Nodejs is asynnchronous. And it has callback function.
As I guess, First, the find function is called, then sort function is called when the result of find function returned , and finally findOne function is called when the sort function is returned.
But I think that it isn't asynchronous.
Because I thought sort function would proceed before the results of the find function were returned.
Could you tell me what is the answer?
In addition, let me know if there is a way to make this query better.
Last, Can mongodb's _id attribute be the reference point when sorting with time?
I'm a beginner so I have too many questions. I'm sorry.
in mongoose you either:
use a query with a callback, if you pass in a callback function, Mongoose will execute the query asynchronously and pass the results to the callback
Info.find({},callbackFunc);
use a query without a callback and this allows you to chain multiple queries. At the end of the chain you add .exec(callbackFunc) to execute and pass the results to the callback
Info.find({user_id: req.params.userid}).sort({"_id": -1}).findOne().exec(callbackFunc)
the callback functions are something like this:
function callbackFunc (err,docs) {
if (err) {
console.log(err); // do something
}
if (!doc) {
// do something else
}
console.log(doc); //do the main thing
}
Frankly, I have no idea how the code you posted works, but if it does, it's definitely not supported.
https://mongoosejs.com/docs/queries.html
As to why you can sort by id and get it sorted in chronological order, that's because in MongoDB we get a timestamp for free if we define our primary key as an ObjectId. This is because the 12 byte ObjectId type contains a 4 byte time component.
http://www.syntaxsuccess.com/viewarticle/sorting-by-objectid-in-mongodb

Promise problems - .then() not getting called?

I have two Sequelize queries that I need to run in a specific order, the first creates a model and the second creates multiple models from an array. It works in that it inserts obj1 first and then the array of transactions, however the final 'then()' doesn't seem to get called... What am I doing wrong?
var fn = function addTransaction(transaction) {
return new Promise(function() {
return Transaction.create(transaction, { fields: [ 'Id', 'Name' ]});
}
};
var transactions = [...]; //array of transaction objects
Model1.create(obj1, { fields: [ 'Id', 'Name' ,'Description' ]}).then(function() {
var actions = transactions.map(fn);
var results = Promise.all(actions).then(function(data) {
return Promise.all(data.map(fn));
});
results.then(function() {
console.log("Done?"); //not getting called
});
}).catch(function(err) {
console.log(err);
});
addTransaction() is never going to resolve the promise it creates. You create an outer promise and return it, but never resolve it. You would at least have to declare a resolve argument to that callback and then call resolve() somewhere.
But, if Transaction.create() already returns a promise, then you can change addTransaction() to this:
var fn = function addTransaction(transaction) {
return Transaction.create(transaction, { fields: [ 'Id', 'Name' ]});
};
It's also unclear why you're using the same fn to process both the transactions array and the data array that results from each of those transactions. That seems a little odd from a logic point of view. Are you sure that's the correct way to write your code? For us to help you with the design of that part of the code, you'd have to explain more about what you're trying to accomplish by calling fn on both the first array of transactions and then again on the results from that.
I'm not sure if this will solve your problem exactly, but I think you are missing a return:
...
return results.then(function() {
console.log("Done?"); //not getting called
});
...
Without it the Model1.create promise would resolve before the results finish.

How to return data as JSON in Sequelize

When I make a Sequelize query it returns to me an object (or array) which I'm guessing is a Sequelize model (or array of Models (a collection type??)) but it's not documented anywhere, so I'm just guessing. I would always like the results to be JSON. Is there anything I can pass in the query to force this? I would prefer not to hand massage every result I get back to be JSON if possible.
The documentation show this to return a string:
console.log(JSON.stringify(users))
So there's some built in serialization. Right now I'm doing this, using the undocumented toJSON() method:
query().then(function(result) {
if(result.length) {
return result.toJSON();
} else {
return result.map(function(item) {
return item.toJSON();
});
}
});
which is cumbersome.
You can use raw: true in the Query however this does not always behave as you might expect, especially with associations.
query({
// ...
raw: true
}).then(function(result) {
// Result is JSON!
});
However in the case where you're using associations you may get something like this:
{
foo: true,
"associated.bar": true
}
Instead of what you might expect:
{
foo: true,
associated: {
bar: true
}
}
When you retrieve the model from the database, you can call the .get({ plain: true}) on the result and it will handle the conversion for you. You can assign the value of the function call to a variable. For example
..).then(function(user){
var _user = user.get({ plain: true});
console.log(_user); //Should be valid json object
});
Hope this helps.
If you're doing a query with which has multiple results you should expect an array to be returned. You should find that each element in the result array is a JSON object.
You should be able to access a given field like this: result[0].myfieldname

Mongoose find always returning true when it shouldn't

I’m using Mongoose.js to interface with my Mongo database. This function searches through my Location names and should be logging not found to the console instead of found, as I don't have a Location with the name !#£.
Location.find({name: "!#£"}, function(err, obj) {
console.log(obj);
if (!err) {
console.log("found");
} else {
console.log("not found");
}
});
This is what is logging out to my console:
[]
found
The expected behaviour should be for it to log not found to the console. Here's a dump of the Location model data:
[
{
"_id":"5384c421af3de75252522aa2",
"name":"London, UK",
"lat":51.508515,
"lng":-0.12548719999995228,
"__v":0,
"modified":"2014-05-27T16:58:09.546Z",
"search_count":1
},
{
"_id":"5384c766af3de75252522ab4",
"name":"Paris, France",
"lat":48.856614,
"lng":2.3522219000000177,
"__v":0,
"modified":"2014-05-27T17:12:06.990Z",
"search_count":1
},
{
"_id":"53851a213a33fe392b758046",
"name":"Zagreb, Croatia",
"lat":45.8150108,
"lng":15.981919000000062,
"__v":0,
"modified":"2014-05-27T23:05:05.306Z",
"search_count":1
}
]
The callback interface semantics are not what you think.
err means the query failed entirely due to an error like the DB being unreachable. It has no meaning with regard to whether documents matched or not
obj is an array of results, which you should name "locations" IMHO to keep things clear. If no documents match the query, this array will be empty. If some match, they will be in the array.
So there are 3 total states to consider: error, success with no matches, success with some matches.

MongoDB Upsert add to array

I'm trying to insert/update an array of strings in a mongodb document using some typescript code running in NodeJS.
The following code is typescript but I guess JS developers will get it w/o any problems:
export function addEvents(entityId: string,
events: string[] ,
callback: () => void) {
db.collection('events', function(error, eventCollection) {
if(error) {
console.error(error); return;
}
eventCollection.update({ _id: entityId }, { "$pushAll ":
{ events: events }},
function(error, result) {
if(error) {
console.error(error); return;
}
callback();
});
});
}
the document have the following structure:
{
_id : string
events : ["array","of","strings"]
}
I simply want to append an array strings at the end of the existing array for a specific _id.
I don't quite get if I should use update,save, $push ,$pushall etc.
Can someone explain?
If I understood correctly the problem is that pushAll does nothing or update returns error? Maybe copy-paste mistake in your example but I think you have typo here.
{ "$pushAll ": { events: events }}
It should be
{ $pushAll: { events: events }}
Your combination of update and $pushAll looks like the best choice for what you're doing here -- it's for appending an array to an existing array. $push is for adding an element to an array. save would involve getting the existing events array, appending to it, then saving the document.
The extra space in "$pushAll " needs to be removed. It may have quotes: "$pushAll".
Found the problem, I needed to pass "{ upsert = true }" as a third argument to the update function.
To achieve 'upsert' semantics in this case, you'd need to use $addToSet. If you have an array of values to add, you'd need to throw in the $each modifier. From mongo shell:
db.events.update(
{ _id: entityId },
{ $addToSet: { $each: events } }
)

Resources