MongoDB find() won't find what I need - node.js

I am trying to find a document in a collection. I have been using this query structure on nearly all my collections, but for some reason this isnt working for this collection. Any help?
The only data in this collection right now
[ { _id: 581757143389e565b5cf8124,
companyProfileID: '86660a5b-7f61-4238-889d-1cc3087947b9',
url: 'sentsoftware.com',
appID: 1 } ]
Query Structure:
function getCompany(companyUrl, app, callback) {
MongoClient.connect(url, function(err, db) {
if (err) {
console.log(err);
} else {
console.log("We are connected");
}
var collection = db.collection('Companies');
collection.find({url: companyUrl, appID: app}).toArray(function (err, result) {
if (err) {
console.log(err);
callback(err);
} else if (result.length) {
console.log("found");
callback(result);
} else {
console.log("No document found");
callback(err);
}
});
});
}
I keep getting No document found. But if i were to take out the , appID: app part, it finds the document.

A Mongo query is type specific (although numbers can be cast), you've likely got a string where you are expecting a number.
If you ask for the string "1",
collection.find({appID: "1"})
it will only return documents with a string equal to "1", if you ask for the number 1
collection.find({appID: 1})
it will only return documents with a real number (Double, Long, Int) in.
You can check which types you have with a $type query:
collection.find( { appID : { $type : "string" } } ); // or type 2 for earlier mongo versions
collection.find( { appID : { $type : "double" } } ); // or type 1 for earlier mongo versions
collection.find( { appID : { $type : "int" } } ); // or type 16 for earlier mongo versions
Check out the BSON types and $type in the docs, currently (2016/11/11): https://docs.mongodb.com/manual/reference/operator/query/type/#op._S_type
Interestingly it will cast between Doubles, Longs and Ints; so inserting these:
db.collection("temp").insertOne({ "type" : "my double", "num" : new mongo.Double(1.0) });
db.collection("temp").insertOne({ "type" : "my long", "num" : new mongo.Long(1) });
db.collection("temp").insertOne({ "type" : "my int", "num" : 1 });
and searching for:
{ "num" : 1 }
returns the three documents.

Related

MongoDB Query Optimize Search Text

I have an application developed in NodeJS, which works as a REST API and consumes data from MongoDB
In MongoDB I have a collection called 'ftp' with more than 10 million documents with the following structure
{
"_id" : ObjectId("59e7c66911506bd1725cf145"),
"ip" : "72.32.177.76",
"timestamp" : "2017-10-16T02:30:26-04:00",
"data" : {
"banner" : "220-FileZilla Server version 0.9.41 beta\r\n"
}
}
The "data.banner" field is a hased index
From NoodeJs I make an aggregate query that filters a string of text using a regular expression, groups and counts the results.
function getData(timeInit, table, textSearch, res) {
MongoClient.connect(url, function (err, db) {
if (err) throw err;
db.collection(table).aggregate([
{
$match: { 'data.banner': $regex:textSearch}
}, {
$group: {
_id: '$data.banner',
num: { $sum: 1 },
}
},
{
$sort: {
num: -1
}
},{
$limit:5
}
], {
allowDiskUse: true
}
).toArray(function (err, result) {
if (err) throw err;
var timeFinal = new Date();
var data = {
result: result,
timeLapse: (timeFinal - timeInit) / 1000,
numResult: result.length
};
res.send(data);
db.close();
});
});
};
The query with regular expression takes about 8 seconds to return results, an excessive time in my opinion, since the regular expressions are not optimal.
My question is how should I make the filter to search for documents that contain text in an optimal way reducing the response time.
If someone knows how to optimize this type of query I would appreciate it a lot.

MongoDB Upserts Variable name as a key (Node.JS)

Node.JS, MONGODB, not using Mongoose.
I have a document I'm saving. When I use UPSERT, it structures the data as so :
{ "_id" : ObjectId("573a123f55e195b227e7a78d"),
"document" :
[ {"name" : "SomeName" } ]
}
When I use INSERT it inserts it all on the root level :
{ "_id" : ObjectId("573a123f55e195b227e7a78d"), "name" : "SomeName" }
This is obviously going to lead to lots of inconsistencies. I have tried various things. I've tried various methods such as findOneAndReplace, Update with Upsert, I've tried Replace, $setOnInsert. It all does the same thing when Upsert is involved it seems.
I have tried to use document[0] to access the first block of the array, but that throws an error... 'Unexpected Token ['
I have tried various methods and dug for hours through the various documentation, and have searched high and low for someone else having this problem, but it doesn't seem to be well documented issue for anyone else.
Anyone have any recommendations to make sure that all the fields are on the ROOT level, not nested under the variable name? Relevant code below.
findReplace: function(db, document, collection) {
var dbCollection = db.collection(collection);
var filter = document.name;
return new Promise(function(resolve, reject) {
dbCollection.updateOne({
"name" : filter
}, {
document
}, {
upsert: true
}, function(err, result) {
if (err) {
console.log('error', err)
reject(err);
}
console.log("Found Document and Upserted replacement Document");
resolve([{'status' : 'success'}]);
});
});
}
When you do this:
{
document
}
You are creating an object containing a document property and the variable's value as its value:
{
document: [ {"name" : "SomeName" } ]
}
This is new functionality from ES6. If you want to access the first item of the document variable, don't create a new object:
return new Promise(function(resolve, reject) {
dbCollection.updateOne({
"name" : filter
}, document[0], { // <========
upsert: true
}, function(err, result) {
if (err) {
console.log('error', err)
reject(err);
}
console.log("Found Document and Upserted replacement Document");
resolve([{'status' : 'success'}]);
});
});

Is it possible to get count of number of docs returned from find() query in mongoose

I am trying to get count of data fetched from the database using find() query in mongoose. Now can anyone tell me can i do something like below or do i have to write other function to do that
merchantmodel.find({merchant_id: merchant_id, rating: {'$ne': -1 }, review: {'$ne': "" }}, {'review':1, '_id':0}, {sort: {time_at: -1}}, function(err, docs) {
if (err) {
} else {
if (docs) {
console.log(docs[1].review);
console.log(docs.size()); // Here by writing something is it possible to get count or not
res.json({success: 1, message : "Successfully Fetched the Reviews"});
}
}
});
Convert returned value to array and then use length property
var query = { merchant_id : merchant_id, rating : { '$ne': -1 }, review: { '$ne': "" }};
var projection = { 'review':1, '_id':0 };
var options = { sort: { time_at: -1 } };
merchantmodel.find(query, projection, options).toArray(function(err, docs) {
if (err) {
throw(err);
}
console.log(docs[1].review);
console.log(docs.length);
res.json({success: 1, message : "Successfully Fetched the Reviews"});
});
You can simply do this:
console.log(docs.length);
The docs variable returned by the find() method is an array so docs.length would do the job.
The mongodb native way to do this would be:
db.collection.find( { a: 5, b: 5 } ).count()

Many-to-Many in MongoDB

I have two MongoDB models "Users" and "Roles". Each user can have multiple roles and each role can be assigned to many users. To keep the relation simple I would like to store the reference between both models only in the "Users" model which is already working as expected. But when I'm loading all roles at once with .find({}), I would also like to know how many users are assigned to these roles (to check if a role can be modified).
I'm using Node.js + ExpressJS and mongoose. This is what I already have:
var userSchema = new Schema({
username: String,
...
roles : [ {
type : Number,
ref : 'Role'
} ]
});
var roleSchema = new Schema({
_id : Number,
name : String
});
--------------------------------------------------
function getRoles(request, response) {
Role.find({}, function(err, roles) {
....
response.send(roles);
});
}
But now I wonder how I would achive the count query per role and still be able to send the result in one response.
I would like to avoid to send a single request per role and try to do the count within the getRoles() function. In a relational database I would do something like this:
select r.*,
(select count(*) from user2role u2r where u2r.role_id = r.role_id)
from role r;
But what's the MongoDB equivalent?
Any help and hints will be appreciated.
Thanks, Gerry
To achieve the user count query per role with the given schema, you could either use the count() method as follows:
function getUsersCount(roleName, req, res) {
Role.findOne({"name": roleName}, function(err, role) {
var roleId = role._id;
// var countQuery = User.where({ "role": roleId }).count();
// User.count({ "roles": roleId }).count(callback)
// User.count({ "roles": roleId }, callback)
User.where({ "roles": roleId }).count(function (err, count) {
if (err) return handleError(err);
console.log("There are %d users with the role %d", count, roleName);
res.send(count);
})
});
}
or you can use the aggregate() method like:
// Find the user count for a given role
function getUsersCount(roleName, req, res) {
Role.findOne({"name": roleName}, function(err, role) {
var roleId = role._id;
var pipeline = [
{
"$match": { "roles": roleId } /* filter the documents that only have the roleId in the role array */
},
{ "$unwind": "$roles" },
{
"$match": { "roles": roleId }
},
{
"$group": {
"_id": null, "count": { "$sum": 1 }
}
},
{
"$project": {
"_id": 0, "count": 1
}
}
];
Users.aggregate(pipeline, function (err, result) {
if (err) return handleError(err);
console.log(result); // [ { count: 55 } ] for example
res.send(result);
});
// Or use the aggregation pipeline builder.
Users.aggregate()
.match({ "roles": roleId })
.unwind("roles")
.match({ "roles": roleId })
.group({ "_id": null, "count": { "$sum": 1 } })
.select("-id count")
.exec(function (err, result) {
if (err) return handleError(err);
console.log(result); // [ { count: 55 } ] for example
res.send(result);
});
});
}
-- EDIT --
One approach that you could take is to use the lean() method to get pure JavaScript objects that you can manipulate to add the extra userCount field for each role. With lean() it's possible because documents returned from queries with the lean option enabled are plain javascript objects', not MongooseDocuments. Thus with the roles returned from the find() cursor, you can convert them into plain JS objects
that you can iterate over using the native JavaScript map() method, get the number of users per role using another query that takes into consideration the count() method explained above and return the modified object.
The following demonstrates how the function is implemented:
function getRoles(request, response) {
Role.find({}).lean().exec(function(err, roles) {
var result = roles.map(function(r){
var obj = {},
countQuery = User.where({ "roles": r._id }).count();
obj["userCount"] = countQuery;
obj["_id"] = r._id;
obj["name"] = r.name;
return obj;
});
response.send(result);
});
}
As per your above explanation about the schema design, i can assume your schema is as below :
Roles collection :
{
role_id :
name :
}
User collection :
{
user :
role_id : [ ]
....
}
you can use aggregation to achieve this :
db.users.aggregate([{$unwind: "$role_id"},
{$group: {_id: "$role_id", count : {"$sum":1}} },
]);

Unable to $pull from array

I have tried a lot of different ways but maybe I dont have the hang of the way mongodb needs me to query it.
this is what the doc looks like.
{
"username" : "amitverma",
"notifications" : {
"notification_add_user" : [
{
"sender" : "a",
"action" : "b",
"type" : "c",
"objectType" : "d",
"objectUrl" : "e"
}
]
},
"_id" : ObjectId("539aa673e97933e7a5000001")
}
I want to remove the object inside "notification_add_user". And these are the different ways I have tried to get it working.
db.notifications.update(
{'username': 'amitverma' },
{ $pull: {
"notifications.notification_add_user":{'sender': "a"}
}}
)
This works in console.
But the code that I have written for it doesnt get the job done.
// notificationtype is 'add_user'
removequery['notifications.notification_' + notificationtype] = { 'sender': 'a' };
co_notifications.update(
{'username': data.username},
{$pull : removequery}, function(err, docs){
console.log('remove notification callback')
console.log(err)
console.log(docs)
})
)
Using the native MongoDB driver, I have used the following query to update from a node console and it works:
>MongoClient = require('mongodb').MongoClient;
>format=require('util').format;
>MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
if(err) throw err;
var collection = db.collection('notifications');
collection.count(function(err, count) {console.log(format("count = %s", count));});
collection.update(
{'username': 'amitverma'},
{$pull:{'notifications.notification_add_user':{'sender':'a'}}},{w:1},function(err){
if (err) console.warn(err.message);
else console.log('successfully updated');
})
})
db.notifications.find({'username': 'amitverma' }).forEach( function(result) {
var i;
for(i in result.notifications.notification_add_user) {
var sender = result.notifications.notification_add_user[i]['sender'];
if(sender === 'a') {
delete result.notifications.notification_add_user[i]['sender'];
}
}
db.notifications.save(result);
});
First i will get all the results where the username is equal to
'amitverma'
Then i loop through the notification_add_user array and see if sender is equal to 'a' and delete all the match results
And for last i save the changes

Resources