How to add parent name with their name using parent Id - node.js

I have following array which is saved in Database. i want to modify it to display like following which show their hierarchic with parent in localeName key.
var allLocales = [
{
id: 123,
localeName: 'Test',
parentId: null
},
{
id: 456,
localeName: 'Test 1',
parentId: 123
},
{
id: 789,
localeName: 'Test 2',
parentId: 456
}
]
I want to change above array to following array by changing their display name like this using their parents.:
allLocales = [
{
id: 123,
localeName: 'Test',
parentId: null
},
{
id: 456,
localeName: 'Test > Test 1',
parentId: 123
},
{
id: 789,
localeName: 'Test > Test 1 > Test 2',
parentId: 456
}
]

Try this aggregation if you are using mongo 3.4+
you can use $graphLookup for hierarchical queries $graphLookup
db.locales.aggregate(
[
{$graphLookup : {
from : "locales",
startWith : "$parentId",
connectFromField : "parentId",
connectToField : "id",
as : "parents"
}
},
{$addFields : {localeName : {$substr : [{$concat : [{$reduce : {input : "$parents", initialValue : "", in : {$concat : ["$$value", " > ", "$$this.localeName"]}}}, " > " ,"$localeName"] }, 3 , 1000]}}},
{$project : {parents : 0}}
]
).pretty()
collection
> db.locales.find()
{ "_id" : ObjectId("5a73dead0cfc59674782913a"), "id" : 123, "localeName" : "Test", "parentId" : null }
{ "_id" : ObjectId("5a73dead0cfc59674782913b"), "id" : 456, "localeName" : "Test 1", "parentId" : 123 }
{ "_id" : ObjectId("5a73dead0cfc59674782913c"), "id" : 789, "localeName" : "Test 2", "parentId" : 456 }
>
result
> db.locales.aggregate( [ {$graphLookup : { from : "locales", startWith : "$parentId", connectFromField : "parentId", connectToField : "id", as : "parents" } }, {$addFields : {localeName : {$substr : [{$concat : [{$reduce : {input : "$parents", initialValue : "", in : {$concat : ["$$value", " > ", "$$this.localeName"]}}}, " > " ,"$localeName"] }, 3 , 100]}}}, {$project : {parents : 0}} ] ).pretty()
{
"_id" : ObjectId("5a73dead0cfc59674782913a"),
"id" : 123,
"localeName" : "Test",
"parentId" : null
}
{
"_id" : ObjectId("5a73dead0cfc59674782913b"),
"id" : 456,
"localeName" : "Test > Test 1",
"parentId" : 123
}
{
"_id" : ObjectId("5a73dead0cfc59674782913c"),
"id" : 789,
"localeName" : "Test > Test 1 > Test 2",
"parentId" : 456
}

You need to make recursive function to solve this problem.
I made like below and tested.
Please see the function.
var allLocales = [
{ id: 123, localeName: 'Test', parentId: null },
{ id: 456, localeName: 'Test 1', parentId: 123 },
{ id: 789, localeName: 'Test 2', parentId: 456 }
];
function nameRecursion(element) {
if(element.parentId == null) {
return element.localeName
}else {
var parent = allLocales.find(item => item.id === element.parentId);
return nameRecursion(parent) + " -> " + element.localeName;
}
}
var newArray = allLocales.map(a => Object.assign({}, a));
for(var i=0; i<allLocales.length; i++){
newArray[i].localeName = nameRecursion(allLocales[i]);
}
console.log(allLocales);
console.log(newArray);

Related

Mongoose - push if array else update

My schema looks like this:
{
_id: mongoose.Schema.Types.ObjectId,
timestamp: { type: Date, default: Date.now },
uid: { type: String, required: false, immutable: true },
gid: [
{
type: String
}
],
status: { type: String, required: false }
}
I want to be able to save a new object everytime uid is new and update it when it already exists. So basically saving this object:
{
uid: "xyz",
gid: "123"
}
should produce
{
_id: ObjectId("123"),
uid: "xyz",
gid: ["123"]
// note that if status is not provided it shouldn't appear here as `null`
}
Then again if this object is saved:
{
uid: "xyz",
gid: "345",
status: "new"
}
it should produce:
{
_id: ObjectId("123"),
uid: "xyz",
gid: ["123","345"]
status: "new"
}
and lastly for this object:
{
uid: "xyz",
gid: "345",
status: "old"
}
the expected result would be
{
_id: ObjectId("123"),
uid: "xyz",
gid: ["123","345"]
status: "old"
}
Is this possible to achieve in a single query? Sorry if my explenation is complicated, I don't know how else to put it.
Case1: only status changes and array exists, the below code, won't add a new element
> //actual code output from Mongo shell 4.2 on windows10 OS
> //use add to set and set update to acheive your update goals(array vs addition
> //of fields in two updates
> //add to set checks for an array element is present in array, if it is present it does nothing
> //else it will add the element in the array
> //pre-update document check
> db.ugs.find().pretty();
{
"_id" : ObjectId("5f625d864651219e25c775c1"),
"uid" : "xyz",
"gid" : [
"345",
"123",
"100",
"200"
],
"status" : "old"
}
> var v_status = "new";
> db.ugs.aggregate([
... {$match:{uid: "xyz"}}
... ]).forEach(function(doc){
... print("uid: ", doc.uid);
... print("gid: ",doc.gid);
... print("pre-status: ", doc.status);
... db.ugs.update(
... {"_id":doc._id},
... {$addToSet:{gid:"200"}
... });
... db.ugs.update(
... {"_id":doc._id},
... {$set:{status:v_status}}
... );
... print("post-status: ", v_status);
... });
uid: xyz
gid: 345,123,100,200
pre-status: old
post-status: new
> //post update document check
> db.ugs.find().pretty();
{
"_id" : ObjectId("5f625d864651219e25c775c1"),
"uid" : "xyz",
"gid" : [
"345",
"123",
"100",
"200"
],
"status" : "new"
}
>
Case2: change status as well add element if it does not exists in array
> //pre-update document check
> db.ugs.find().pretty();
{
"_id" : ObjectId("5f625d864651219e25c775c1"),
"uid" : "xyz",
"gid" : [
"345",
"123",
"100",
"200"
],
"status" : "new"
}
> var v_status = "old";
> db.ugs.aggregate([
... {$match:{uid: "xyz"}}
... ]).forEach(function(doc){
... print("uid: ", doc.uid);
... print("gid: ",doc.gid);
... print("pre-status: ", doc.status);
... db.ugs.update(
... {"_id":doc._id},
... {$addToSet:{gid:"444"}
... });
... db.ugs.update(
... {"_id":doc._id},
... {$set:{status:v_status}}
... );
... print("post-status: ", v_status);
... });
uid: xyz
gid: 345,123,100,200
pre-status: new
post-status: old
> //post update document check
> db.ugs.find().pretty();
{
"_id" : ObjectId("5f625d864651219e25c775c1"),
"uid" : "xyz",
"gid" : [
"345",
"123",
"100",
"200",
"444"
],
"status" : "old"
}
>
Case3: set and unset the value of the status field as needed.
> db.ugs.find().pretty();
{
"_id" : ObjectId("5f625d864651219e25c775c1"),
"uid" : "xyz",
"gid" : [
"345",
"123",
"100",
"200",
"444"
],
"status" : ""
}
> var v_status = "";
> db.ugs.aggregate([
... {$match:{uid: "xyz"}}
... ]).forEach(function(doc){
... print("uid: ", doc.uid);
... print("gid: ",doc.gid);
... print("pre-status: ", doc.status);
... db.ugs.update(
... {"_id":doc._id},
... {$addToSet:{gid:"444"}
... });
... db.ugs.update(
... {"_id":doc._id,status:{$exists:true}},
... {$set:{status:v_status}}
... );
... print("post-status: ", v_status);
... });
uid: xyz
gid: 345,123,100,200,444
pre-status:
post-status:
> //post update document check
> db.ugs.find().pretty();
{
"_id" : ObjectId("5f625d864651219e25c775c1"),
"uid" : "xyz",
"gid" : [
"345",
"123",
"100",
"200",
"444"
],
"status" : ""
}
//unset the field if not required, it will not show in the document
> var v_status = "";
> db.ugs.aggregate([
... {$match:{uid: "xyz"}}
... ]).forEach(function(doc){
... print("uid: ", doc.uid);
... print("gid: ",doc.gid);
... print("pre-status: ", doc.status);
... db.ugs.update(
... {"_id":doc._id},
... {$addToSet:{gid:"444"}
... });
... db.ugs.update(
... {"_id":doc._id,status:{$exists:true}},
... {$unset:{status:v_status}}
... );
... print("post-status: ", v_status);
... });
uid: xyz
gid: 345,123,100,200,444
pre-status: [unknown type]
post-status:
> //post update document check
> db.ugs.find().pretty();
{
"_id" : ObjectId("5f625d864651219e25c775c1"),
"uid" : "xyz",
"gid" : [
"345",
"123",
"100",
"200",
"444"
]
}
>
You're looking the upsert option to add a new doc if it doesn't exist already. Combined with the addToSet atomic opperator to create the array of gids.
You'll have to break the object into $set and $addToSet updaters with rest.
let saveObj = {
uid: "xyz",
gid: "123"
};
let {gid, ...setFields} = saveObj;
YourModel.updateOne(
{uid: setFields.uid},
{$set: setFields, $addToSet: {gid}},
{upsert: true}
).then(...);

How to update multiple mongodb documents with different values based on a key?

I am trying to figure out a way to update multiple documents in a collection with different values based on a key.
persons collection:
{
_id: 1,
name: "Jackie Chan",
Country: Australia
},
{
_id: 2,
name: "Brad Pitt",
Country: Russia
},
{
_id: 3,
name: "Al Pacino",
Country: USA
}
Payload:
{
_id: 1,
name:"Jackie Chan",
Country:"China"
}
,{
_id: 2,
name:"Brad Pitt",
Country:"USA"
}
persons collection after update:
{
_id: 1,
name: "Jackie Chan",
Country: "China"
},
{
_id: 2,
name: "Brad Pitt",
Country: "USA"
},
{
_id: 3,
name: "Al Pacino",
Country: "USA"
}
SQL equivalent would be :
update t1.country = t2.country from persons t1 inner join #temptable t2 on t1._id=t2._id
None of the examples mentioned here explain how to do it. Unless I am missing something?
It seems like bulk write is exactly the right tool. Simply map the payload array so to make it an array of updates, such as:
db.persons.bulkWrite(payload.map( function(p) {
return { updateOne:{
filter: {_id: p._id},
update: {$set: {Country: p.Country}}
}}
}))
//code when run from mongodb client
> db.persons.find();
{ "_id" : 1, "name" : "Jackie Chan", "Country" : "China1" }
{ "_id" : 2, "name" : "Brad Pitt", "Country" : "Russia1" }
{ "_id" : 3, "name" : "Al Pacino", "Country" : "USA1" }
> var payload=[{_id: 1,name:"Jackie Chan",Country:"China2"},
... {_id: 2,name: "Brad Pitt",Country: "Russia2"},
... {_id: 3, name: "Al Pacino",Country: "USA2"}];
> print("payload: ",payload);
payload: [object Object],[object Object],[object Object]
>
> db.persons.bulkWrite(payload.map( function(p) {
... return { updateOne:{
... filter: {_id: p._id},
... update: {$set: {Country: p.Country}}
... }}
... }));
{
"acknowledged" : true,
"deletedCount" : 0,
"insertedCount" : 0,
"matchedCount" : 3,
"upsertedCount" : 0,
"insertedIds" : {
},
"upsertedIds" : {
}
}
> db.persons.find();
{ "_id" : 1, "name" : "Jackie Chan", "Country" : "China2" }
{ "_id" : 2, "name" : "Brad Pitt", "Country" : "Russia2" }
{ "_id" : 3, "name" : "Al Pacino", "Country" : "USA2" }
>

Display Nested Mongoose data on EJS template?

I am trying to display order data from a nested mongoose object and I can't seem to get the data to display correctly. Appreciate all help in advance.
Here is the order model:
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var schema = new Schema({
//todo: check the user part in this model
user: {type: Schema.Types.ObjectId, ref: 'User'},
cart: {type: Object, required: true},
address: {type: String, required: true},
name: {type: String, required: true},
paymentId: {type: String, required: true},
email:{type: String, required: true},
time : { type : Date, default: Date.now }
});
module.exports = mongoose.model('Order', schema);
Here is how the data is saved to the database:
{ "_id" : ObjectId("5975214fc8db300731d0e8b8"), "user" : ObjectId("5951d4d1f57b440556898e1a"), "cart" : { "items" : { "596068
4581c02d077c914283" : { "item" : { "_id" : "5960684581c02d077c914283", "imagePath" : "https://www.swarovski.com/is-bin/inters
hop.static/WFS/SCO-Media-Site/-/-/publicimages//CG/B2C/PROD/180/Swarovski-Cosmic-Bracelet-5226308-W180.jpg", "title" : "Brace
let 1", "description" : "This is the first Bracelet in the collection.", "price" : 10, "__v" : 0 }, "qty" : 1, "price" : 10 }
, "59742cad8f1bf6071b5419cb" : { "item" : { "_id" : "59742cad8f1bf6071b5419cb", "title" : "Bracelet 2 ", "imagePath" : "https
://img0.etsystatic.com/160/0/12655872/il_340x270.1187191078_i2ha.jpg", "description" : "This is the second Bracelet in the co
llection.", "price" : 5, "__v" : 0 }, "qty" : 1, "price" : 5 }, "59742ebdd1242f07530d2b30" : { "item" : { "_id" : "59742ebdd1
242f07530d2b30", "title" : "Bracelet 3", "imagePath" : "https://www.costco.com/wcsstore/CostcoUSBCCatalogAssetStore/category-
tiles/pearl-bracelets.jpg", "description" : "This is the third Bracelet in the collection.", "price" : 12, "__v" : 0 }, "qty"
: 1, "price" : 12 } }, "totalQty" : 3, "totalPrice" : 27 }, "address" : "6210 place", "name" : "pauls", "paymentId" : "ch_1A
isRfDfJryYeuMpbJGWY2NV", "email" : "pauls#a.com", "time" : ISODate("2017-07-23T22:21:03.819Z"), "__v" : 0 }
I would like to display the data in a table using ejs:
<table border="1">
<tr>
<th>Item</th>
<th>Description</th>
<th>Price</th>
<th>Quantity</th>
</tr>
<% orders.forEach(function(order){ %>
<tr>
<td><%=...item%> </td>
<td><%=...description%> </td>
<td><%=...price%> </td>
<td><%=...quantity%> </td>
</tr>
<% }); %>
</table>
Here is the route:
router.get('/order', function(req, res){
Order.find({}, function(err, allOrders){
if(err){
console.log(err);
} else {
res.render("admin/order", {orders: allOrders});
}
});
});
Here is the cart:
module.exports = function Cart(oldCart) {
this.items = oldCart.items || {};
this.totalQty = oldCart.totalQty || 0;
this.totalPrice = oldCart.totalPrice || 0;
this.add = function (item, id) {
var storedItem = this.items[id];
if (!storedItem) {
storedItem = this.items[id] = {item: item, qty: 0, price: 0};
}
storedItem.qty++;
storedItem.price = storedItem.item.price * storedItem.qty;
this.totalQty++;
this.totalPrice += storedItem.item.price;
};
this.reduceByOne = function (id) {
this.items[id].qty--;
this.items[id].price -= this.items[id].item.price;
this.totalQty--;
this.totalPrice -= this.items[id].item.price;
if (this.items[id].qty <=0) {
delete this.items[id];
}
};
this.removeItem = function (id) {
this.totalQty -= this.items[id].qty;
this.totalPrice -= this.items[id].price;
delete this.items[id];
};
this.generateArray = function () {
var arr = [];
for (var id in this.items) {
arr.push(this.items[id]);
}
return arr;
};
};
Edit: Now data is saved like below:
{ "_id" : ObjectId("5976b6b11306910658b1ff57"), "address" : "6210 place", "name" : "frank", "paymentId" : "ch_1AjJRVDfJ
ryYeuMpJC80cp5k", "email" : "g#mail.com", "time" : ISODate("2017-07-25T03:10:41.522Z"), "cart" : [ { "items" : { "59752
28a215c0f074b64f58e" : { "item" : { "_id" : "5975228a215c0f074b64f58e", "title" : "Bracelet 3", "imagePath" : "https://
www.costco.com/wcsstore/CostcoUSBCCatalogAssetStore/category-tiles/pearl-bracelets.jpg", "description" : "This is brace
let 3", "price" : 12, "__v" : 0 }, "qty" : 1, "price" : 12 }, "59752242215c0f074b64f58c" : { "item" : { "_id" : "597522
42215c0f074b64f58c", "title" : "Bracelet 1", "imagePath" : "https://img0.etsystatic.com/160/0/12655872/il_340x270.11871
91078_i2ha.jpg", "description" : "This is bracelet 1", "price" : 10, "__v" : 0 }, "qty" : 2, "price" : 20 }, "5975226a2
15c0f074b64f58d" : { "item" : { "_id" : "5975226a215c0f074b64f58d", "title" : "Bracelet 2", "imagePath" : "http://media
.tiffany.com/is/image/Tiffany/EcomBrowseM/paloma-picasso-knot-bead-bracelet-34946183_963148_ED.jpg?op_usm=1.00,1.00,6.0
0&defaultImage=NoImageAvailable&&", "description" : "This is bracelet 2", "price" : 5, "__v" : 0 }, "qty" : 1, "price"
: 5 } }, "totalQty" : 4, "totalPrice" : 37 } ], "__v" : 0 }

Can't create correct query to reach mongodb document

I need to translate a mongo shell command to the correct mongoose update in my express route.
The :id in the url is the ObjectId # in my document. The req.body will have an object with the key/values for title, season_number, episode_number, and watched. I thought I'd just replace this part of the mongo shell query
{ 'season_number': 1, 'episode_number': { $gt: 4, $lt: 6 },
with
{
'season_number': req.body.season_number,
'episode_number': {
$gt: req.body.episode_number - 1,
$lt: req.body.episode_number + 1
}
}
in the query but that did not find the document.
Route
router.put('/api/shows/:id/episodes/add', function(req, res){
var query = {
/*
I've tried many things but my query never returns the document
to update so I am pretty sure the problem is here
*/
}
var setObject = {
$push:{
'episodes':req.body
}
}
TV.update(query, setObject, function(err, results){
if(err){console.log(err)}
else{res.json(results)};
})
})
Mongo Shell Document
{
"_id" : ObjectId("581972b7b04acfc99b4dae0f"),
"title" : "Designated Survivor",
"poster" : "https://images-na.ssl-images-amazon.com/images/M/MV5BMTY5NzYzODU4N15BMl5BanBnXkFtZTgwNzA1MjUwMDI#._V1_.jpg",
"rated" : "TV-14",
"program_time" : 60,
"network" : "ABC",
"airs_on" : [
"Wednesday"
],
"streams_on" : [
"123Movies",
"Hulu Plus"
],
"genre" : [
"Drama"
],
"episodes" : [
{
"season_number" : 1,
"episode_number" : 1,
"title" : "Pilot",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 2,
"title" : "The First Day",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 3,
"title" : "The Confession",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 4,
"title" : "The Enemy",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 5,
"title" : "The Mission",
"watched" : true
},
{
"title" : "The Interrogation",
"season_number" : 1,
"episode_number" : 6,
"watched" : false
}
],
"test" : "gt four less than 6"
}
Mongo Shell Command that added the 6th Episode
db.tvShows.findOneAndUpdate(
{
$and: [
{ '_id': ObjectId('581972b7b04acfc99b4dae0f') },
{ 'episodes': {
$elemMatch: {
'season_number': 1,
'episode_number': { $gt: 4, $lt: 6 }
}
} }
]
},
{
$push: {
'episodes': {
'title': 'The Interrogation',
'season_number': 1,
'episode_number': 6,
watched: false
}
}
}
)

mongodb aggregation get the total number of matched document

I have a following sample docs saved in mongogb, like:
{
name: 'andy',
age: 19,
description: 'aaa aaa aaa'
}
{
name: 'andy',
age: 17,
description: 'bbb bbb bbb'
}
{
name: 'leo',
age: 10,
description: 'aaa aaa aaa'
}
{
name: 'andy',
age: 17,
description: 'ccc ccc ccc'
}
what the pipeline should look like to get the total number of name in each of matched sets? so I can use this sum number for next pipe. the pipeline I currently have is this:
var pip = [
{
$match: { name: 'andy' }
}
]
and I want to get this result like
{
name: 'andy',
age: 19,
description: 'aaa aaa aaa',
total_andy: 3
}
{
name: 'andy',
age: 17,
description: 'bbb bbb bbb',
total_andy: 3
}
{
name: 'andy',
age: 17,
description: 'ccc ccc ccc',
total_andy: 3
}
I am not exactly clear as to what you want. And i don't have enough reputation to ask for that in a comment. So let me have a shot at answering. If the answer isn't what you want, clarify the question further and we'll get to it...
var term1group = {$group :
{'_id' : '$name'},
'total_names' : {$sum : 1},
'ageAndDescription' : {$addToSet : {'$age', '$description'}}
}
var term2unwind = {$unwind : '$ageAndDescription'}
var term3project = {$project : {
_id : 0,
'name' : '_id',
'age' : '$ageAndDescription.age',
'description' : '$ageAndDescription.description',
'total_name' : 1
}
db.collection.aggregate(term1group, term2unwind, term3project);
Haven't tested but i am hopeful this will work.
You just need to use a $group and $sum to do a simple count. The output won't match exactly, but you could reformat it with NodeJS easily.
You apparently want to group on the three fields shown (name, age, and description). To do that, just add the fields and a field reference (using $):
{ $match: { name: 'andy' } },
{ $group: {
_id: { name: "$name", age: "$age", description: "$description"},
count: { $sum: 1}
}
}
To add the count of each group, include a $sum of 1 (for each document that matches the group).
Your output will look something like:
{ "_id" : { "name" : "andy", "age" : 17, "description" : "ccc ccc ccc" }, "count" : 1 }
{ "_id" : { "name" : "andy", "age" : 17, "description" : "bbb bbb bbb" }, "count" : 1 }
{ "_id" : { "name" : "andy", "age" : 19, "description" : "aaa aaa aaa" }, "count" : 3 }
If you used a projection with $project, you could also format the output to more closely match your original request:
{ $match: {name: 'andy' }},
{ $group: { _id: { name: "$name", age: "$age", description: "$description"} ,
count: {$sum: 1}}
},
{ $project : { name: "$_id.name", _id: 0, age: "$_id.age",
description: "$_id.description", total_andy: "$count"
}
}
Results:
{ "name" : "andy", "age" : 17, "description" : "ccc ccc ccc", "total_andy" : 1 }
{ "name" : "andy", "age" : 17, "description" : "bbb bbb bbb", "total_andy" : 1 }
{ "name" : "andy", "age" : 19, "description" : "aaa aaa aaa", "total_andy" : 3 }

Resources