nodejs/mongodb - reading out one specific element - node.js

I want to read out one specific element out of mongodb
db.collection('profiles', function(err, collection) {
collection.findOne({'email': mail}, function(err, item) {
this reads the whole entry
for example:
{
"email" : "asdd#asd.de",
"password" : "asd",
"_id" : ObjectId("51c8790f912501e403000001")
}
how can i read out only one of those elements
for example password
{
"password" : "asd"
}

collection.findOne({'email': mail}, {password: 1, _id: 0}, function(err, item) {
}
The second argument to find/findOne is the fields to select(projection).
{_id: 0} is explicitly required because by default _ids are always returned.

Related

Create Rating if Not Rated Yet, Update User Rating if Rating Exists

I am creating a user experience where a user will be able to rate items from different vendors. My initial thought is for each User schema to have an array which stores all the items that the user has rated. The rated item would include the unique vendor item ID and a numerical rating value.
User Model
const userSchema = new mongoose.Schema({
...
userType: String,
ratedItems: Array,
...
});
Controller
exports.postUpdateRatedItem = (req, res, next) => {
User.findById(req.user.id, (err, user) => {
if (err) { return next(err); }
user.update(
{ $push: {ratedItems : {
vendorItem : req.body.itemID,
rating : req.body.rating
}}},
function (err) {
res.send(200);
});
});
}
Current Output
{
"_id" : ObjectId("5c91869a71ece20551fd6aed"),
"userType" : "participant",
"ratedItems" : [
{ "vendorItem" : "5c9bdd524a0dfa753e08a0a4", "rating" : "3" },
{ "vendorItem" : "5c9bdd524a0dfa753e08a0a4", "rating" : "6" }
]
}
This approach works great in adding new object to the array, but only adds and does not update. Instead, every time a user updates a rating, a new object is added to the array. What approach would allow to check for the unique vendorItem id? How do I go about checking the user rated items? If found, update the rating value, if not found, push to the array.
Thank you in advance, still learning MongoDB/Mongoose.
Edit
Below is what I expect the outcome. For each object in the array, the 'rating' is updated when the user changes the rating. The ratedItems array will eventually have many many vendorItem with unique IDs and ratings.
Expected Output
{
"_id" : ObjectId("5c91869a71ece20551fd6aed"),
"userType" : "participant",
"ratedItems" : [
{ "vendorItem" : "5c9bdd524a0dfa753e08a0a4", "rating" : "6" },
// additional rated items all with unique IDs
{ "vendorItem" : "5c9bcc14d5161c38a4581e28", "rating" : "2" },
{ "vendorItem" : "5c9407d143cd0f20d758acdb", "rating" : "11" }
]
}
It sounds like you are looking for "upsert" functionality. The Mongoose model API provides findByIdAndUpdate and other similar methods for this.
Make sure you set the new and upsert options to true. This will create the object if it doesn't exist and return the modified document if it is updated.
Your use case would look something like this:
const update = {
$push: {
ratedItems: {
vendorItem: req.body.itemID,
rating: req.body.rating
}
}
};
const options = {'new': true, upsert: true};
User.findByIdAndUpdate(req.user.id, update, options, function(err, user) {
// ...
});

$addToSet in a nested Array item

Considering the following
User collection & sample User document
{
"_id" : ObjectId("575c01f7b8e5999addeb598c"),
"username" : "test.1#gmail.com",
"password" : "<password>",
"firstName" : "Test,
"lastName" : "User"
}
I am trying to run an update request to add an entry in userData.eventData which is meant to be an array
In mongo script I can do
> db.Users.update({_id: ObjectId("575c01f7b8e5999addeb598c")}, {"$addToSet":{"userData.eventData":"My event"}} )
And I have the following result : userData is created as an Object and eventData as a nested Array
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.Users.find({_id: ObjectId("575c01f7b8e5999addeb598c")})
{ "_id" : ObjectId("575c01f7b8e5999addeb598c"), "username" : "test.1#gmail.com", "password" : "<password>", "firstName" : "Test", "lastName" : "User", "userData" : { "eventData" : [ "My event" ] } }
While running the same logic in mongo (using driver version 2.1.21)
// with a properly initialized db object
db.collection("Users").update({"_id" : ObjectId("575c01f7b8e5999addeb598c")}, {"$addToSet": { "userData.eventData": "My Event"}}, function(err, result) {
// do something
});
I receive the following response
result:Object
n:0
nModified:0
ok:1
And indeed the database entry is unchanged.
Is that the way it is meant to behave? I can easily fix this by creating the userData.eventData array but I found disturbing the fact that node's Mongo driver and mongo shell didn't behave the same on this
Thanks in advance for your help & advice
Edit 13/6/16
Mistake was on my side, I missed a 'new' before 'ObjectId(...' in node. With it, it behaves exactly the same as in mongo shell (i.e. 'userData' is created as an Object and it includes 'eventData' array)
No issue, then :)
Update, updates an already existing object in your document.
What you want is insert or use upset which creates a new document when no document matches the query criteria
db.collection.update(
{ name: "Andy" },
{
name: "Andy",
rating: 1,
score: 1
},
{ upsert: true }
);
If you wanted to add an object to your array, you would need $push
// Insert a document in the capped collection
function push (db, collection, search, data, callback) {
db.collection(collection).update(
search, {
$push: data
}, function (err, result) {
console.log("New object pushed");
callback(err);
});
}

Find data by two values in mongoose

Structure is
{
id : 12345,
userIDs : [ "1", "2", "3"]
}
How to write query in nodejs to find userId is present in document or not on the based on id? So we have to pass two values in query first is ID and second is userId, am I correct?
Structure.findOne( {'id' : id, "userIDs" : userId },
'-_id' , function (err, u) {
if(!err) {
//callback
} else {
// callback
}
}
u return null if not match otherwise it return whole document.

Fetch sub-document Mongodb only match with criteria

I have data in mongodb like this:
{
"_id" : ObjectId("55a12bf6ea1956ef37fe4247"),
"tempat_lahir" : "Paris",
"tanggal_lahir" : ISODate("1985-07-10T17:00:00.000Z"),
"gender" : true,
"family" : [
{
"nama" : "Robert Deniro",
"tempat_lahir" : "Bandung",
"tanggal_lahir" : ISODate("2015-07-09T17:00:00.000Z"),
"pekerjaan" : "IRT",
"hubungan" : "XXX",
"tanggungan" : false,
"_id" : ObjectId("55a180f398c9925299cb6e90"),
"meta" : {
"created_at" : ISODate("2015-07-11T20:59:25.242Z"),
"created_ip" : "127.0.0.1",
"modified_at" : ISODate("2015-07-12T15:54:39.682Z"),
"modified_ip" : "127.0.0.1"
}
},
{
"nama" : "Josh Groban",
"tempat_lahir" : "Jakarta",
"tanggal_lahir" : ISODate("2015-06-30T17:00:00.000Z"),
"pekerjaan" : "Balita",
"hubungan" : "Lain-Lain",
"tanggungan" : true,
"_id" : ObjectId("55a29293c65b144716ca65b2"),
"meta" : {
"created_at" : ISODate("2015-07-12T16:15:15.675Z"),
"created_ip" : "127.0.0.1"
}
}
]
}
when i try to find data in sub-document, with this code:
person.findOne({ _id: req.params.person, {'family.nama': new RegExp('robert', 'gi') }}, function(err, data){
// render code here
});
It show all data in Family Data,
Can we fetch or display a data only match with criteria/keyword, for example only "Robert Deniro" row
Thank You
In 'regular' MongoDB, you can use the $ operator for that. I'm not sure if it works with Mongoose, but it's worth a try:
person.findOne({
_id : req.params.person,
'family.nama' : new RegExp('robert', 'gi')
}, {
// Only include the subdocument(s) that matched the query.
'family.$' : 1
}, function(err, data){
// render code here
});
If you need any of the properties from the parent document (tempat_lahir, tanggal_lahir or gender; _id will always be included), you need to add them to the projection object explicitly.
One caveat: the $ operator will only return the first matching document from the array. If you need it to return multiple documents, you can't use this method and (AFAIK) have to postprocess the results after they are returned from the database.
It solved with this code:
var options = {
family: {
$elemMatch: { nama: req.query.keyword }
},
};
person.findOne({ _id: req.params.person, 'family.nama': keyword }, options, function(err, data){
//render code here
});
Thanks to #hassansin & #robertklep

Mongoose/Mongo faster to have separate collections?

Currently I have a schema USERS with a sub doc VIEWED
As a 'user' views other users, their ID gets logged in the viewers sub doc
So when viewing, technically this gets all the users, then filters that through all the viewed users [for any given user]. So you get a list of unique/fresh users.
My method is currently fetching the list of users - Query 1
Then its fetching the list of viewed users (for a given user) - Query 2
Then using array.filter function to get a list of new users.
(using async parallel for those queries)
Question is, would it be faster to just have a separate document/collection that stores a list of viewed users for any given user. e.g:
{
userID: 1002,
viewedID: 9112
},
{
userID: 1002,
viewedID: 9222
},
{
userID: 1002,
viewedID: 9332
}
Is it possible for me to some how do a query that gets me a fresh list of users, so i don't have to do the computation myself. i.e let mongo do all the work.
edit, adding code to make it more clear
var ViewedSchema = new Schema({
coupleId: {type: Number, required: true}
});
var UserSchema = new Schema({
name : { type: String, trim: true, required: true}
, partnerId : { type: Number}
, viewed : [ViewedSchema]
});
code to view partners/users that have not been viewed before
async.parallel([
function(callback) {
//gets all the users/partners
User.find({}, function(err, users) {
var allPartners = [];
users.forEach(function(user){
if(allPartners.indexOf(user.partnerId) == -1) {
allPartners.push(user.partnerId);
}
});
callback(null, allPartners);
});
},
function(callback) {
//gets all the users/partners i have already viewed
var votedPartners = [];
User.findById(id, function(err, user) {
user.viewed.forEach(function(user){
votedPartners.push(user.coupleId);
});
callback(null, votedPartners);
});
}
],
function(err, result) {
//gets the differences between the 2 arrays
function exists(element) {
return (result[1].indexOf(element) == -1);
}
var showPartners = result[0].filter(exists);
User.find({partnerId: showPartners[0]}, function(err, user){
var json = {objects: user};
res.render('index', json);
});
});
};
I'm not sure what you mean by fresh or new users, exactly, but have you loked at the distinct() command? You can use it to get all the unique viewed user IDs for all the users in the collection, which is what it sounds like you want to do. See
http://docs.mongodb.org/manual/reference/method/db.collection.distinct/
From the documentation:
Return an array of the distinct values of the field sku in the subdocument item from all documents in the orders collection:
db.orders.distinct( 'item.sku' )
If you give an example of your current document schema, I could try to write the exact query for you.
Edit: You can use $nin to find the userIds that are not in a given list. Here is an example I set up in my local Mongo:
> db.dating.insert({"userId":100,"viewedId":["200","201"]})
> db.dating.findOne()
{
"_id" : ObjectId("5398d81799b228e88aef2441"),
"userId" : 100,
"viewedId" : [
"200",
"201"
]
}
> db.dating.insert({"userId":200,"viewedId":[""]})
> db.dating.insert({"userId":201,"viewedId":[""]})
> db.dating.insert({"userId":202,"viewedId":[""]})
> db.dating.insert({"userId":203,"viewedId":[""]})
> db.dating.insert({"userId":204,"viewedId":[""]})
> db.dating.insert({"userId":205,"viewedId":[""]})
> db.dating.find({"userId":{$nin: [200,201]}})
{ "_id" : ObjectId("5398d81799b228e88aef2441"), "userId" : 100, "viewedId" : [ "
200", "201" ] }
{ "_id" : ObjectId("5398d84099b228e88aef2444"), "userId" : 202, "viewedId" : [ "
" ] }
{ "_id" : ObjectId("5398d84799b228e88aef2445"), "userId" : 203, "viewedId" : [ "
" ] }
{ "_id" : ObjectId("5398d85699b228e88aef2446"), "userId" : 204, "viewedId" : [ "
" ] }
{ "_id" : ObjectId("5398d85c99b228e88aef2447"), "userId" : 205, "viewedId" : [ "
" ] }

Resources