Update mongodb document that has multiple arrays of embedded documents using mongoose - node.js

Say i have a document that looks like this:
{
"personId": 13998272,
"address": [
{
"addressType": "HOME",
"streetNo": 21,
"addressLine1": "LORRAINE AVENUE",
"addressLine2": "EDGEWATER",
"city": "KINGSTON",
"parish": "ST ANDREW",
"country": "JAMAICA",
"qScore": 0.9,
"modifiedDate": "2019-02-17 15:24:19"
}
],
"phone": [
{
"originalNumber": "+18767842983",
"phoneNumberIFormat": "+18768514679",
"phoneNumberLFormat": "8768514679",
"qualityScore": 0.8,
"dataSource": "PERSON",
"modifiedDate": "2018-12-17 09:42:31"
}
],
"email": [
{
"emailAddress": "neilagreen78#yahoo.com",
"dataSource": "FINACLE",
"qualityScore": 0.89,
"modifiedDate": "2018-12-17 09:38:41"
}
]
}
My schema is defined in the code snippet below for reference:
const contactSchema = new mongoose.Schema({
pid: Number,
address: [
new mongoose.Schema({
addressType: String,
streetNo: String,
addressLine1: String,
addressLine2: String,
city: String,
parish: String,
country: String,
qScore: String,
modifiedDate: String
})
],
phone: [
new mongoose.Schema({
originalNumber: String,
phoneNumberIFormat: String,
phoneNumberLFormat: String,
qualityScore: Number,
dataSource: String,
modifiedDate: String
})
],
email: [
new mongoose.Schema({
emailAddress: String,
dataSource: String,
qualityScore: Number,
modifiedDate: String
})
]
});
How would update each array of embedded documents without overwriting the others?
Say a request is with and address and email object but not phone, how would I handle that?

You can try this ..
Get the structure of the contact object then the check to see what properties were sent in the req.body and build out the query accordingly.
N.B: You must have some validation to check the request body to ensure no unwanted properties are sent. You can use a package like Joi
const getContact = await contact.findOne({ id: req.params.id });
let query = { $addToSet: {} };
for (let key in req.body) {
if (getContact[key] && getContact[key] !== req.body[key])// if the field we have in req.body exists, we're gonna update it
query.$addToSet[key] = req.body[key];
}
const contact = await Customer.findOneAndUpdate(
{ pid: req.params.id },
query,
{new: true}
);

With mongoose, you can use $push to push object to array. The query will be like:
(saving an address and an email, querying on pid)
db.getCollection("contactSchema").update({"pid":1}, {$push: {email: emailToPush,
address:addressToPush}})
{"pid":1} is the pid of the object you want to update;
{$push: {email: emailToPush, address:addressToPush}} is the object you want to push on each array
Then you have to filter the body of the request with a middleware or something like that. I usually use a middleware to check if the request is correct, like:
EDIT:
const buildQuery = (requestBody) => {
let query = {$push: {}};
Object.keys(requestBody).map(key => {
query.$push[key] = requestBody[key];
});
}
This will build your query object (the second parameter of the update function).

Related

$push into deeply nested array mongoose

my userSchema is as follows...
const userSchema = new mongoose.Schema({
firstName: { type: String },
lastName: { type: String },
movies: [
{
name: String,
duration: Number,
actors: [{ name: String, age: Number }]
}
],
});
In my NodeJS app with express I am trying to update my actors array to have another actor with value stroking from req.body.
I thought that I could do something like this...
await User.updateOne(
{
_id: req.user.id
movies._id: req.params.id
},
{
$push: { 'movies.actors': req.body }
}
)
I thought this would push an actor into the specified movie req.params.id but does nothing instead.
Try using positional operator $ in this way:
db.collection.update({
"_id": 1,
"movies._id": 1
},
{
"$push": {
"movies.$.actors": req.body
}
})
Note the only diference is the use of $. But with positional operator you are saying mongo where push the new data.
As docs says:
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array.
So you can update the element movies.actors pushing new data without knowin the position of the element.
Example here
Try this:
await user.updateOne(
{$and:[{_id: req.user.id},{movie.name: 'I am Legend'}]},
{$set: { movies.actors:req.body}},
);

Mongoose findById

I am trying desperately to find a object stored with mongodb, with nodejs and mongoose.
The model of the object looks like:
const SimpleResourceSchema = new mongoose.Schema(
{
_id: String,
title: String,
objective: String,
url: String,
content: String,
active: Boolean,
type: String,
owner: String,
},
{
timestamps: true,
// _id: false,
}
);
export const SimpleResourceModel = mongoose.model<
SimpleResource & mongoose.Document
>('simpleResource', SimpleResourceSchema);
The query is made with 'id' parameter value '5f1da9737917360dd038bfc0':
return await SimpleResourceModel.findById(id).exec();
The data stored in mongodb is:
{
"_id": {
"$oid": "5f1da9737917360dd038bfc0"
},
"title": "Learn cooking",
"objective": "<p>Is the fridge empty ?</p>",
"content": "...",
"url": "..",
"active": true,
"type": "simple",
"owner": "5efceb2f63b75c1750846b0a",
"createdAt": {
"$date": "2020-07-26T16:04:03.806Z"
},
"updatedAt": {
"$date": "2020-07-26T16:04:03.806Z"
},
"__v": 0
}
I have looked around to get a solution, but have not found any solution to this roadblock.
Anyone can help ?
The main issue that when you define the schema you defined the id as string remove _id: String from schema definition. and it automatic be added.
If you want to add _id to typescript you can create interface
export interface SimpleResource extends Document {
_id: schema.Types.ObjectId,
...
}
then in model you directly add it but _id already defined in Document interface
and make sure that you install #types/mongoose
export const SimpleResourceModel = mongoose.model<SimpleResource>('simpleResource', SimpleResourceSchema);
Have you tried?
var ObjectId = require('mongoose').Types.ObjectId;
return await SimpleResourceModel.findById(new ObjectId(id)).exec();
i still get a null response when I try:
await SimpleResourceModel.findById(mongoose.Types.ObjectId(id)).exec()

Nested MongoDB document issue (Mongoose and Node Js)

I am facing some issues while inserting data into nested documents structure of mongoDb.
Following is the Mongoose Model:
const funnel = new mongoose.Schema({
funnelName:{
type:String,
unique:true
},
group: String,
category: String,
funnelStep: {
stepType: String,
stepName: String,
stepPath: String,
isTracking: Boolean,
viewsStorage: []
} })
Below is the push I am sending to Db:
router.post('/createFunnel',async (req,res)=>{
if(!req.body.funnelName || !req.body.group || !req.body.category)
{return res.status(422).json({error:"Please add all the fields."})}
try{
const funnelSteps = []
funnelSteps.push({
stepType: req.body.stepType,
stepName: req.body.stepName,
stepPath: req.body.stepPath,
isTracking: req.body.isTracking,
viewsStorage: req.body.viewsStorage
})
const funnels = new Funnel({
funnelName : req.body.funnelName,
group : req.body.group,
category : req.body.category,
funnelStep : funnelSteps
})
await funnels.save(function(err){
if(err){
return res.status(422).send({error: err.message})
}
return res.json(funnels)
})
} catch(err){
return res.status(422).send({error: err.message})
} })
Below is the data structure I am sending through postman:
{
"funnelName":"Name-Funnel",
"group":"AVC",
"category":"XYZ",
"funnelStep":[
{
"stepType":"Advert",
"stepName":"Angle",
"stepPath":"google.com",
"isTracking":1,
"viewsStorage":[0,0]
},
{
"stepType":"Optin",
"stepName":"Ver 1",
"stepPath":"fb.com",
"isTracking":1,
"viewsStorage":[1,0]
},
{
"stepType":"Check",
"stepName":"rev-cat",
"stepPath":"google.com",
"isTracking":0,
"viewsStorage":[2,0]
}
] }
Below is the output I am getting in response:
{
"funnelStep": {
"viewsStorage": []
},
"_id": "5ec0ff78a6dfab18f4210e96",
"funnelName": "Testing The Latest Method4",
"group": "AVC",
"category": "XYZ",
"__v": 0
}
How can I fix this issue as my data is not getting inserted properly?
And apart from this, in the viewsStorage array, how to store date and a number which will increment after a certain operations and will get saved in the array according to the dates?
I think there is an issue in the funnelSteps array creation part. You are trying to get data directly from req.body instead of req.body.funnelStep
const funnelSteps = []
req.body.funnelStep.forEach(fs => {
funnelSteps.push({
stepType: fs.stepType,
stepName: fs.stepName,
stepPath: fs.stepPath,
isTracking: fs.isTracking,
viewsStorage: fs.viewsStorage
})
})
Schema
const funnel = new mongoose.Schema({
funnelName:{
type:String,
unique:true
},
group: String,
category: String,
funnelStep: [{
stepType: String,
stepName: String,
stepPath: String,
isTracking: Boolean,
viewsStorage: []
}] })

inserting an objet with nested arrays in mongodb with moongose, nodejs

I am trying to insert an object like this in mongodb.
{
"client" : "Jhon"
"items" : [
[
"item": "whatever1",
],
[
"item": "whatever2",
]
]
}
I am using Mongoose, so i have a schema like this.
const itemSchema= new Schema({
item: { type: String, required: true },
})
const clientSchema = new Schema({
client: { type: String, required: true },
items: [itemSchema],
})
After i send the object to my nodejs server and create the document, i check the document created and "items" is an array that only contain _id: but nothing more.
I create the document like this.
createClient = (req, res) => {
console.log(req.body); // Here i check what is receiving the server.
clientModel.create(req.body).then(() => res.json('saved'));
}
In req.body, i checked that the server is receiving an object with empty arrays... is that normal? I am learning and i am new programing...
Your payload does not seem to be correct.
[ "item": "whatever1" ]
is not valid JSON and won't be parsed properly. Try changing your payload to:
{
"client": "Jhon"
"items": [
{ "item": "whatever1" },
{ "item": "whatever2" }
]
}

GraphQL: Resolver for nested object types in graphql

Lets assume in the graphql schema, a UserType object is present:
const UserType = new GraphQLObjectType({
name:'User',
fields:() => ({
id: {type:GraphQLString},
name: {type: GraphQLString},
email: {type: GraphQLString},
age: {type: GraphQLInt},
friends: {type: new GraphQLList(UserType)}
});
});
The following data is present in the database:
{
"Users":[
{
"id": "1",
"name": "John Doe",
"email": "John#gmail.com",
"age": 35,
"friends": [
"3",
"5",
"7"
]
}
]
}
Query:
user {
name
friends {
name
}
}
As can be seen in the above example, the friends with ids are stored in the database.
How do I go about writing a resolver to get the user details (by id) plus details of all the users friends at the same time by sending a single graphql request?
The resolver takes four arguments (obj, args, ctx, info).
The first argument, in this case, obj has the result of the resolver from the parent's object (The parent resolver to the UserType). Therefore if you do obj.friends in the friends field's resolver, you will get the correct result.
Example:
friends: {
type: new GraphQLList(GraphQLString),
resolve: (obj) => {
return obj.friends
}
}

Resources