Updating an Array at a specific Index using Mongoose - node.js

I have a collection of documents in mongoDB. One of the schema's properties has an array, that contains objects. Each object within this array contains a property that has another array as it's value.
const userSchema = new mongoose.Schema({
username: "",
password: "",
firstName: "",
clients: [],
});
The "clients" property array looks like this:
[
{
clientName: 'Chabad of Closter',
activeInvoice: [],
pastInvoices: []
},
{
clientName: 'Chabad UC',
activeInvoice: [],
pastInvoices: []
},
{
clientName: 'Chabad Mobile',
activeInvoice: [],
pastInvoices: []
}
]
My goal is to push an Object into any of the "activeInvoice" arrays by using the index of its object. I used this code and it works when I specify the index manually:
User.findByIdAndUpdate(id, {"$push": {"clients.2.activeInvoice": newCharge}}, {new : true},
function(err, updatedCharge){
if (err) {
console.log(err)
} else {
console.log(updatedCharge);
}
});
In the example above, I used the "2" index. I need to be able to change that index dynamically. I tried this:
// code to find the index I want and save it to indexer
const indexer = clientsArr.findIndex(i => i.clientName == newCharge.clientName);
// form it into a string
const mongooseLink = "clients." + indexer + ".activeInvoice";
//place it into the mongoose request
User.findByIdAndUpdate(id, {"$push": {mongooseLink: newCharge}}, {new : true},
function(err, updatedCharge){
if (err) {
console.log(err)
} else {
console.log(updatedCharge);
}
});
but this doesn't work. I double checked to make sure the indexer is working. No error, just the document doesn't get updated.

Related

remove a particular array element from mongodb on clicking the element

I'm using this code for removing a particular array element stored in MongoDB when clicked that from the app. But this code is not working.
schema structure looks like this -
const tagsSchema = new Schema({
category: {
type: String,
required: true
},
tname: { type: Array }
}, { _id: true });
Below is the code I'm using for removing array element from db -
Tags.updateOne({ tname: req.params.name }, { $pull: { _id: [req.params.id] } })
For example - "tname": "technical", "nontechnical"
Now, technical is being clicked in the app to remove, but with the code I'm using it's not getting removed.
you can use directly your element_value without [] after your array field, like below..
Tags.updateOne({tname: req.params.name}, { $pull: { your_array_field: req.params.id } } )
You have to find the specific tag by its "_id" and then remove the particular name from the "tname" array.
Tags.updateOne({ _id: req.params.id }, { $pull: { tname: req.params.name } })

Can't push items in mongo array

I can't push items into MongoDB array every time that i try to push a new element it creates an empty object and i cant figure out why,
I already used the
Collection.Array.push({element})&
Collection.save()
but i cant figure out a solution
This is My Schema
const Schema = mongoose.Schema;
var ParticipantSchema = new Schema({
nom:{Type:String},
prenom:{Type:String},
email:{Type:String}
})
var CompetitionSchema = new Schema({
nom:String,
date:Date,
place:String,
participant :[ParticipantSchema]
})
module.exports = mongoose.model("Competition",CompetitionSchema);
This is my funtion
exports.addParticipant=function(req,res){
var newParticipant={
"nom":req.body.nom,
"prenom":req.body.prenom,
"email":req.body.email
}
Competition.updateOne(
{ _id:req.body.id},
{ $push: { participant: newParticipant } },
(err,done)=>{
return res.json(done)
}
);
}
the result is always an empty object like below
{
"_id": "5ded0eeb85daa100dc5e57bf",
"nom": "Final",
"date": "2019-01-01T23:00:00.000Z",
"place": "Sousse",
"participant": [
{
"_id": "5ded0eeb85daa100dc5e57c0"
},
{
"_id": "5dee3c1b08474e27ac70672e"
}
],
"__v": 0
}
There is no problem in your code, the only problem is that in schema definition you have Type, but it must be type.
If you update your ParticipantSchema like this, it will work:
var ParticipantSchema = new Schema({
nom: { type: String },
prenom: { type: String },
email: { type: String }
});
You are using another Schema in the Array. This results in so-called subdocuments (https://mongoosejs.com/docs/subdocs.html). Mongoose does not populate subdocuments by default. So all you see is just the _id. You can use the populate method to see all subdocuments in detail. ( https://mongoosejs.com/docs/populate.html ) .
Example :
Competition.
find({}).
populate('participant').
exec(function (err, comps) {
//
});
You can either use populate on the Model or on the Document. For populating a document, take a look at https://mongoosejs.com/docs/api.html#document_Document-populate . There is also a auto-populate plugin available via npm but in most cases it's not necessary : https://www.npmjs.com/package/mongoose-autopopulate .

Mongoose how to auto add _id to objects in array within collection item?

i have a mongo collection that looks like this:
{
name: string
_id: (auto set)
items: array[
name: string
url: string
items: array[
{
name: string,
url: string,
items: []
}
]
]
}
I'm using findByIdAndUpdate (with mongoose) to add an item into the items array:
Menu.findByIdAndUpdate(
req.body.parentid,
{
$push: {
items: {
name: req.body.item.name,
url: req.body.item.url,
items: []
}
}
},
{
safe: true,
upsert: true,
new: true
},
function(err, model) {
if (err !== null) {
console.log(err);
}
}
);
This works fine, but it does not add an _id to each object inserted into the items array. And i really need an id for each one.
I'm guessing it comes from the method used, findByIdAndUpdate as it looks more like an update rather than an insert. If my thinking is correct.
Using mongodb 3.2.10 and mongoose 4.7.6.
Any help would be really appreciated.
Thanks.
EDIT: the _id: (auto set) is not real, it's being automatically added via mongo. But just at the top level objects.
Found the solution in this thread: mongoDB : Creating An ObjectId For Each New Child Added To The Array Field
basically, added
var ObjectID = require('mongodb').ObjectID;
and then forcing the creation:
$push: {
items: {
_id: new ObjectID(),
name: req.body.item.name,
url: req.body.item.url,
items: []
}
}
You dont need to sepcify _id: (auto set) in mongoose schema it will automatically add unique _id with each document.
if you don't define _id in Schema, mongoose automatically add a _id to array item.
for example:
const countrySchema = new Schema({
name: {
type: String
},
cities: [
{
// don't define _id here.
name: String
}
],
});
now when you insert a row, the result is something like this:
{name : 'Iran', cities : [{_id : 6202902b45f0d858ac141537,name :
'Tabriz'}]}

Mongoose Insert many to one

I need to help!
I'm creating a website with nodejs and mongo for learning.
I have a problem that I know the best way to do it.
I have two collections codes and tag into table codes I have the tags field is array of tags.
CodeModel:
var CodeSchema = new Schema({
title: { type: 'String', required: true },
text: { type: 'String', required: true },
url: { type: 'String', required: true },
uri: String,
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
owner: {
type: Schema.ObjectId,
ref: 'User'
},
tags: [
{
type: Schema.ObjectId,
ref: 'Tag'
}
]
});
CodeSchema.pre("save", function (next) {
// if create for first time
if (!this.created_at) {
this.created_at = Date.now();
}
next();
});
module.exports = mongoose.model('Code', CodeSchema);
And My Tag Model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TagSchema = new Schema({
name: 'string'
});
module.exports = mongoose.model('Tag', TagSchema);
when I get the result in my rest I got it:
[
{
"_id": "5540f557bda6c4c5559ef638",
"owner": {
"_id": "5540bf62ebe5874a1b223166",
"token": "7db8a4e1ba11d8dc04b199faddde6a250eb8a104a651823e7e4cc296a3768be6"
},
"uri": "test-save",
"url": "http://www.google.com.br/",
"text": " hello ",
"title": "testing...",
"__v": 0,
"tags": [
{
"_id": "55411700423d29c70c30a8f8",
"name": "GO"
},
{
"_id": "55411723fe083218869a82d1",
"name": "JAVA"
}
],
"updatedAt": "2015-04-29T15:14:31.579Z",
"createdAt": "2015-04-29T15:14:31.579Z"
}
]
This I populate into database, I don't know how I insert it, is there any way automatic with mongoose that to do it or I need to create by myself?
I am testing with this json:
{
"url": "http://www.google.com.br/",
"title": "Test inset",
"text": "insert code",
"tags": [
"ANGULAR",
{
"_id": "55411700423d29c70c30a8f8",
"name": "GO"
}
]
}
I need to do a insert of tags, if I have id or not. Do I need to create it or has way to do it automatically?
and how can I do it?
Sorry my english =x
Generally speaking to create and save a document in a mongo database using mongooseJS is fairly straightforward (assuming you are connected to a database):
var localDocObj = SomeSchemaModel(OPTIONAL_OBJ); // localDocObj is a mongoose document
localDocObj.save(CALLBACK); // save the local mongoose document to mongo
If you have an object that is of the same form as the schema, you can pass that to the constructor function to seed the mongoose document object with the properties of the object. If the object is not valid you will get an invalidation error passed to the callback function on validate or save.
Given your test object and schemas:
var testObj = {
"url": "http://www.google.com.br/",
"title": "Test inset",
"text": "insert code",
"tags": [
"ANGULAR",
{
"_id": "55411700423d29c70c30a8f8",
"name": "GO"
}
]
};
var codeDoc = Code(testObj);
codeDoc.save(function (err, doc) {
console.log(err); // will show the invalidation error for the tag 'Angular'
});
Since you are storing Tag as a separate collection you will need to fetch/create any tags that are string values before inserting the new Code document. Then you can use the new Tag documents in place of the string values for the Code document. This creates an async flow that you could use Promises (available in newer node releases) to manage.
// Create a promise for all items in the tags array to iterate over
// and resolve for creating a new Code document
var promise = Promise.all(testObj.tags.map(function(tag) {
if (typeof tag === 'object') {
// Assuming it exists in mongo already
return tag;
}
// See if a tag already exists
return Tag.findOne({
name: tag
}).exec().then(function(doc) {
if (doc) { return doc; }
// if no tag exists, create one
return (Tag({
name: tag
})).save(); // returns a promise
});
})).then(function(tags) {
// All tags were checked and fetched/created if not an object
// Update tags array
testObj.tags = tags;
// Finally add Code document
var code = Code(testObj);
return code.save();
}).then(function(code) {
// code is the returned mongo document
console.log(code);
}).catch(function(err) {
// error in one of the promises
console.log(err);
});
You can do it like
var checkNewTagAndSave = function(data, doc, next){ // data = req.body (your input json), doc = mongoose document to be saved, next is the callback
var updateNow = function(toSave, newTags){
// save your mongoose doc and call the callback.
doc.set(toSave);
doc.save(next);
};
var data = req.body;
var tagsToCreate = [];
var tagids = [];
data.tags.forEach(function(tag, index){
if(typeof(tag) == 'string') {
tagsToCreate.push({ name: tag });
} else tagids.push(tag._id);
});
data.tags = tagids;
if(tagsToCreate.length === 0) updateNow(data);
else {
mongoose.model('tag').create(tagsToCreate, function(err, models){
if(err || !models) return next(err);
else {
models.forEach(function(model){
data.tags.push(model._id);
});
updateNow(data, models);
}
});
}
};
Hope code is reflecting its logic itself
usage :
after you have found your Code document say aCode
just call
checkNewTagAndSave(req.body, aCode, function(err, doc){
//end your response as per logic
});

ExpressJS and MongooseJS: can I query array values in a subdocument?

I've got a web api written using expressjs and mongoosejs.
The main schema in the app contains a subdocument, permissions, which contains array fields. Those arrays contain ids of users who can perform an action on that document.
I'm trying to limit results of a query on that collection by the values in the read subdocument array field:
Here's the main schema:
var MainSchema = new Schema({
uri: { type: String, required: false },
user: { type: String, required: false },
groups: [String],
tags: [String],
permissions: {
read: [String],
update: [String],
delete: [String]
}
});
I want to return documents that have a specific value in the permissions.read array or where that array is empty.
My code doesn't throw an error, but doesn't limit the results; I still get documents that don't match the given value, and aren't empty.
Here's what I've got:
var MainModel = mongoose.model('MainSchema', MainSchema);
app.get('/api/search', function (req, res) {
var query = MainModel.find({'uri': req.query.uri });
// This works fine
if (req.query.groups) {
query.where('groups').in(req.query.groups);
}
else if (req.query.user) {
query.where('user').equals(req.query.user);
}
// How can I return only documents that match req.query.user or ""?
query.where('permissions.read').in([req.query.user, ""]);
query.exec(function (err, results) {
if (!err) {
return res.send(results);
} else {
return console.log(err);
}
});
});
I have a hunch that the where clause is not testing the value of each of the elements of permissions.read against each of the values of the array passed to the in clause.
Thanks.
EDIT: Here's a document that shouldn't be returned, but is (note, permissions.read array includes a value that's not the current user's ID):
{
"user": "firstname#gmail.com",
"uri": "http://localhost:3000/documents/test",
"permissions": {
"delete": [
"firstname#gmail.com"
],
"update": [
"firstname#gmail.com"
],
"read": [
"firstname#gmail.com"
]
},
"tags": [],
"groups": [
"demo",
"2013"
]
}
EDITED: Corrected Model/Schema confusion, which wasn't in my code, but was left out in the copy/paste. Thanks.
I have looked for and not found an example of a mongodb/mongoose query that tests either for a specified value in a subdocument array field or an empty array field.
Instead, since I control both the API (which this code comes from) and the client application, I refactored.
I now add one of three where clauses to the query, based on client-side user selection:
if (req.query.mode === 'user') {
query.where('user').equals(req.query.user);
}
else if (req.query.mode === 'group') {
query.where('groups').in(req.query.groups);
query.$where('this.permissions.read.length === 0');
}
else if (req.query.mode === 'class') {
query.$where('this.permissions.read.length === 0');
}
Thanks for your input, #JohnnyHK.

Resources