unable to get count in mongodb collection.find - node.js

I am using mongoose ODM for a application. I am trying to count the number of items returned by a model like this:
app.post("/verifyLogin",function(request,response){
var usr=request.body.username;
var pass=request.body.password;
response.send(userModel.find({$and:[{username:"admin"},{password:"admin"}]}).count());
});
But i get in return:
{
"options": {
"populate": {}
},
"_conditions": {
"$and": [
{
"username": "admin"
},
{
"password": "admin"
}
]
},
"_updateArg": {},
"op": "count"
}
I expected a number :(

One way of doing this is to use Model.count method, in your case:
userModel.count({$and:[{username:"admin"},{password:"admin"}]}, function(err, result) {
if (err) {
//handle errors
} else {
response.send({count :result});
}
});
Also: you're in node.js world - use callbacks.
The result you're getting now is the result of the count call on the Model.find() result (which, i think, is a query object), not the count of the query results.

Related

CouchDB count all documents and return a number?

We have a college project in CouchDB and I'm using node, I want to create a view that returns a number of all my documents by email.
I cannot find anything that works and I'm not sure what I'm missing, I tried a lot of different reduce functions and emit methods.
Thanks for any answers.
The documents have 2 fields, name and email
Do not use the db endpoint because the response field doc_count includes design documents along with other documents that may not have an email field.
A straight forward way to do this is with a view. The code snippet demonstrates the difference between db info doc_count and a view's total_rows using PouchDB. I'd guess there's probably more interesting uses for the index.
The design doc is trivial
{
_id: '_design/my_index',
views: {
email: {
map: function(doc) {
if (doc.email) emit(doc.email);
}.toString()
}
}
}
And the view query is very efficient and simple.
db.query('my_index/email', {
include_docs: false,
limit: 0
})
const gel = id => document.getElementById(id);
let db;
function setJsonToText(elId, json) {
gel(elId).innerText = JSON.stringify(json, undefined, 3);
}
async function view() {
// display db info
setJsonToText('info', await db.info());
// display total number or rows in the email index
const result = await db.query('my_index/email', {
include_docs: false,
limit: 0
});
setJsonToText('view', result);
}
// canned test documents
function getDocsToInstall() {
return [{
email: 'jerry#garcia.com',
},
{
email: 'bob#weir.com',
},
{
email: 'phil#lesh.com'
},
{
email: 'wavy#gravy.com'
},
{
email: 'samson#delilah.com'
},
{
email: 'cosmic#charlie.com'
},
// design doc
{
_id: '_design/my_index',
views: {
email: {
map: function(doc) {
if (doc.email) emit(doc.email);
}.toString()
}
}
}
]
}
// init example db instance
async function initDb() {
db = new PouchDB('test', {
adapter: 'memory'
});
await db.bulkDocs(getDocsToInstall());
};
(async() => {
await initDb();
await view();
})();
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb-7.1.1.min.js"></script>
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb.memory.min.js"></script>
<pre>Info</pre>
<pre id='info'></pre>
<div style='margin-top:2em'></div>
<pre>email view</pre>
<pre id='view'>
</pre>
You can use GET /{db}, which returns information about the specified database. This is a JSON object that contains the property doc_count.
doc_count (number) – A count of the documents in the specified database.
With Angular for example, this could be done with the following method:
async countDocuments(database: string): Promise<number> {
return this.http.get<any>(this.url('GET', database), this.httpOptions).toPromise()
.then(info => info['doc_count']);
}
Assumption:
Assuming that following documents are present in the Customers database:
[
{
"_id": "93512c6c8585ab360dc7f535ff00bdfa",
"_rev": "1-299289ee89275a8618cd9470733035f4",
"name": "Tom",
"email": "tom#domain.com"
},
{
"_id": "93512c6c8585ab360dc7f535ff00c930",
"_rev": "1-a676883d6f1b5bce3b0a9ece92da6964",
"name": "Tom Doe",
"email": "tom#domain.com"
},
{
"_id": "93512c6c8585ab360dc7f535ff00edc0",
"_rev": "1-09b5bf64cfe66af7e1134448e1a328c3",
"name": "John",
"email": "john#domain.com"
},
{
"_id": "93512c6c8585ab360dc7f535ff010988",
"_rev": "1-88e347af11cfd1e40e63920fa5806fd2",
"name": "Alan",
"email": "alan#domain.com"
}
]
If I understand your query correctly, then based on above data, You need below given result set.
{
"tom#domain.com": 2,
"alan#domain.com": 1,
"john#domain.com": 1
}
Solution:
In order to achieve above, Consider following design document containing a View which has Map and Reduce functions.
{
"_id": "_design/Customers",
"views": {
"by-email": {
"map": "function (doc) {
if(doc.email){
emit(doc.email, doc._id);
}
}",
"reduce": "_count"
}
},
"language": "javascript"
}
The above view function emits value of the key email of the document if the key exists in the document.
The reduce function _count is a built in reducer (provided by CouchDB) that does the counting logic.
Executing View Query:
In order to query this view, you need to: select the view function, mark reduce to be executed (as it is optional to run reduce) and set 1 as group level.
Here is how you can do it through the UI:
Result:
Here is the result given by above query:
[![result of map reduce query
Hope this helped.
For more details about other reduce functions and group level, please refer CouchDB documentation.
Cheers.

RethinkDB: how can I query nested arrays?

I have a person object in RethinkDB that looks like this:
{
"email": "user#domain.com",
"orders": {,
"id": 1,
"additionalInvoices": [
{
"description": "Bananas",
"stripeChargeId": "ch_333"
},
{
"description": "Apples",
"stripeChargeId": "ch_444"
}
]
}
}
Except no results are returned. Am I allowed to nest contains() functions? What's the best way to get the results I want?
Edit: I've also tried pluck() and describing the data structure, per this example of using nested datastructures in the RethinkDB docs - I thought this works, but it gives an empty object matching the query as the only result:
rethink.db(DATABASE).table(TABLE).pluck(
{orders: [{additionalInvoices: [{'stripeChargeId': chargeID}]}]}
).run(dbConnection, function(err, results) {
if ( err ) {
cb(err)
return;
}
toOne(results, cb)
})

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.

Issue with a simple mongo/monk findAndModify query

Just a note I am fairly new to mongo and more notably very new to using node/js.
I'm trying to write a query to insert new documents or update already existing documents in my collection.
The proposed structure of the collection is:
{ _id: xxxxxxx, ip: "xxx.xxx.xxx.xxx:xxxxxx", date: "xx-xx-xx xxxx" }
Note that my intention is a store an fixed length int for the _id rather than the internal ObjectId (is this possible/considered bad practice?). The int is guaranteed to be unique and comes from another source.
var monk = require('monk');
var db = monk('localhost:27017/cgo_schedule');
var insertDocuments = function(db, match) {
var db = db;
var collection = db.get('cgo_schedule');
collection.findAndModify(
{
"query": { "_id": match.matchId },
"update": { "$set": {
"ip": match.ip,
"date": match.date
},
"$setOnInsert": {
"_id": match.matchId,
}},
"options": { "new": true, "upsert": true }
},
function(err,doc) {
if (err) throw err;
console.log( doc );
}
);
}
This doesn't work at all however. It doesn't insert anything to the database, but it also gives no errors, so I have no idea what I'm doing wrong.
The output (for console.log (doc)) is null.
What am I doing wrong?
The Monk docs aren't much help, but according to the source code, the options object must be provided as a separate parameter.
So your call should look like this instead:
collection.findAndModify(
{
"query": { "_id": match.matchId },
"update": {
"$set": {
"ip": match.ip,
"date": match.date
}
}
},
{ "new": true, "upsert": true },
function(err,doc) {
if (err) throw err;
console.log( doc );
}
);
Note that I removed the $setOnInsert part as the _id is always included on insert with an upsert.

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