For example, if a purchase order has line items like this:
and user from the client side send us line items to be updated in an array called lineItemsToUpdate and has a format like this:
[
{ unitCost: 342,
totalQuantity: 13,
acceptedQuantity: 6,
rejectedQuantity: 18,
title: 'Unbranded Concrete Pizza',
description: 'Soft',
variant: 5f2d5eb0195026e6dd549ef0 },
{ unitCost: 189,
totalQuantity: 95,
acceptedQuantity: 49,
rejectedQuantity: 16,
title: 'Handcrafted Rubber Cheese',
description: 'Assurance',
variant: 5f2d5eaf195026e6dd549b7d },
{ unitCost: 267,
totalQuantity: 18,
acceptedQuantity: 93,
rejectedQuantity: 11,
title: 'Incredible Soft Car',
description: 'solution-oriented',
variant: 5f2d5eb0195026e6dd549d3d },
]
here in the lineItemsToUpdate array, it's possible that the value of each element has been changed by the client and I want to update all the matching element by variant field in my sub-document if I describe it in the format of a user story:
update all lineItems "fields" according to lineItemsToUpdate array where the lineItemsToUpdate.[elem].variant === linitem.[elem].variant using $set, if possible $each and arrayFilters operators
I read some example through the mongodb documentation like this one:
db.students2.update(
{ },
{ $set: { "grades.$[elem].mean" : 100 } },
{
multi: true,
arrayFilters: [ { "elem.grade": { $gte: 85 } } ]
}
)
but the problem in here { $set: { "grades.$[elem].mean" : 100 } } is that the 100 value is constant and I want this part dynamic as I described in above.
Following is JS function you have to take help of.
function(c1,arr2){
var arr1 = c1.next().lineItems;
for(i=0;i<arr1.length;i++){
for(j=0;j<arr2.length;j++){
if(arr1[i].variant===arr2[j].variant){
db.purchaseOrder.update(
{ },
{ $set: { "lineItems.$[elem].unitCost" : arr2[j].unitCost,"lineItems.$[elem].totalQuantity" : arr2[j].totalQuantity } .....},
{ multi: true,
arrayFilters: [ { "elem.variant": { $eq: arr2[j].variant } } ]
}
)
}
}
}
}
Explanation
We have to pass 2 parameters: i) Mongo cursor and ii) An array.
Cursor is the collection to be updated. For your case, it is
Purchase Order. Array is lineItemsToUpdate array here.
Next we take 2 for loops to match variant field between cursor and
array.
If there is a match, we update the Purchase Order using update
command and $set operator.
Execution
Go to your Mongo Shell. Ensure the current database has Purchase
Order collection.
Define modifyItems function as below.
var modifyItems=function(c1,arr2){
var arr1 = c1.next().lineItems;
for(i=0;i<arr1.length;i++){
for(j=0;j<arr2.length;j++){
if(arr1[i].variant===arr2[j].variant){
db.purchaseOrder.update(
{ },
{ $set: { "lineItems.$[elem].unitCost" : arr2[j].unitCost,"lineItems.$[elem].totalQuantity" : arr2[j].totalQuantity }... },
{ multi: true,
arrayFilters: [ { "elem.variant": { $eq: arr2[j].variant } } ]
}
)
}
}
}
}
Define a variable arr which contains lineItemsToUpdate.
var arr=[
{ unitCost: 342,
totalQuantity: 13,
acceptedQuantity: 6,
rejectedQuantity: 18,
title: 'Unbranded Concrete Pizza',
description: 'Soft',
variant: 5f2d5eb0195026e6dd549ef0 },
{ unitCost: 189,
totalQuantity: 95,
acceptedQuantity: 49,
rejectedQuantity: 16,
title: 'Handcrafted Rubber Cheese',
description: 'Assurance',
variant: 5f2d5eaf195026e6dd549b7d },
{ unitCost: 267,
totalQuantity: 18,
acceptedQuantity: 93,
rejectedQuantity: 11,
title: 'Incredible Soft Car',
description: 'solution-oriented',
variant: 5f2d5eb0195026e6dd549d3d }
]
Now execute the JS function.
modifyItems(db.purchaseOrder.find({},{"lineItems":1,"_id":0}),arr);
If there is no error, your Purchase Order line items will be now updated with the array values in one single shot.
I have four collections person , other_details, occupation_details, bank_details as stated below
person =>[
{
id : 1,
name : 'john',
other_details : 1023
},
{
id : 2,
name : 'mark',
other_details : 99
}
]
other_details => [
{
id: 1023,
married: false,
occupation_details: 144,
bank_details : 10
},
{
id: 99,
married: true,
occupation_details: 45,
bank_details : 11
}
]
occupation_details => [
{
id: 144,
comp_name : 'oscorp inc.'
},
{
id: 45,
comp_name : 'tesla inc.'
}
]
bank_details => [
{
id: 10,
bank : 'Bank of canada'
},
{
id: 11,
bank : 'Peoples bank of canada'
}
]
I am using mongoose library with nodejs
// id = 1
person.findById(id).populate({
path: 'other_details',
populate: [
{
path: 'occupation_details'
},
{
path: 'bank_details'
}
]
})
So the result for the above query comes like below
=>
{
id : 1,
name : 'john',
other_details : {
id: 1023,
married: false,
occupation_details: {
id: 144,
comp_name : 'oscorp inc.'
},
bank_details : {
id: 10,
bank : 'Bank of canada'
}
}
}
But for some reasons I want the result like below
{
id : 1,
name : 'john',
other_details : {
id: 1023,
married: false,
occupation_details: {
id: 144,
comp_name : 'oscorp inc.'
},
bank_details : 10,
custom_bank_details : {
id: 10,
bank : 'Bank of canada'
}
}
}
The change I need is the following, The bank_details object should exist with id and the populated bank_details data from other collection should come in other object name as custom_bank_details
bank_details : 10,
custom_bank_details : {
id: 10,
bank : 'Bank of canada'
}
Update:
You could use virtual to populate bank details into a new field. Something like
OtherDetailsSchema.virtual('custom_bank_details', {
ref: 'BankDetails',
localField: 'bank_details',
foreignField: '_id',
justOne: true
});
Person.findById(id).populate({
path: 'other_details',
populate: [
{path: 'occupation_details'},
{path: 'custom_bank_details'}
]})
Original
I'm not a mongoose user so I'm not sure if it is possible to populate to new field name. If feasible you could quite easily achieve using aggregate with lookup.
Something like
person.aggregate([
{"$lookup":{
"from":"other_details",
"let":{"other_details":"$other_details"},
"pipeline":[
{"$match":{"$expr":{"$eq":["$id","$$other_details"]}}},
{"$lookup":{
"from":"occupation_details",
"localField":"occupation_details",
"foreignField":"id",
"as":"occupation_details"
}},
{"$unwind":"$occupation_details"},
{"$lookup":{
"from":"bank_details",
"localField":"bank_details",
"foreignField":"id",
"as":"custom_bank_details"
}},
{"$unwind":"$custom_bank_details"},
],
"as":"other_details"
}},
{"$unwind":"$other_details"}
])
Working example https://mongoplayground.net/p/5Kee0tBQmTd
I have a Deployd API which exposes a json structure like this:
[
{id: "1"
username: "john",
password: " ..... ",
email: "example#gmail.com",
coins: 60,
badges: [ ],
courses:[
{ id: "123456",
title: "Animals",
grades_per_module: [ [30], [28, 26], [25, 24]]
.....
},
{ id: "112233",
title: "Food",
grades_per_module: [ [20, 25, 27], [22]]
.....
}
]
},
{id: "2"
username: "mark",
password: " ..... ",
email: "ex#gmail.com",
coins: 40,
badges: [ ],
courses:[
{ id: "123456",
title: "Animals",
grades_per_module: [ [27], [21, 30], [30, 30]]
.....
}
]
}
]
Then I need to remove the intere course with id="112233" of the user "john" using an angular code.
So I use this code, but it doesn't work:
this.http.put('http://localhost:2403/users/1',
{ "courses": { $pull: { "id": 112233 } }
}).subscribe( ..... )
Deployd API returns me a positive message, but the course is not really removed from the user object.
Can anyone help me?
I am at a loss on why Google column chart is repeating the x-axis label.
Please find the CodePen URL: https://codepen.io/anon/pen/MPOJQG?editors=0010
You may notice that I have tried both the approaches:
arrayToDataTable (line #4 in code pen)
conventional datatable structure (line #5 in code pen)
Following is the code from CodePen link:
//console.log("Loading current Google charts");
google.charts.load("current");
google.charts.setOnLoadCallback(function() {
//let dataTable = new google.visualization.arrayToDataTable(GetJSONArray()); //This also has the same issue
let dataTable = new google.visualization.DataTable(GetJSONData());
RenderChart(dataTable, "chart");
});
function RenderChart(dataTable, elementId) {
try {
const dateFormat = "MMM dd";
//debugger;
let numberOfRows = dataTable.getNumberOfRows();
let options = {
tooltip: { isHtml: true /*, trigger: 'selection'*/ },
height: 240,
legend: { position: "bottom" },
colors: ["#4CAF50"],
chartArea: { left: 80, top: 20, width: "90%" },
//isStacked: 'true',
hAxis: {
format: dateFormat
//gridlines: { count: numberOfRows }
},
vAxis: {
//format: '%',
title: "Percentage",
viewWindow: {
max: 100,
min: 0
}
}
};
if (numberOfRows === 1) {
//If there is only one date then Google chart messes up the chart, in that case it is must to set viewWindow
let hAxis = {
hAxis: {
viewWindow: {
min: dataTable.getValue(0, 0),
max: dataTable.getValue(numberOfRows - 1, 0)
}
}
};
options = $.extend(true, options, hAxis);
}
let wrapper = new google.visualization.ChartWrapper({
chartType: "ColumnChart",
dataTable: dataTable,
options: options,
containerId: elementId
});
wrapper.draw();
} catch (e) {
console.log(e.toString());
}
}
function GetJSONArray(){
let data = [
['Date', 'Pass', { role: 'annotation' } , {'type': 'string', 'role': 'tooltip', 'p': {'html': true}} ],
[new Date(2018, 9, 6),96, "48 (96.00%)", "<div>2018-10-06 (Sat)</div><div> - Pass: 48 (96.00%)</div><div> - Fail: 2 (4.00%)</div>"],
[new Date(2018, 9, 8),96.55172413793103448275862069,"168 (96.55%)","<div>2018-10-08 (Mon)</div><div> - Pass: 168 (96.55%)</div><div> - Fail: 6 (3.45%)</div>"],
[new Date(2018, 9, 9),95.82409460458240946045824095,"2,593 (95.82%)","<div>2018-10-09 (Tue)</div><div> - Pass: 2,593 (95.82%)</div><div> - Fail: 113 (4.18%)</div>"],
[new Date(2018, 9, 10),96.81303116147308781869688385,"2,734 (96.81%)","<div>2018-10-10 (Wed)</div><div> - Pass: 2,734 (96.81%)</div><div> - Fail: 90 (3.19%)</div>"],
[new Date(2018, 9, 11),96.80555555555555555555555556,"2,788 (96.81%)","<div>2018-10-11 (Thu)</div><div> - Pass: 2,788 (96.81%)</div><div> - Fail: 92 (3.19%)</div>"],
[new Date(2018, 9, 12),96.863295880149812734082397,"2,069 (96.86%)","<div>2018-10-12 (Fri)</div><div> - Pass: 2,069 (96.86%)</div><div> - Fail: 67 (3.14%)</div>"]
]
return data;
}
function GetJSONData() {
return {
cols: [
{ type: "date", id: "Date", label: "Date" },
{ type: "number", id: "Pass", label: "Pass %" },
{
type: "string",
id: "Annotation",
label: "Annotation",
p: { role: "annotation" }
},
{
type: "string",
id: "ToolTip",
label: "ToolTip",
p: { html: "true", role: "tooltip" }
}
],
rows: [
{
c: [
{ v: "Date(2018, 9, 6)" },
{ v: 96 },
{ v: "48 (96.00%)" },
{
v:
"<div>2018-10-06 (Sat)</div><div> - Pass: 48 (96.00%)</div><div> - Fail: 2 (4.00%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 8)" },
{ v: 96.55172413793103448275862069 },
{ v: "168 (96.55%)" },
{
v:
"<div>2018-10-08 (Mon)</div><div> - Pass: 168 (96.55%)</div><div> - Fail: 6 (3.45%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 9)" },
{ v: 95.82409460458240946045824095 },
{ v: "2,593 (95.82%)" },
{
v:
"<div>2018-10-09 (Tue)</div><div> - Pass: 2,593 (95.82%)</div><div> - Fail: 113 (4.18%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 10)" },
{ v: 96.81303116147308781869688385 },
{ v: "2,734 (96.81%)" },
{
v:
"<div>2018-10-10 (Wed)</div><div> - Pass: 2,734 (96.81%)</div><div> - Fail: 90 (3.19%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 11)" },
{ v: 96.80555555555555555555555556 },
{ v: "2,788 (96.81%)" },
{
v:
"<div>2018-10-11 (Thu)</div><div> - Pass: 2,788 (96.81%)</div><div> - Fail: 92 (3.19%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 12)" },
{ v: 96.863295880149812734082397 },
{ v: "2,069 (96.86%)" },
{
v:
"<div>2018-10-12 (Fri)</div><div> - Pass: 2,069 (96.86%)</div><div> - Fail: 67 (3.14%)</div>"
}
]
}
]
};
}
I have also referred following URLs:
Duplicate label on x-axis, stacking bar chart (google charts)
since you're using datetime for x-axis,
the chart doesn't know it should only show one label for each day.
instead, it adds dates to fill the range of the x-axis.
and since the format does not include time,
labels are repeated.
to correct, use option hAxis.ticks to provide your own labels.
to build dynamically, use data table method --> getColumnRange
this will return the min and max dates in the table.
then build an array of dates for each day.
let dateRange = dataTable.getColumnRange(0);
for (var i = dateRange.min.getTime(); i <= dateRange.max.getTime(); i = i + oneDay) {
hAxisTicks.push(new Date(i));
}
see following working snippet...
google.charts.load("current");
google.charts.setOnLoadCallback(function() {
//let dataTable = new google.visualization.arrayToDataTable(GetJSONArray()); //This also has the same issue
let dataTable = new google.visualization.DataTable(GetJSONData());
RenderChart(dataTable, "chart");
});
function RenderChart(dataTable, elementId) {
try {
const dateFormat = "MMM dd";
const oneDay = (1000 * 60 * 60 * 24);
//debugger;
let hAxisTicks = [];
let dateRange = dataTable.getColumnRange(0);
for (var i = dateRange.min.getTime(); i <= dateRange.max.getTime(); i = i + oneDay) {
hAxisTicks.push(new Date(i));
}
let numberOfRows = dataTable.getNumberOfRows();
let options = {
tooltip: { isHtml: true /*, trigger: 'selection'*/ },
height: 240,
legend: { position: "bottom" },
colors: ["#4CAF50"],
chartArea: { left: 80, top: 20, width: "90%" },
//isStacked: 'true',
hAxis: {
format: dateFormat,
ticks: hAxisTicks
//gridlines: { count: numberOfRows }
},
vAxis: {
//format: '%',
title: "Percentage",
viewWindow: {
max: 100,
min: 0
}
}
};
if (numberOfRows === 1) {
//If there is only one date then Google chart messes up the chart, in that case it is must to set viewWindow
let hAxis = {
hAxis: {
viewWindow: {
min: dataTable.getValue(0, 0),
max: dataTable.getValue(numberOfRows - 1, 0)
}
}
};
options = $.extend(true, options, hAxis);
}
let wrapper = new google.visualization.ChartWrapper({
chartType: "ColumnChart",
dataTable: dataTable,
options: options,
containerId: elementId
});
wrapper.draw();
} catch (e) {
console.log(e.toString());
}
}
function GetJSONArray(){
let data = [
['Date', 'Pass', { role: 'annotation' } , {'type': 'string', 'role': 'tooltip', 'p': {'html': true}} ],
[new Date(2018, 9, 6),96, "48 (96.00%)", "<div>2018-10-06 (Sat)</div><div> - Pass: 48 (96.00%)</div><div> - Fail: 2 (4.00%)</div>"],
[new Date(2018, 9, 8),96.55172413793103448275862069,"168 (96.55%)","<div>2018-10-08 (Mon)</div><div> - Pass: 168 (96.55%)</div><div> - Fail: 6 (3.45%)</div>"],
[new Date(2018, 9, 9),95.82409460458240946045824095,"2,593 (95.82%)","<div>2018-10-09 (Tue)</div><div> - Pass: 2,593 (95.82%)</div><div> - Fail: 113 (4.18%)</div>"],
[new Date(2018, 9, 10),96.81303116147308781869688385,"2,734 (96.81%)","<div>2018-10-10 (Wed)</div><div> - Pass: 2,734 (96.81%)</div><div> - Fail: 90 (3.19%)</div>"],
[new Date(2018, 9, 11),96.80555555555555555555555556,"2,788 (96.81%)","<div>2018-10-11 (Thu)</div><div> - Pass: 2,788 (96.81%)</div><div> - Fail: 92 (3.19%)</div>"],
[new Date(2018, 9, 12),96.863295880149812734082397,"2,069 (96.86%)","<div>2018-10-12 (Fri)</div><div> - Pass: 2,069 (96.86%)</div><div> - Fail: 67 (3.14%)</div>"]
]
return data;
}
function GetJSONData() {
return {
cols: [
{ type: "date", id: "Date", label: "Date" },
{ type: "number", id: "Pass", label: "Pass %" },
{
type: "string",
id: "Annotation",
label: "Annotation",
p: { role: "annotation" }
},
{
type: "string",
id: "ToolTip",
label: "ToolTip",
p: { html: "true", role: "tooltip" }
}
],
rows: [
{
c: [
{ v: "Date(2018, 9, 6)" },
{ v: 96 },
{ v: "48 (96.00%)" },
{
v:
"<div>2018-10-06 (Sat)</div><div> - Pass: 48 (96.00%)</div><div> - Fail: 2 (4.00%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 8)" },
{ v: 96.55172413793103448275862069 },
{ v: "168 (96.55%)" },
{
v:
"<div>2018-10-08 (Mon)</div><div> - Pass: 168 (96.55%)</div><div> - Fail: 6 (3.45%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 9)" },
{ v: 95.82409460458240946045824095 },
{ v: "2,593 (95.82%)" },
{
v:
"<div>2018-10-09 (Tue)</div><div> - Pass: 2,593 (95.82%)</div><div> - Fail: 113 (4.18%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 10)" },
{ v: 96.81303116147308781869688385 },
{ v: "2,734 (96.81%)" },
{
v:
"<div>2018-10-10 (Wed)</div><div> - Pass: 2,734 (96.81%)</div><div> - Fail: 90 (3.19%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 11)" },
{ v: 96.80555555555555555555555556 },
{ v: "2,788 (96.81%)" },
{
v:
"<div>2018-10-11 (Thu)</div><div> - Pass: 2,788 (96.81%)</div><div> - Fail: 92 (3.19%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 12)" },
{ v: 96.863295880149812734082397 },
{ v: "2,069 (96.86%)" },
{
v:
"<div>2018-10-12 (Fri)</div><div> - Pass: 2,069 (96.86%)</div><div> - Fail: 67 (3.14%)</div>"
}
]
}
]
};
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>