how to find a single field in mongoDB instead of entire document - node.js

I want to achieve the result as obtained by
SELECT AGE FROM COLL WHERE NAME="AYUSH";
I took the following approach
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("new");
//var query = { name:'ayush' };
//var age = {age : 1, _id:0};
dbo.collection("coll").find(
{ name:'ayush' },
{ age : 1, _id:0}).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
});
});
the result that i am getting is
[ { _id: 5a818b71d2029813505d736a,
name: 'ayush',
age: '22',
sex: 'm' } ]

From MongoDB documentation : Project Fields to Return from Query
Return the Specified Fields and the _id Field Only
A projection can explicitly include several fields by setting the
<field> to 1 in the projection document. The following operation
returns all documents that match the query. In the result set, only
the item, status and, by default, the _id fields return in the
matching documents.
db.inventory.find( { status: "A" }, { item: 1, status: 1 } )
The operation corresponds to the following SQL statement:
SELECT _id, item, status from inventory WHERE status = "A"
In your case, if you only want the field age, you have to suppress the other fields _id, name and sex as following :
dbo.collection("coll").find({ name:'ayush' },{age:1, _id:0, name:0, sex:0})...

The easiest way to do this would be to convert the result in to an array and then send that array as the response.
The code would look like:
dbo.collection("coll").find(
{ name:'ayush' },
{ age : 1, _id:0}).toArray(function(err, result) {
if (err) throw err;
var array = [];
array.push(result[0].age);
res.send(array);});
Moreover don't use mongoClient,use mongoose instead.its easier and better

Related

Bulk insert in MongoDB with mongoose for multiple collections

I have 2 collections(data, metaData)
data schema is
{
_id: ......,
name: ......, //not unique
mobile: ......, // unique or null
email: ......, // unique or null
uniqueId: ......, // unique or null
}
at least one of unique data is required for insert
metaData schema is
{
_id: ......,
dataId: ......,//refrence from _id of data collection
key: ......,
value: ......
}
JSON array is getting from client
[{
name: "abc",
mobile: 9999999999,
mData: {
c1: 123,
c2: "xyz"
}
},
{
name: "qwerty",
email: 'qwerty#mail.com',
mData: {
c1: 123,
c2: "zxc"
}
}
......
]
I am iterating through the array and inserting each of them in both collections into MongoDB.
let Bulk = Data.collection.initializeUnorderedBulkOp();
dataArr.forEach(function(item) {
let data = service.generateData(item);
// data.query: {mobile: ..., email: ..., uniqueId: ...}
// if value exists then keys is also exists for mobile, email, uniqueId in query
Bulk.find(data.query).upsert().updateOne(data.doc);
});
Bulk.execute((e, d) => {
let metaBulk = MetaData.collection.initializeOrderedBulkOp();
let length = dataArr.length;
dataArr.forEach(function(data) {
Data.findOne(data.query).exec(function(err, data) {
length--;
for(let key in data["mData"]) {
let value = data["mData"][key] || "";
let mData = service.generateMdata(key, value, data._id);
metaBulk.find(mData.query).upsert().updateOne(mData.doc);
}
if(length == 0) {
metaBulk.execute();
}
});
});
});
my solution is working fine right now but it's taking so much time to iterating data collection for finding ids for metaData collection.
I need a way of inserting the data in bulk into MongoDB without find query for data id. Is there any option to perform bulk upserts with mongoose for multiple collections in a single query.
No multiple collection update in a single command for your scenario. In your case if you can include metadata array inside parent collection it can insert data with single command with updateMany(). MongoDB also supports bulk insert through the db.collection.insertMany().
db.data.insertMany( [{ name: "abc",mobile: 9999999999, mData: { c1: 123, c2: "xyz"} },
{name: "qwerty",email: 'qwerty#mail.com',mData: { c1: 123, c2: "zxc" }}]);
Also you can use db.collection.bulkWrite() as well.
I think what you can do is:
async.each(jsonArray, function(jsonData,callback){
//first insert data in data schema
var data = new data(jsonData);
data.save(function(err){
if err throw err;
//then you save the data in metaData collection
async.each(jsonData.mData, function(metadata, callback2){
var metaDataObj = new metaData(metadata);
metaDataObj.dataId = data._id;
metaDataObj.save(function(err){
callback2();
});
}, function(err, results1){
callback();
});
});
}, function(err, results){
console.log('Data is saved');
});

Mongoose, concise way to get one item with each unique property into an array?

I have a collection of documents with many different characteristics. I want to get from Mongoose an array where each item in the array is a unique value for an attribute, in this case color. In other words, I do not want an array of each item, just of the color values. So in this case, if I have 100 different products, but only 8 colors between all 100, then I would like the to have an array of the 8 colors. Here is the way I have been using but I am wondering if there is a more concise way of doing it?
var allproducts = Products.find(function (err, products) {
// Get all products
var collection = [];
// Make an array to contain a product with each of the 8 colors
var uniqueArray = [];
// Make an array to check the current color against (if already used)
for (var i = products.length - 1; i >= 0; i--) {
if (uniqueArray.indexOf(products[i].color) === -1) {
// Check to see if the color is in the unique color array
uniqueArray.push(products[i].color);
// Add it if not
collection.push(products[i]);
// Add the product to the collection array if its color is not in the unique array
}
}
});
Attempting to use the Mongoose aggregate method:
router.get('/', function (req, res) {
Products.aggregate([{
$group: {
_id: '$color'
}
}], {}, function (err, collection) {
console.log(err, collection);
if (err) {
throw err
}
res.end();
// res.json(collection);
});
});
This can be easily solved using lodash
npm install lodash --save
var _ = require('lodash');
var allproducts = Products.find(function (err, products) {
...
var uniqueArray = _.pluck(products, 'color');
uniqueArray = _.uniq(uniqueArray);
....
});
I. Directly in mongodb, you have to this request :
db.colors.aggregate( { $group: {_id:"$color"} } )
Result :
{ "_id" : "yellow" }
{ "_id" : "blue" }
{ "_id" : "red" }
MongoDB Tutorial for aggregation
II. In mongoose you can do :
Products.aggregate(
{$group: {
_id: '$color' // grouping key - group by field district
}
}).exec( function( err, products ) {
if ( err ) {
return res.status( 400 ).send({
message: errorHandler.getErrorMessage( err )
});
} else {
res.json( products );
}
});
Mongoose : Model aggregate

Cannot applying find() method with Native MongoDB becaus of ID type

I have a function that is needed to get results.
When I give 1 as _id filter everything is OK.
collectionPersonnel
.find({ '_id' : 1 })
.toArray(function (err, personnel) {
console.log(personnel);
});
If I give filter another way for instance user[0]['personnel_id'] -that is store 1- then I get only [] result;
collectionPersonnel
.find({ '_id' : user[0]['personnel_id'] })
.toArray(function (err, personnel) {
console.log(personnel);
});
And then I've tried another way. But it doesn't work because I used a string(user[0]['personnel_id']) instead of an ObjectID.
var ObjectID = require('mongodb').ObjectID;
var personnelPK_Hex = (user[0]['personnel_id']).toHexString();
var personnelPK = ObjectID.createFromHexString(personnelPK_Hex);
What should I do?
Edit
All of my codes are below;
module.exports = {
show: function(req, res) {
User.native(function(err, collectionUser) {
if(err) {
console.log("There is no exist a User by current_id");
};
collectionUser
.find({'_id' : req.param('id')})
.toArray(function (err, user) {
Personnel.native(function(err, collectionPersonnel) {
if(err) {
// handle error getting mongo collection
console.log("There is no exist a Personel by current _id");
};
if(!collectionPersonnel) {
console.log("There is no exist a Personel by current _id");
};
// var ObjectID = require('mongodb').ObjectID;
// var personnelPK_Hex = (user[0]['personnel_id']).toHexString();
// var personnelPK = ObjectID.createFromHexString(personnelPK_Hex);
collectionPersonnel
.find({ '_id' : user[0].personnel_id })
.toArray(function (err, personnel) {
console.log(personnel);
});
});
});
});
}
};
And console's output is;
[]
Solved
Just like apsillers's said. I had given a numeric _id to collection, incorrectly.
I've fixed _id value and everything is OK.
Thank you all...
user[0]['personnel_id'] might be a string. For Mongo, "1" is different from 1, which is why your literal number 1 worked, but your variable (which holds a string) does not.
Instead, try using a unary plus to convert the string to a number: +user[0]['personnel_id'].
try to use like user[0].personal_id instead of user[0]['personnel_id'] please provide your schema design that would be better to figure out what exactly you are missing.
i tried like this
collectionPersonnel
.find({ '_id' : user[0].personnel_id })
.toArray(function (err, personnel) {
console.log(personnel);
});

How to check if Mongo's $addToSet was a duplicate or not

I am using Mongoskin + NodeJS to add new keywords to MongoDB. I want to notify the user that the entry was a duplicate but not sure how to do this.
/*
* POST to addkeyword.
*/
router.post('/addkeyword', function(req, res) {
var db = req.db;
db.collection('users').update({email:"useremail#gmail.com"}, {'$addToSet': req.body }, function(err, result) {
if (err) throw err;
if (!err) console.log('addToSet Keyword.' );
});
});
The result does not seem to be of any use since it doesn't state if the keyword was added or not.
At least in the shell you can differentiate if the document was modified or not (see nModified).
> db.test4.update({_id:2}, {$addToSet: {tags: "xyz" }})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test4.update({_id:2}, {$addToSet: {tags: "xyz" }})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
Update for Node
When you use collection.update(criteria, update[[, options], callback]); you can retrieve the count of records that were modified.
From the node docs
callback is the callback to be run after the records are updated. Has
two parameters, the first is an error object (if error occured), the
second is the count of records that were modified.
Another Update
It seems at least in version 1.4.3 the native Mongo Node driver is not behaving as documented. It is possible to work around using the bulk API (introduced in Mongo 2.6):
var col = db.collection('test');
// Initialize the Ordered Batch
var batch = col.initializeOrderedBulkOp();
batch.find({a: 2}).upsert().updateOne({"$addToSet": {"tags": "newTag"}});
// Execute the operations
batch.execute(function(err, result) {
if (err) throw err;
console.log("nUpserted: ", result.nUpserted);
console.log("nInserted: ", result.nInserted);
console.log("nModified: ", result.nModified); // <- will tell if a value was added or not
db.close();
});
You could use db.users.findAndModify({email:"useremail#gmail.com"},[],{'$addToSet': { bodies: req.body }},{'new':false}). Pay attention to new:false switcher, it allows you to get document before update and you could check whether array contained item before update. However, it could be problematic approach if your documents are big, because you analyze it on client side.
P.S. Your original query with $addToSet is wrong: field name is missing.
Edit: I tried to use count returned by update, but it returns 1 for me in all cases. Here is the code I used for test with MongoDB 2.6:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost:27017/mtest', function(err, db) {
if(err) throw err;
db.collection('test').insert({_id:1,bodies:["test"]},function(err,item){
db.collection('test').update({_id:1},{$addToSet:{bodies:"test"}}, function(err,affected){
if(err) throw err;
console.log(affected); //1 in console
});
});
});
i am update a array from Collection with this JSON:
{
"<arrayname>":"<value>"
}
route.js
routes.post("/api/:id", Controller.addOne);
Controller.js
async addOne(req, res) {
//juryman id to list add
if (Object.keys(req.body).length === 1) {
console.log("Size 1");
}
await Session.findOneAndUpdate(
{ _id: req.params.id },
{ $addToSet: req.body }
)
.then(function(success) {
res.send("Successfully saved.");
})
.catch(function(error) {
res.status(404).send(error);
});
},
I have five arrays in my Collection and this changes the JSON array name-value and updates correctly, the respectively Collection array. This works only for one item.

How to use a variable as a field name in mongodb-native findOne()?

I have this data in mongodb:
{
"name": "Amey",
"country": "India",
"region": "Dhule,Maharashtra"
}
and I want to retrieve the data while passing a field name as a variable in query.
Following does not work:
var name = req.params.name;
var value = req.params.value;
collection.findOne({name: value}, function(err, item) {
res.send(item);
});
How can I query mongodb keeping both field name and its value dynamic?
You need to set the key of the query object dynamically:
var name = req.params.name;
var value = req.params.value;
var query = {};
query[name] = value;
collection.findOne(query, function (err, item) { ... });
When you do {name: value}, the key is the string 'name' and not the value of the variable name.
Just put the variable in []
var name=req.params.name;
var value = req.params.value;
collection.findOne({[name]:value}, function(err, item) {
res.send(item);
});
I'd like to clarify that if you're trying to make a query concerning a nested field only (not its value), like if you want to query the field "name" from this document:
{
loc: [0, 3],
unit: {
name : "playername"
}
}
this will work (as in my case - using update):
mdb.cords.updateOne(
{_id: ObjectID(someid)},
{$set: {[query]: newValue}},
function (err, result) {
...
}
}
Simply enclosing [query] in brackets tells mongodb that it's not literal, but rather a path.
use like this if the object is nested.
Direct Object:-
var name=req.params.name;
var value = req.params.value;
collection.findOne({[name]:value}, function(err, item) {
res.send(item);
});
An object is nested:-
var surname=req.params.surname;
var value = req.params.value;
var condition = `name.${surname}`
collection.findOne({[condition]:value}, function(err, item) {
res.send(item);
});

Resources