Mongoose updating sub document array's individual element(document) - node.js

Schema of group and member are as below:
var group=new Schema({
group_id:Number,
group_name:String,
members:[member]
});
var member=new Schema({
member_id:number,
name:String,
});
Sample document after inserting some record in group collection
[{
_id:55ff7fca8d3f6607114dc57d
group_id:1001,
group_name:"tango mike",
members:[
{
_id:44ff7fca8d3f6607114dc21c
member_id:2001,
member_name:"Bob martin" ,
address:String,
sex:String
},
{
_id:22ff7fca8d3f6607114dc22d
member_id:2002,
member_name:"Marry",
address:String,
sex:String
},
{
_id:44ff7fca8d3f6607114dc23e
member_id:2003,
member_name:"Alice" ,
address:String,
sex:String
}
]
}]
My problem:
I am trying to update record of individual group member(element of subdocument members). While updating I have follwing data group: _id, group_id, members:_id and newdata. I am trying like this; but it is not working
var newData={
member_name:"Alice goda" ,
address:"xyz",
sex:"F"
}
groupModel.findOne({"_id":"55fdbaa7457aa1b9bd7f7cf7","group_id":1001},'members -_id',function(err,groupMembers){
if(err)
{
res.json({
"isError":true,
"error":{
"status":1042,
"message":err
}
});
}
else
{
var mem=groupMembers.id("44ff7fca8d3f6607114dc23e");
mem.member_name=newData.member_name;
mem.address=newData.address;
mem.sex=newData.sex;
mem.save(function(err,data){
if(!err)
//sucessfull updated
});
res.json(groupDetails);
}
});

As I understand from your question details, you would like to update one object from the members array, in accordance with the criteria that you specify.
Thus, in order to accurately run the update query for your use case, you could run the following update operation against your collection:
db.collection.update({ _id: "55ff7fca8d3f6607114dc57d",
group_id:1001,
members: {
$elemMatch: { _id: "44ff7fca8d3f6607114dc23e" }
}
},
{ $set: {
"members.$.member_name": "Alice goda",
"members.$.address": "xyz",
"members.$.sex": "F"
}});
Still, be aware that the $ positional operator only updates the first array item that matches your query.
Unfortunately, there is no possibility of updating all the array elements that match your criteria in a single operation. As you can see on MongoDB Jira, the aforementioned feature is one of the most requested functionality, but it has not yet been directly implemented in MongoDB.

Related

Using Mongoose to query an array of objects

I've got a MongoDB database collection called Dealers structured a bit like this:
{
... dealer info goes here like address etc,
"user_logins": [
{
"Username": "something",
... other stuff
}
]
},{
... next dealer etc...
I'm using Mongoose to try and query on the user_logins.Username using this:
Mongoose model
const myTest = mongoose.Schema({
Username: {
type: "String",
required: true
}
}, { collection: "Dealers" })
module.exports = mongoose.model("Dealer", myTest);
The query
Dealer.find({'user_logins.Username' : 'something'}, (err, result) => {
if (err) {
console.log(err);
} else {
res.json(result);
}
});
All the Username's are distinct. But instead of returning the one matching document, it seems to be returning the whole Dealers collection.
I followed this example.
https://kb.objectrocket.com/mongo-db/use-mongoose-to-find-in-an-array-of-objects-1206
What am I doing wrong please?
Thanks.
EDIT: It seems fine if I try to find something on the root level. EG. Company name, address etc. But if I try to query an imbedded array of objects, that's when it pulls the whole collection. I don't get it.
Found the answer.
My model was wrong. It needed to reflect the actual structure of my data, which does kind of make sense.
This worked:
const myTest = mongoose.Schema({
user_logins: [{
Username: {
type: "String",
required: true
}
}]
}, { collection: "Dealers" })
module.exports = mongoose.model("Dealer", myTest);

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 duplicate items getting inserted using $addToSet and $each to push items into the array

I am trying to push an array of objects into a document. I am using $addToSet to try and not insert duplicate data. I want to do a check on applied.studentId. But if I pass the same request twice, then the data is getting inserted. Is there any check on $addToSet and $each that I have to use?
My schema is as follows
jobId: { type: Number},
hiringCompanyId: String,
applied: [{
studentId: String,
firstName:String,
lastName:String,
gender:String,
identityType:String,
identityValue:String,
email:String,
phone:String,
}],
My node code is as follows.
public ApplyForJob(data: JobDto): Promise<{ status: string }> {
let students = data.applied;
let findQuery = {hiringCompanyId: data.hiringCompanyId, jobId: data.companyJobId};
let appliedQuery = {};
if (!isNullOrUndefined(data.applied.length)) {
appliedQuery = {
"$addToSet": {
"applied": {
"$each": data.applied
}
}
};
}
return new Promise((resolve, reject) => {
Jobs.findOneAndUpdate(findQuery, appliedQuery).exec((err, info) => {
if (err) {
reject(new UpdateError('Jobs - Update()', err, Jobs.collection.collectionName));
} else {
console.log(info);
resolve({status: "Success"});
}
})
});
}
On disabling the date field, $addToSet does not add duplicate values. As per the doc https://docs.mongodb.com/manual/reference/operator/update/addToSet/
As such, field order matters and you cannot specify that MongoDB compare only a subset of the fields in the document to determine whether the document is a duplicate of an existing array element.
as Rahul Ganguly mention absolutely correctly, we cannot use reliably $addToSet with JS objects.
One options is to move applied in to separate collection and make Job schema to ref new Applied model.
Example:
{
jobId: { type: Number },
hiringCompanyId: String,
applied: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Applied'
}]
}

Updating a nested subdocument's array - Mongoose

Currently I have the following structure for one of my documents
Company: {
Buildings: [{
Users: [ { _id: ObjectID, name: String, number: String } ]
}]
}
I'm trying to update the user's name and number and currently have tested and verified the following query in mongo:
db.companies.update(
{ "_id": companyID, "buildings._id": buildingID, "buildings.users._id": userID }
,
{ $set: { "buildings.$.users.0.name": "A new name for the user" } }
);
This query updates correctly however when I run the same exact query using mongoose
Company.findOneAndUpdate(
{ _id: companyID, "buildings._id": buildingID, "buildings.users._id": userID }
,
{ $set: { "buildings.$.users.0.name": newName }})
I get no error but the update is not performed.
Is the updating of a deep nested array not available on Mongoose?
Answer was found in an alternative answer to this question:
Answer: https://stackoverflow.com/a/28952991/1327815

Mongoose count certain element in an embedded documents array

I am using mongoose 4.6.3.
I have the following schema :
var mongoose = require('mongoose');
var User = require('./User');
var TicketSchema = new mongoose.Schema({
user : { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
},
{
timestamps: true
});
var DrawSchema = new mongoose.Schema({
...
max_ticket_per_user : { type : Number, required: true },
tickets: [TicketSchema]
});
module.exports = mongoose.model('Draw', DrawSchema);
How can I count the embedded documents of a certain User ObjectId(user field in TicketSchema) in a Draw's tickets(tickets field in DrawSchema) ?
I want to count the tickets of a user for a single draw.
Would it be better to change my schema design ?
Thanks
You can use the aggregation framework taking advantage of the $filter and $size operators to get the filtered array with elements that match the user id and its size respectively which will subsequently give you the count.
For an single draw, consider adding a $match pipeline operator as your initial step with the _id query to filter the documents in the collection.
Consider running the following aggregation pipeline to get the desired result:
Draw.aggregate([
{ "$match": { "_id": drawId } },
{
"$project": {
"ticketsCount": {
"$size": {
"$filter": {
"input": "$tickets",
"as": "item",
"cond": { "$eq": [ "$$item.user", userId ] }
}
}
}
}
}
]).exec(function(err, result) {
console.log(result);
});
You can pass the .count() deep parameters like any other query object:
Draw.count({'tickets.user._id' : userId}, console.log);
Make sure the userId variable is an ObjectId. If it's a string, do this:
const ObjectId = require('mongoose').Types.ObjectId;
let userId = new ObjectId(incomingStringId);

Resources