Push data in nested arrays nodeJs, MongoDB - arrays

I Have Document Like,
{
"_id" : ObjectId("5ab4cc12c773133bae3d8dc9"),
"__v" : 19.0,
"geoGraphicalFilter" : {
"aCountries" : [
{
"country" : "Spain",
"_id" : ObjectId("5ad49ab21210c42aa6ccba23"),
"cities" : [ ]
},
{
"country" : "India",
"_id" : ObjectId("5ad49ab21210c42aa6ccba24"),
"cities" : [ ]
}
]
}
}
Model
const HuntingWindow = new Schema({
geoGraphicalFilter: {
aCountries: [
{
country: String,
cities: { type: Array }
}
]
},
});
Snippet
const addCityFilter = (req, res) => {
if (req.body.aCities === "") {
res.status(409).jsonp({ message: adminMessages.err_fill_val_properly });
return false;
} else {
var Cities = req.body.aCities.split(",");
huntingModel.update({ _id: req.body.id,"data.geoGraphicalFilter.aCountries":req.body.sName },
{$push:{"geoGraphicalFilter.0.aCountries.$.cities":Cities}},
(error, data) => {
// Cities.forEach(element => {
// data.geoGraphicalFilter.aCountries.find(req.body.sName)
// });
data.save((error, success) => {
res.status(200).jsonp({
message: adminMessages.succ_countryFilter_added
});
});
});
}
};
Now I want to first Find Document by root id and then I want to match the country name and insert the cities in the Array. I am new to MongoDB and nodejs how can i do this ? While i am trying to with update query but i thing i am doing it on wrong way plese help me with it.

Try following code:
huntingModel.update(
{ _id: ObjectId("5ab4cc12c773133bae3d8dc9"), "geoGraphicalFilter.aCountries.country": "Spain" },
{ $addToSet: { "geoGraphicalFilter.aCountries.$.cities": { $each: [ "city1", "city2" ] } } }
)
You should specify two filtering conditions: one for entire document and one to match array element. Then you can use $ operator to update first matching document in that array. To push multiple values you can use $each operator. To ensure that there will be no duplicates you should use $addToSet

Related

Update nested array object value by replacing substring of that object property

I have the following set of data in my MongoDB collection called orders:
{
"_id" : ObjectId("618e0e1b17687316dcdd6246"),
"groupUID": "abc",
"orderData" : {
"charges" : {
"total" : 18480.0,
"subtotal" : 13980.0
},
"items" : [
{
"name" : "Chocolate cookies",
"imageURL": "domainURL2.com/cookies"
},
{
"name" : "Chocolate muffins",
"imageURL": "domainURL2.com/muffins"
}
]
}
}
Now I want to update the imageURL substring part of "domainURL2" to "domainURL1" field in every document of this collection. I have the following query so far:
db.orders.update(
{
"groupUID" : "abc"
},
{ "$set": { "orderData.items.$.imageURL":"myURL.com" } } )
I also have the query in JavaScript form but I want this to be in pure Mongo query. So the query below will not do it for me unfortunately.
db.getCollection("orders").find({"groupUID" : "abc"}).forEach(function(aRow) {
if (aRow.orderDetails !== undefined) {
var updated = false;
aRow.orderData.items.forEach(function(item) {
item.imageURL = item.imageURL.replace("eddress/", "noknok-app/");
})
db.getCollection("orders").save(aRow);
}
});
I want to update all records' imageURL field's substring part. I am unable to figure out the rest of the query. Can anyone please help me?
My answer may look complex (Welcome for suggestion/improvement).
Work the update with Aggegration Pipeline.
$set - Update orderData.items field.
1.1. $map - Iterate orderData.items and returns new array.
1.1.1. $mergeObjects - Merge current object and imageURL field from 1.1.1.1.
1.1.1.1. $cond - With $regexMatch to find the imageURL starts with "domainURL2.com".
1.1.1.2. If true, then replace "domainURL2.com" with "domainURL1.com".
1.1.1.3. If false, remain existing value.
db.collection.update({
"groupUID": "abc"
},
[
{
"$set": {
"orderData.items": {
$map: {
input: "$orderData.items",
in: {
$mergeObjects: [
"$$this",
{
imageURL: {
$cond: {
if: {
$regexMatch: {
input: "$$this.imageURL",
regex: "^domainURL2.com"
}
},
then: {
$concat: [
"domainURL1.com",
{
$arrayElemAt: [
{
$split: [
"$$this.imageURL",
"domainURL2.com"
]
},
-1
]
}
]
},
else: "$$this.imageURL"
}
}
}
]
}
}
}
}
}
])
Sample Mongo Playground
Another approach is using $replaceOne (suggested by #rickhg12hs) which will be much easier.
$replaceOne to replace for 1.1.1.1.
db.collection.update({
"groupUID": "abc"
},
[
{
"$set": {
"orderData.items": {
$map: {
input: "$orderData.items",
in: {
$mergeObjects: [
"$$this",
{
imageURL: {
$replaceOne: {
input: "$$this.imageURL",
find: "domainURL2.com",
replacement: "domainURL1.com"
}
}
}
]
}
}
}
}
}
])
Sample Mongo Playground ($replaceOne)

Write a mongo query to count the data where similar data in array?

Sample data: there are multiple similar collection:
{
"_id" : NumberLong(301),
"telecom" : [
{
"countryCode" : {
"value" : "+1"
},
"extension" : [
{
"url" : "primary",
"value" : [
"true"
]
}
],
"modifiedValue" : {
"value" : "8887778888"
},
"system" : {
"value" : "phone"
},
"useCode" : {
"value" : "Home Phone"
},
"value" : {
"value" : "8887778888"
}
},
{
"extension" : [
{
"url" : "primary",
"value" : [
"true"
]
}
],
"modifiedValue" : {
"value" : "abc#test.com"
},
"system" : {
"value" : "email"
},
"useCode" : {
"value" : "work"
},
"value" : {
"value" : "abc#test.com"
}
}
]
}
Issue: I want to cont the collection where telecom.system.value = email and countryCode doesn't exist in the email part object. here I am attaching a script but I need one line query
var count = 0,i;
db.getCollection('practitioner').find({"telecom.system.value":"email"}).forEach(function(practitioner){
//print("updating : " +practitioner._id.valueOf())
telecom = practitioner.telecom.valueOf()
for(i= 0;i<telecom.length;i++){
if(telecom[i].system.value === 'email' && telecom[i].countryCode){
count+=1;
}
}
});
print(" Total count of the practitioner with country code in email object: "+count)
Above mention, the script is working fine and the output is as I expected. but the script is not optimised and I want to write in a single line query. Thanks in advance.
You can try aggregation method aggregate(),
Approach 1:
$match condition for countryCode should exists and system.value should be email
$filter to iterate loop of telecom array and check both condition, this will return expected elements
$size to get total element from above filter result
$group by null and count total
var result = await db.getCollection('practitioner').aggregate([
{
$match: {
telecom: {
$elemMatch: {
countryCode: { $exists: true },
"system.value": "email"
}
}
}
},
{
$project: {
count: {
$size: {
$filter: {
input: "$telecom",
cond: {
$and: [
{ $ne: [{ $type: "$$this.countryCode" }, "missing"] },
{ $eq: ["$$this.system.value", "email"] }
]
}
}
}
}
}
},
{
$group: {
_id: null,
count: { $sum: "$count" }
}
}
]);
print("Total count of the practitioner with country code in email object: "+result[0].count);
Playground
Approach 2:
$match condition for countryCode should exists and system.value should be email
$unwind deconstruct telecom array
$match to filter document using above conditions
$count to get total elements count
var result = await db.getCollection('practitioner').aggregate([
{
$match: {
telecom: {
$elemMatch: {
countryCode: { $exists: true },
"system.value": "email"
}
}
}
},
{ $unwind: "$telecom" },
{
$match: {
"telecom.countryCode": { $exists: true },
"telecom.system.value": "email"
}
},
{ $count: "count" }
]);
print("Total count of the practitioner with country code in email object: "+result[0].count);
Playground
I have not tested the performance but you can check and use as per your requirement.

How to push object without filed name in mongodb with nodejs

This is my Schema design
mongoose.Schema({
traffic_countries:String,
company_details:String,
campaigns:[{
_campaignid : mongoose.Schema.Types.ObjectId,
}],
impressions:[{
country:String,
_campaignid :mongoose.Schema.Types.ObjectId
}],
payments:[],
budget:String })
What i want to do
I want to push the object below in the payments array the code that i am using for this purpose is given below the object
{
"id" : "PAY-52L98986JB4797714LN73QTY",
"intent" : "sale",
"state" : "created",
"payer" : {
"payment_method" : "paypal"
},
"transactions" : [
{
"amount" : {
"total" : "12.00",
"currency" : "USD"
},
"description" : "",
},
"related_resources" : [ ]
}
],
"create_time" : "2018-08-24T07:48:30Z",
"links" : [
{
"rel" : "execute",
"method" : "POST"
}
],
"httpStatusCode" : 201
}
The Code I am using for this purpose
The above object is stored in the transaction variable that i am getting from paypal api and pushing in the payments array
var newPayment = {
$push:{
payments:{
transaction
}
}
}
Advertiser.updateOne({_id:req.user.id},newPayment,function(err,rec){
if(err) throw err;
})
The objects that are stored in mongodb are like
payments:[
{ transaction:{object} },
{ transaction:{object} },
{ transaction:{object} }
]
But i want to store objects like
payments:[
{object},
{object},
{object}
]
After a lot of time waste i found out the answer to this problem by myself i only need to remove the extra brackets in the push query
Instead of this
var newPayment = {
$push:{
payments:{
transaction
}
}
}
I needed that
var newPayment = {
$push:{
payments:transaction
}
}

How can I insert a new entry inside Multilevel structure of mongoDB database?

I am working on an app that contain details of an academic year in Multilevel structure of mongoDB database.
For this I've created structure like this:
[
{
"_id" : ObjectId("5a1519a71fe8cc4df5888ff5"),
"academicyear" : "2016-2017",
"departments" : [
{
"deptname" : "computer",
"semesters" : [
{
"sem" : "2",
"classes" : [
{
"class" : "1",
"theory" : [
{
"subname" : "DS",
"faculty" : [
{
"facultyname" : "KSS",
"facultydept" : "COMP"
}
]
}
],
"practical" : [
{
"subname" : "DS",
"batch" : [
{
"bname" : "C",
"facultyname" : "KSS",
"facultydept" : "COMP"
}
]
}
]
}
]
}
]
}
]
}
]
Now for the same academic year and for the same department I would like to create a new semester node inside the array of semesters. For that I've tried syntax of $ and use of . operator but it did not worked well. How can I add a new semester node inside that semesters array? Also after creating a new semester node, how can I add a new class in an classes array?
it could be done by means of the positional operator $[],
db.collection.update(
{ "academicyear": "2016-2017", "departments.deptname": "computer"},
{ "$push":
{"departments.$[].semesters":
{
"sem" : "2",
"classes" : [ ...
}
}
}
)
It could also be done by indicating the position in the array if known. "departments.1.semesters", where 1 is the position of 'departments' in the array.
db.collection.update(
{ "academicyear": "2016-2017"},
{ "$push":
{"departments.1.semesters":
{
"sem" : "2",
"classes" : [ ...
}
}
}
)
One way of doing it would be:
var toInsert = {"sem" : "2", "classes" : [ ... }
db.collection.update(
{ "academicyear": "2016-2017", departments: { $elemMatch: { deptname: "computer" } } },
{ $addToSet: { 'departments.$.semesters': toInsert } }
)
The query part will find the document from the list array with id = 2. Then we use $ positional element to add a new element at that array index.
and the same would apply for classes.
db.collection.update(
{ "academicyear": "2016-2017"},
{ "$push":
{"departments.1.semesters.0.classes":
{
"class" : "1",
"theory" : [{ ...
}
}
}
)
Another alternative would be to extract the document, update it on the returned object and save it again.
db.collection.findOne({"academicyear": "2016-2017", "departments.deptname": "computer"}, {'departments.semesters': 1}, (err, doc) => {
if(err) return handleError(res)(err);
if(!doc) return res.send(404).end();
//I guess semester is 0, but you can find the one that you are going to update by some filter
doc.departments[0].semesters[0].classes.push({...});
doc.save( (err) => {
if(err) return handleError(res)(err)
return res.status(200).end();
});
});

MEAN-Stack MongoDB Sub Array Delete - Works in IDE, not in API

I have a [user] document stored that contains a nested sub-array [profiles],[favorites]. I am simply trying to delete($pull) a favorites from a given profile based on the favorites name.
{
"_id" : ObjectId("558d53eebdd9804820090fa1"),
"name" : "Frank",
"email" : "Frank#FrankTheTank.com",
"profiles" : [
{
"avatar" : "div-male",
"age" : "35",
"gender" : "Male",
"profilename" : "Oly Lifter",
"_id" : ObjectId("558d5404bdd9804820090fa2"),
"favorites" : [
{
"name" : "Power Clean"
},
{
"name" : "Hang Clean"
},
{
"name" : "Clean and Jerk"
}
],
"createdAt" : ISODate("2015-06-26T13:30:44.661Z")
}
],
"createdAt" : ISODate("2015-06-26T13:30:22.884Z"),
"role" : "user",
"__v" : 0
}
Using a MongoDB IDE robomongo, I'm able to successfully remove a favorite item from a known User and Profile ID using this
db.users.update($find: {
'profiles': {
'profiles._id': ObjectId("558d5404bdd9804820090fa2")
},
{
$pull: {
'profiles.$.favorites': {
'name': 'Hang Clean'
}
}
})
However, when I call from my server API using the following syntax, I receive an error, note req.body._id = "558d5404bdd9804820090fa2" and req.body.favorites.name = "Hang Clean"
User.findByIdAndUpdate(_user._id, {
'profiles._id': req.body._id
}, {
$pull: {
'profiles.$.favorites': {
'name': req.body.favorites.name
}
}
}, {
safe: true,
upsert: true
},
function(err, model) {
if (err) {
console.log(err);
return res.status(500).send('Error Deleting Profile');
}
return res.status(200).send('Profile Deleted!');
});
Try updating using the findOneAndUpdate() method since you are supplying the findByIdAndUpdate() method with the wrong parameters: the second argument { 'profiles._id': req.body._id } should be part of the first query object hence you need to use the findOneAndUpdate() method as follows, making sure you convert the string ids into ObjectId's:
var mongoose = require('mongoose');
var id = mongoose.Types.ObjectId(_user._id),
profileId = mongoose.Types.ObjectId(req.body._id),
query = {
"_id": id,
"profiles._id": profileId
},
update = {
"$pull": {
"profiles.$.favorites": { "name": req.body.favorites.name }
}
},
options = { "multi": true, "upsert": true };
User.findOneAndUpdate(query, update, options, function(err, model) {
if(err){
console.log(err);
return res.status(500).send('Error Deleting Profile');
}
return res.status(200).send('Profile Deleted!');
});

Resources