Mongo with Express: what does find return - mongodb

I am developing an application in Express with Mongo, in which i have to query, whether a document exits in the collection or not. I am doing this:
var dept= req.body.dept;
var name= req.body.name;
mongoose.model('User').find({'dept': dept, 'name': name}, function(err, user){
if(err){
res.render('user/test1');
}else{
res.redirect('/');
}
});
What I want to do is to check if that document exits in the collection and upon true condition, i want to redirect to another page otherwise render the current page. But when I pass the wrong input, even then it goes to the else part and redirects it.

Well you are actually using mongoose, so there is in fact a distinction between what is returned by this specific version ( abtraction ) to what the base driver itself returns.
In the case of mongoose, what is returned is either an array of results or a null value if nothing is found. If you are looking for a singular match then you probably really want findOne() instead of a result array.
But of course the case would be a null result with either function if not returned as opposed to an err result that is not null, which is of course about specific database connection errors rather than simply that nothing was found.
The base driver on the other hand would return a "Cursor" ( or optionally considered a promise ) instead, which can either be iterated or converted to an array like mongoose does via the .toArray() method. The .findOne() method of the base driver similarly returns either a result or a null document where the conditions do not match.
mongoose.model('User').findOne({'dept': dept, 'name': name}, function(err, user){
if(err){
// if an error was actually returned
} else if ( !user ) {
// Nothing matched, in REST you would 404
} else {
// this is okay
}
});
In short, if you want to test that you actually found something, then look at the user value for a value that is not null in order to determine it matched. If it is null then nothing was matched.
So no match is not an "error", and an "error" is in fact a different thing.

Related

Fetch document by _id in express and mongodb

I am trying to create a todo list, where i am using mlab as my db provider.
tasks = db.connect().collection('tasks');
tasks.find().toArray(function (err, result) {
console.log("result", result) // giving me result
if(err) throw err;
res.render('tasks/index', {title:"Tasks",tasks:result});
})
Attached is one single shot of all routes output in console.
In the image (1) is the output of find().toArray()
(2) is get update and i am getting the id of task in the url route params.
(3) is after i run this findOne statment.
update function -> get task based on id
tasks.findOne({"_id":"59c91fbb4b262004f059f67f"}, function(err, task){
console.log(err)
console.log(task)
res.render('tasks/update', {title:"Update"});
});
I am getting null as value.(Note: I am hard coding the value for confirmation).
Let me know why i am getting null as the id matches (which you can refer from 1).
Task-2
Also, if i want to update, how shuld i send the altered values.
Regarding your first question, when matching on _id with Mongo you need to define your search criteria as an ObjectId :
tasks.findOne({"_id":ObjectId("59c91fbb4b262004f059f67f")}, function(err, task){
console.log(err)
console.log(task)
res.render('tasks/update', {title:"Update"});
});
Regarding the update, are you having some kind of trouble in particular ? The documentation has plenty of information regarding the structure of the query you're supposed to send :)

How to find and return a specific field from a Mongo collection?

Although I think it is a general question, I could not find a solution that matches my needs.
I have 2 Mongo collections. The 'users' collection and the second one 'dbInfos'.
Now, I have a template called 'Infos' and want the already existing fields in the Mongo collections to be presented to the user in input fields in case there is data in the collection. When no data is provided in the database yet, it should be empty.
So here is my code, which works fine until I want to capture the fields from the second collection.
Template.Infos.onRendered(function() {
$('#txtName').val(Meteor.user().profile.name);
$('#txtEmail').val(Meteor.user().emails[0].address);
});
These 2 work great.
But I don´t know how to query the infos from the collection 'dbInfos', which is not the 'users' collection. Obviously Meteor.user().country does not work, because it is not in the 'users' collection. Maybe a find({}) query? However, I don´t know how to write it.
$('#txtCountry').val( ***query function***);
Regarding the structure of 'dbInfos': Every object has an _id which is equal to the userId plus more fields like country, city etc...
{
"_id": "12345",
"country": "countryX",
"city": "cityY"
}
Additionally, how can I guarantee that nothing is presented, when the field in the collection is empty? Or is this automatic, because it will just return an empty field?
Edit
I now tried this:
dbInfos.find({},{'country': 1, '_id': 0})
I think this is the correct syntax to retrieve the country field and suppress the output of the _id field. But I only get [object Object] as a return.
you're missing the idea of a foreign key. each item in a collection needs a unique key, assigned by mongo (usually). so the key of your country info being the same as the userId is not correct, but you're close. instead, you can reference the userId like this:
{
"_id": "abc123",
"userId": "12345",
"country": "countryX",
"city": "cityY"
}
here, "abc123" is unique to that collection and assigned by mongo, and "12345" is the _id of some record in Meteor.users.
so you can find it like this (this would be on the client, and you would have already subscribed to DBInfos collection):
let userId = Meteor.userId();
let matchingInfos = DBInfos.find({userId: userId});
the first userId is the name of the field in the collection, the second is the local variable that came from the logged in user.
update:
ok, i think i see where you're getting tripped it. there's a difference between find() and findOne().
find() returns a cursor, and that might be where you're getting your [object object]. findOne() returns an actual object.
for both, the first argument is a filter, and the second argument is an options field. e.g.
let cursor = DBInfos.find({
userId: Meteor.userId()
},
{
fields: {
country: 1
}
});
this is going to:
find all records that belong to the logged in user
make only the country and _id fields available
make that data available in the form of a cursor
the cursor allows you to iterate over the results, but it is not a JSON object of your results. a cursor is handy if you want to use "{{#each}}" in the HTML, for example.
if you simply change the find() to a findOne():
let result = DBInfos.findOne({ /** and the rest **/
... now you actually have a JSON result object.
you can also do a combination of find/fetch, which works like a findOne():
let result = DBInfos.find({
userId: Meteor.userId()
},
{
fields: {
country: 1
}
}).fetch();
with that result, you can now get country:
let country = result.country;
btw, you don't need to use the options to get country. i've been assuming all this code is on the client (might be a bad assumption). so this will work to get the country as well:
let result = DBInfos.findOne({userId: Meteor.userId()});
let country = result.country;
what's going on here? it's just like above, but the result JSON might have more fields in it than just country and _id. (it depends on what was published).
i'll typically use the options field when doing a find() on the server, to limit what's being published to the client. on the client, if you just need to grab the country field, you don't really need to specify the options in that way.
in that options, you can also do things like sort the results. that can be handy on the client when you're going to iterate on a cursor and you want the results displayed in a certain order.
does all that make sense? is that what was tripping you up?

SAVE operation returning storage _id?

Is there a save operation in mongoose.js or mongodb api, which returns the _id of stored entry?
In mongoose the following will save the Entry.
p.save(function (err) {
if (err) return handleError(err);
else console.log('saved');
})
})
Likewise db.collection.save(document) will save document in mongodb api. But in both cases, you need to query db again for _id, which I want to avoid. As it seems ineffient.
When you instantiate an object in Mongoose, the object will have an _id already assigned (as it's assigned on the client by default).
So, if you just check:
console.log(p.id);
You'll see that it has a value before you call save.
And in the case of using the native driver for Node.JS, the document is returned in the result parameter with the _id set.
var collection = db.collection("tests");
collection.insert(p, function(err, results) {
if (err) { return; }
// results in an array, and in this case
// results[0]._id would be available
});

MongoDB Find using Array

I have a mongoDB collection which contains a list of documents which contain (among other items) a time stamp and an address for each different type of device. The device address is in hexidecimal (eg. 0x1001, 0x2001, 0x3001, etc.).
On the server side, I'm trying to query the collection to see what documents exists within a certain date range, and for a list of device
Collection.find(
{"addr": data.devices.Controls, "time": {$gte:d0, $lte:d1}},{},
function (err, docs) {
if( err|| !docs) console.log("No data found");
else {
//I've simplified the code here...
}
}
);
d0 and d1 are my start and end dates... and the data.devices.Controls is a list of device addresses. If I add the line:
console.log("Controls: " + JSON.stringify(data.devices.Controls));
I can see on the server side that it prints out a list of addresses that I'm looking for (the actual print statement looks like: Controls: ["0x1001", "0x2001", "0x3001"].)
However, this find statement doesn't seem to return any data from the query. There's no error (as I don't see the "No Data Found" message)... It just doesn't seem to return any data. What's strange is that if I specify a specific element out of the Controls array (something like data.devices.Controls[0]...), then it works fine. I can specify any element in the array and it works... but by passing an entire array in the argument, it doesn't seem to work. Does anyone know why this happens (and how to fix it)?
You need to use the $in operator to match against an array of values; like this:
Collection.find(
{"addr": {$in: data.devices.Controls}, "time": {$gte:d0, $lte:d1}}, ...

Finding an Embedded Document by a specific property in Mongoose, Node.js, MongodDB

For this app, I'm using Node.js, MongoDB, Mongoose & Express
So I have a Param Object that contains an array of Pivots, and I want to read certain data from the pivots as outlined below
---in models.js-------------------------
var Pivot = new Schema({
value : String
, destination : String
, counter : Number
});
var Param = new Schema({
title : String
, desc : String
, pivots : [Pivot]
});
------------- in main.js --------------
var Param = db.model('Param');
app.get('/:title/:value', function(req, res){
Param.findOne({"title":req.param('title')}, function(err, record){
console.log(record.pivots);
record.pivots.find({"value":req.param('value')}, function(err, m_pivot){
pivot.counter++;
res.redirect(m_pivot.destination);
});
record.save();
});
});
I know that the code works until console.log(record.pivots), since i got a doc collection with the right pivot documents inside.
However, there does not seem to be a find method to let me match an embedded document by the 'value' property defined in the schema. Is it possible to search through this array of embedded documents using .find() or .findOne() , and if not, is there some easy way to access it through mongoose?
varunsrin,
This should do it
app.get('/:title/:value', function(req, res) {
Param.findOne({'pivots.value': req.param('value'), "title":req.param('title')}},
function(err, record) {
record.pivot.counter++;
res.redirect(m_pivot.destination);
record.save();
});
});
Note the pluralization of the query to match the field name in your schema
You can querying using embedded document properties like this:
{'pivot.value': req.param('value')}}
Update in response to comment:
app.get('/:title/:value', function(req, res) {
Param.findOne({'pivot.value': req.param('value'), "title":req.param('title')}},
function(err, record) {
record.pivot.counter++;
res.redirect(m_pivot.destination);
record.save();
});
});
I solved it temporarily using a simple for loop to parse the object array as follows:
for (var i=0; i <record.pivots.length; i++){
if (record.pivots[i].value == req.param('value')){
res.redirect(record.pivots.destination);
}
}
However, I still think that Mongoose must have a simpler way of interacting with embedded documents - and this loop is somewhat slow, especially when the number of embedded documents grows large.
If anyone has any suggestions for a faster way to search this object array either in js or with a mongoose function, please post below.
the biggest problem with this is that if your req has some fields empty (that should act as wildcard), you will not find anything since mongo tries to match empty params as well, so searching for {"user":"bob", "color":""} is not the same as {"user":"bob", "color":"red"} or {"user":"bob"}. this means that you have to first create a query object and filter out any unused parameters before you pass it in, and if you create a query object, you can no longer do something like "user.name=..." because mongo interperets this as an error since it does not first resolve the object literal into a string.
Any ideas on this problem?
ps. You'd think it would be easy enough to make an object like:
user.name="bob"; user.color:"green"; user.signup.time="12342561"
and then just use user as a query object :/
I think you are looking for the "$in" keyword?
As in:
{a: {$in: [10, "hello"]}}
source: MongoDB Queries CheatSheet