Prevent duplicates in an array of objects - javascript

I'm learning Node / Mongoose, and trying to figure out how to prevent duplicates in an array of ListItems:
var listitemSchema = new mongoose.Schema({
name: String,
rank: Number
});
...which exist inside a user:
var userSchema = new mongoose.Schema({
username: String,
password: String,
list: [{
type: mongoose.Schema.Types.ObjectId,
ref: "ListItem"
}]
});
...based on whether the user already has the name within his/her list. I was looking into $addtoset but can't really figure out how to put conditions when dealing with an object. The docs weren't very helpful and I can't seem to find similar examples online.
Right now my 'POST' request is a mess and looks like this:
router.post("/editlist", isLoggedIn, function(req,res){
User.findById(req.user._id).populate("list").exec(function(err , user) {
if(err) {
console.log(err);
res.redirect("/editlist");
} else {
//prevent duplicates based on username
User.update(
{ "_id": req.user._id },
{ $addToSet: { "streamName": req.body.listitem.name}
});
res.redirect("/watch");
}
});
});
Can anyone send me on my way? My $addToSet isn't pushing items onto the list array.

According to the $addToSet documentation, the operator is used in this form:
{ $addToSet: { field1: value1, ... } }
where field is an Array.
To add an item to the list array, we do:
User.update({
{ "_id": req.user._id },
{ $addToSet: { list: {"streamName": req.body.listitem.name } }}
});
You'll need to update the userSchema definition:
var userSchema = new mongoose.Schema({
username: String,
password: String,
list: [listitemSchema]
});

Related

Mongo - Update nested array of objects by _id

How can I access an array of object with their own _id and update it with Mongo/Mongoose?
Take a look to my update query and check if there's something wrong, because this code doesn't return any error, but it doesn't really update the field
modelUser.findOneAndUpdate(
{ userName: body.author, "portfolio._id": body.id },
{ new: true },
{
$set: { //I thing the problem it's over here
"portfolio.$.profitLoss": profitLoss,
"portfolio.$.percentage": percentage
}
},
(err, user) => {
if (err) {
console.log(err);
}
console.log(`Done`);
}
);
This is my User Schema:
const userSchema = new Schema({
...stuff,
portfolio: [
{
coin: String,
amount: String,
price: String,
bought: Date,
profitLoss: String,
percentage: String
}
],
});
Basically i think mongo just don't know which of these sub documents should update, I don't know if there's something like another findOneAndUpdate for sub object/document by id.
Just changed findOneAndUpdate to updateOne and everything works.

Connect mongoose-array-values to a unique ID

This may seem like a vague question, but I'm going to try to explain the best I can. As a side note, I'm quite new to using mongoose :)
I have a mongoose-schema storing different values for each user, like so...
let userSchema = mongoose.Schema({
user: { type: String, required: true, unique: true },
pass: { type: String, required: true },
files: [{ type: String, required: false }],
});
The "files"-key contains an array of values, lets say for example:
userSchema.files = [value1, value2, value3]
And I want each value to be connected to some kind of ID, so that when I call the specified ID, I get the specified value. Just for demonstrating purposes, it could look something like this:
userSchema.files = [{value:value1, id: id1},
{value:value2, id: id2},
{value:value3, id: id3}]
Then I want to find the specified id, and return it's "value"-key in a request:
router.route("/home/:id")
.get(restrict, function(req, res) {
User.findOne({ user: req.session.Auth.username }, function(error, data) {
data.files.forEach(function(file) {
if (file.id === req.params.id) {
response.render("../home", file.value)
}
}
});
});
How can I do this? Tried pushing an object to files, but that didn't work as expected. Read something about ObjectId, but couldn't quite understand it. Any tips?
I think you simply need to create a separate model for File and connect it to your User model using the 'ref' keyword :
let fileSchema = mongoose.Schema({
_id : Number,
value : String
});
let userSchema = mongoose.Schema({
user: { type: String, required: true, unique: true },
pass: { type: String, required: true },
files: [{ type: Number, ref: 'File' }]
});
let User = mongoose.model('User', userSchema);
let File = mongoose.model('File', fileSchema);
let f1 = new File({ _id: 1, value: 'File 1'});
let f2 = new File({ _id: 2, value: 'File 2'});
let f3 = new File({ _id: 3, value: 'File 3'});
let user1 = new User({user:'chuck', pass:'norris'});
user1.files.push(f1);
user1.files.push(f2);
user1.files.push(f3);
user1.save(function(err){ });
Now to get the data back:
User
.findOne({ user: 'chuck' })
.populate('files') // only works if we pushed refs to children
.exec(function (err, user) {
if (err) return handleError(err);
console.log(user);
//you can now loop through user.files and compare _id
user.files.forEach(function(file) {
if (file._id === req.params.id) {
response.render("../home", file.value)
}
}
});
You can read about mongoose reference population here: http://mongoosejs.com/docs/populate.html

How to add object to collection inside another collection in MongoDB using Node.js

I know how to add object to collection in MongoDB using Node.js, for example:
router.post('/addProduct', function (req, res) {
Partner.findByIdAndUpdate({ _id: req.body.partnerId }, { $push: { "products": { name: req.body.dataProduct.name } } }, { safe: true }, function (err, response) {
if (err) throw err;
res.json(response);
});
});
but what if in product will be another table? How can I simply add object there?
Let's say this is my schema:
var partnerSchema = new mongoose.Schema({
name: String,
products: [
{
name: String,
campaignList: [
{
name: String,
type: String,
startDate: Date,
endDate: Date,
paymentMethod: String,
partnerPayout: Number,
ourPayout: Number
}
]
}]
});
ID in each partner and product are default ._id eg. partner._id and product._id. That's why aren't in schema above. However I sending them from FrontEnd to BackEnd as a req.parameter - normally thing but i wanted to say it for sure :)
Your best bet would bet to define the Schema & Model for the campaign on its own, and add it to the Partner by reference using the _id
var partnerSchema = new mongoose.Schema({
name: String,
products: [
{
name: String,
campaignList: [
{ type : mongoose.Schema.Types.ObjectId, ref : 'campaignModel' }
]
}]
});
var campaignSchema = new mongoose.Schema({
name: String,
type: String,
startDate: Date,
endDate: Date,
paymentMethod: String,
partnerPayout: Number,
ourPayout: Number
});
var campaignModel = mongoose.model('campaignModel', campaignSchema);
var partnerModel = mongoose.model('partnerSchema', partnerSchema);
A good practice is to look for times where you're trying nest semi-complex data, or objects with more than two or three keys, and extract them into their own collection. Not only does it make it easier to search for those documents, it makes it easier to use them in conjunction with other objects.
Be sure to call .populate() during your query so that MongoDB knows to nest the documents from the other collections, otherwise, you'll just have an array of ObjectId.
First match the required products array position. You can confirm this by testing a simple find like:
Partner.find({_id: req.body.partnerId), 'products.name': req.body.dataProduct.name }, { 'products.$': 1})
Use the positional $ operator to push the new object into the array in the matched product element:
Partner.update({_id: req.body.partnerId), 'products.name': req.body.dataProduct.name }, { $push: { 'products.$.campaignList': { name: 'new campaign' }}})
Reference https://docs.mongodb.com/manual/reference/operator/update/positional/
try this:
router.post('/addProduct', function (req, res) {
Partner.findOneAndUpdate({ _id: req.body.partnerId }, { $push: { "products": { name: req.body.dataProduct.name, $push: {"campaignList": {name: req.body.name}} } } }, { safe: true }, function (err, response) {
if (err) throw err;
res.json(response);
});
});
i hope it helps you

Mongoose - delete subdocument array item

I have this little schema for users:
{
username: String,
contacts: Array
}
So for example some user's contacts will look like this:
{
username: "user",
contacts: [{'id': ObjectId('525.....etc'), 'approved': false}, {'id':ObjectId('534.....etc'), 'approved': true}]
}
Now I need to delete an item from contacts so I do:
model.findByIdAndUpdate(23, {'$pull': {
'contacts':{'id':'525.....etc'}
}});
but seems not working, no errors but it doesn't gets deleted, I just would like to return this document for the user:
{
username: "user",
contacts: [{'id':ObjectId('534.....etc'), 'approved': false}]
}
how to achieve this?
The $pull operator actually just performs the conditions on the array element on which it is operating. It seems that your question might not actually show that you are probably working with an ObjectId value that mongoose creates by default for all array fields.
So you could to your query like this, after importing the ObjectId creation method:
model.findByIdAndUpdate(23, {
'$pull': {
'contacts':{ '_id': new ObjectId(someStringValue) }
}
});
Or in fact you can actually define your "schema" a little better, and mongoose will actually "autocast" the ObjectId for you based on the "type" defined in the schema:
var contactSchema = new Schema({
approved: Boolean
});
var userSchema = new Schema({
username: String,
contacts: [contactSchema]
});
This allows mongoose to "follow the rules" for strictly typed field definitions. So now it knows that you actually have an _id field for each element of the contacts array, and the "type" of that field is actually an ObjectId so it will automatically re-cast "String" values supplied as a true ObjectId.
finaly!
MongoDB:
"imgs" : {"other" : [ {
"crop" : "../uploads/584251f58148e3150fa5c1a7/photo_2016-11-09_21-38-55.jpg",
"origin" : "../uploads/584251f58148e3150fa5c1a7/o-photo_2016-11-09_21-38-55.jpg",
"_id" : ObjectId("58433bdcf75adf27cb1e8608")
}
]
},
router.get('/obj/:id', function(req, res) {
var id = req.params.id;
Model.findOne({'imgs.other._id': id}, function (err, result) {
result.imgs.other.id(id).remove();
result.save();
});

Populating an array when creating a record in MongoDB with mongoose

I am writing an application that keeps track of members and their certifications. Each member has some arrays that refer to other models as described in the Mongoose population docs.
Member schema
{
name: {
first: String,
last: String
},
unit: Number,
// more stuff
class_year: Number,
campus_box: String,
campus_address: String,
// more stuff
emails: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Email' }],
certifications: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Certification' }],
service_credits: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ServiceCredit' }]
}
Certification schema
{
type: String,
issue: Date,
expiry: Date,
number: String,
_member: { type: mongoose.Schema.Types.ObjectId, ref: 'Member' }
}
The schemata for Email and ServiceCredit are similar in that they both have the _member field.
When I create an Email, it is added to the Email model and I can subsequently perform
var Email = mongoose.model('Email');
Email.findOne().populate('_member').exec(function (err, email) {
console.log(email._member.name.first);
});
However, it is not added to the array in the Member schema, because when I run
var Member = mongoose.model('Member');
Member.findOne().populate('emails').exec(function (err, member) {
res.json(200, member);
});
I get emails: [] in the JSON, when there is clearly an Email associated with that member.
I have read on the Mongoose API that the document itself needs to be populated, so I'm trying to do what is referenced at http://mongoosejs.com/docs/api.html#document_Document-populate with the following:
var Member = mongoose.model('Member');
Member.findOne({ _id: req.session.member._id }, function (err, member) {
member.populate('emails', function (err) {
console.log('populated emails');
});
});
But I get the error: (member's JSON data) has no method 'populate'. I know I'm trying to run populate on the wrong thing, but I can't figure out what the correct way would be.
I've also encountered issues with populating array, the schema you defined will create a sub-doc for each array element (try dumping the array).
Try changing the schema to:
emails: [ type: mongoose.Schema.Types.ObjectId ]
and use
Member.findOne().populate({ path: 'emails', model: 'Email' }).exec(function (err, member) {
res.json(200, member);
});
to populate the array.

Categories