save Embed docs as an object using mongoose? - node.js

I have a schema like this
const user = new Schema({
firstName: { type: String, required: true },
lastName: { type: String , required: true},
phone:{type: Number, unique true}
embeddedDocsAsJson: {} // not as an array
},
{ minimize: false }
)
I want to use embeddedDocsAsJson because of two reasons
In case of array a duplicate data can be pushed to array , if I use json it will not occur as I'll use unique id as json key
Retrieval will be faster as I don't have to iterate on the array . I can fetch it from the json key
Problem:
Firstly I'm inserting firstName and lastName phone.
And embeddedDocsAsJson is added while updating the docs below is my code for updating
let user = await User.findOne({phone: somenumber})
user.embeddedDocsAsJson.someId = someObject // getting error in this line because `user.embeddedDocsAsJson` is `undefined`
user.save()
I'm adding value to embeddedDocsAsJson while updating

EmbeddedDocs are by-default array if you want to save object in your collection below code will work.
let user = await User.findOne({phone: somenumber})
user.embeddedDocsAsJson = {}
user.embeddedDocsAsJson.someId = someObject // getting error in this line because `user.embeddedDocsAsJson` is `undefined`
user.save()

Related

Node Js and Moongoose: How to loop into array of objects and delete a particular object and then update the whole document

My document schema is as follows:
const CollectionSchema = mongoose.Schema({
ImageCategory:{type:String,required:true},
imgDetails: [
{
_id: false,
imageUrl:{type:String},
imageName:{type:String},
imageMimeType:{type:String},
}
],
Date: {
type: String,
default: `${year}-${month}-${day}`,
},
},{timestamps: true,})
So in the database for example one document has multiple images with a single image category. What I am trying to do is I want to delete an object from imgDetails array.
Let me explain my question more precisely: imgDetails is an array
Explanation: I want to loop in imgDetails and then find (where imgageUrl === req.body.imageUrl) if its match delete that whole object which have that req.body.imageUrl and then update the document.
Please guide me on how to write such a query. Regards
Demo - https://mongoplayground.net/p/qpl7lXbKAZE
Use $pull
The $pull operator removes from an existing array all instances of a value or values that match a specified condition.
db.collection.update(
{},
{ $pull: { "imgDetails": { imageUrl: "xyz" } } }
)

how to query mongoDb with array of boolean fields?

My mongoose model schema looks like -
{
email: { type: String},
Date: {type: Date},
isOnboarded: {type: Boolean},
isVip: {type: Boolean},
isAdult: {type: Boolean}
}
In my frontend I have 3 checkboxes for "isVip", "isOnboarded" and "isAdult" options. If they are checked I'm adding them to an array, which I'll pass to the server. Let's say if "isVip" and "isAdult" are checked, I will pass [isVip, isAdult] in post api to server. Now how can I write a query to get all the documents with the fields in array as true i.e in above example how can I retrieve all docs with {isVip: true, isAdult:true}
I'm having trouble because the array values keep changing, it can be only one field or 3 fields. I couldn't find a way to give condition inside mongoose query.
User.find(
{ [req.query.array]: true},
{ projection: { _id: 0 } }
)
User is my mongoose model.
I want something like this (documents with the value 'true' for the fields given in the array) and 'req.query.array' is the array with field names I passed from frontend.
You have to create your object in JS and pass then to mongo in this way:
var query = {}
if(isVip) query["isVip"] = true;
if(isOnboarded) query["isOnboarded"] = true;
if(isAdult) query["isAdult"] = true;
And then you can use the mongoose method you want, for example:
var found = await model.find(query)
And this will return the document that matches the elements.
Also, note that this is to create and object to be read by the query, so you can use this into an aggregation pipeline or whatever you vant
Check the output:
var query = {}
query["isVip"] = true;
query["isOnboarded"] = true;
query["isAdult"] = true;
console.log(query)
Is the same object that is used to do the query here
{
"isVip": true,
"isOnboarded": true,
"isAdult": true
}
Also, to know if your post contains "isVip", "isOnboarded" or "isAdult" is a javascript question, not a mongo one, but you can use something like this (I assume you pass a string array):
var apiArray = ["isVip","isAdult"]
var isVip = apiArray.includes("isVip")
var isAdult = apiArray.includes("isAdult")
var isOnboarded = apiArray.includes("isOnboarded")
console.log("isVip: "+isVip)
console.log("isAdult: "+isAdult)
console.log("isOnboarded: "+isOnboarded)

Mask data in Mongoose find operation

The requirement is to mask mobile number and show only last 4 digits. I do not want this to be performed at client instead mask it before sending the response. I am not sure how to modify transaction object to mask the data. I want to check if there is any mongoose function to do this. If not please suggest me the best way to mask a selected field.
Logic to fetch transactions
Transaction.find(query).populate('from','name mobile email').sort({ createdAt : -1 }).skip((page) * limit).limit(limit).exec((err, transaction) =>{
if(transaction){
Transaction.countDocuments({to:id,paymentStatus:"SUCCESS"},function(err,count){
return res.status(200).send({transaction,count:count});
});
}
else if(transaction==null) return res.status(200).send("No Transactions Found");
else if(err) return res.status(400).send("Error Occurred");
});
User.Model.ts - Merchant model is similar with some additional fields
var User= new mongoose.Schema({
email:{type:String,required:"E-Mail address cannot be empty",unique:true},
mobile:{type:String,required:"Mobile number cannot be empty",min : [10,"Please provide a valid 10 digit mobile number"],unique:true},
password:{type:String,required:"Password cannot be empty",minlength : [4,"Password must be more than 4 characters"]},
.......some unrelated fields...
});
Transaction.Model.ts
var transactionSchema = new mongoose.Schema({
from:{ type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
amount : {type:String,required:true},
to:{ type: mongoose.Schema.Types.ObjectId, required: true, ref: 'Merchant' },
paymentStatus:{type : String, default : "INCOMPLETE"},
.......some unrelated fields...
});
Current output
{"transaction":[{"paymentStatus":"SUCCESS","isDisputed":true,"_id":"5eb8e50b3e2adb3b74e85d4f","from":{"_id":"5eb8e50a3e2adb3b74e85d43","name":"John Doe","email":"test#gmail.com","mobile":"9999999999"},"amount":"40","to":"5eb8e50a3e2adb3b74e85d46"}],"count":1}
Expected output
{"transaction":[{"paymentStatus":"SUCCESS","isDisputed":true,"_id":"5eb8e50b3e2adb3b74e85d4f","from":{"_id":"5eb8e50a3e2adb3b74e85d43","name":"John Doe","email":"test#gmail.com","mobile":"*******999"},"amount":"40","to":"5eb8e50a3e2adb3b74e85d46"}],"count":1}
You can use string-masking to mask the fields after you fetch them.
Mongoose plugin, virtuals or getters would also involve you to iterate over the array so the end result is same.
let stringMasking = require('string-masking');
...
transactions = transactions.map(transaction => {
let mask = stringMasking(transaction.from.phone, 0);
transaction.from.phone = mask.response;
return transaction;
});
...
return res.status(200).send({transaction,count:transaction.length});
Also its better to make the password not included in all find queries if not needed. Can be done by :
password: {type: String,select: false}

Access a specific property in Object.keys adonis.js (Node.js)

I have a query that returns an array, with Object.key (array) .foreach I am iterating, I want to know the value of a property in specific array.
Example:
Object.keys(arreglo).forEach(function(key) {
console.log(arreglo[key]);
});
The output is:
name: "Pepito",
Surname: "Perez"
I want to know how to get only the value of the surname
I know it will not work but it would be something like:
console.log(arreglo[key].surname);
You can use Array.forEach on the original array as shown below. You can even extract the fields you are interested using Array.map.
// let's assume the arrary you got from your query is in this format
const arreglo = [
{ firstname: "fn1", surname: "ln1"},
{ firstname: "fn2", surname: "ln2"},
{ firstname: "fn3", surname: "ln3"}
];
// you can log `surname` for each entry in the array
arreglo.forEach(v => console.log(v.surname));
// you can use map to transform the array to just have `surname` using array.map()
const surnames = arreglo.map(v => v.surname);
console.log(surnames);
Is this what you are looking for
const object1 = {
a: {firstname:"sali",lastname:"mali"},
b: {firstname:"sali",lastname:"mali"},
c: {firstname:"sali",lastname:"mali"}
};
Object.keys(object1).forEach(function(key){console.log(object1[key].lastname)});

Check if Obejct is Empty: Mongoose Nested/Embedded Document

In my documents pre save hook, I check if all the properties from the nested property accident exist and have correct values. If accident does not exist at all this is also fine.
When I'm trying to save a doc which has no value on the nested property accident of my doc it still goes into my custom validation rules. This is because I fail to detect if this nested object is empty.
Property in the Schema
prescriptionSchema = mongoose.Schema({
accident: {
kind: String, #accident, workAccident,
date: Date,
company: String
},
...
Pre Save Hook
console.log _.isEqual(data.accident, {}) #false
console.log JSON.stringify data.accident #{}
console.log JSON.stringify data.accident == JSON.stringify {} #false
console.log JSON.stringify {} #{}
console.log Object.keys(data.accident).length #14
for key, value of data.accident
console.log key, value #gives me basically the whole document with functions etc.
Current Detection (not good code)
if data.accident? && (data.accident['kind'] || data.accident['date'] || data.accident['company']) #=> do validation
Seed
newRecipe = new RecipeModel()
for key, value of recipe
newRecipe[key] = value
newRecipe.save((err, result) ->
return next err if err?
return next "No Id" if !result._id?
return next null, result._id
)
I tried {}, null and nothing as values for recipe.accident.
Mongoose Version: 4.0.2
Node Version: 5.9
This solution worked although I now have _ids on the embedded objects which I don't need.
I moved out the object from the main schema and defined it as a own sub schema:
drugSchema = mongoose.Schema({
name: String,
exchangeable: Boolean
})
accidentSchema = mongoose.Schema({
kind: String, #accident, workAccident,
date: Date,
company: String
})
prescriptionSchema = mongoose.Schema({
drugs: [drugSchema],
dutiable: Boolean,
accident: accidentSchema,
...
After this, the object is undefined in the pre save hook.
By default, mongoose won't save empty objects. You need to disable the minimize option in your Schema.
prescriptionSchema = mongoose.Schema({
accident: {
kind: String, #accident, workAccident,
date: Date,
company: String
},
...
}, {
minimize: false
})
Source : http://mongoosejs.com/docs/guide.html#minimize
UPDATE : If it is not saving the object, you can try to use the markModified() method (http://mongoosejs.com/docs/schematypes.html#mixed).
And for your checking problem, you can perform a function which will check your fields. Here's two examples : https://jsfiddle.net/ew2um2dh/13/

Resources