Get minimum column value from related entity - javascript

Please, help me. I cant find information about how do this.
I have got this code. It load all products with all relations. One of relations is product item. In product item entity I have got price column.
How I can get minimal product item price without get in my response array of product items?
const { skip, take } = pagination;
const query = this.createQueryBuilder('product');
query.where('product.shop = :id AND product.blocked = FALSE', {
id: shop.id,
});
if (skip) {
query.offset(Number(skip));
}
if (take) {
query.limit(Number(take));
}
query.leftJoin('product.info', 'info');
query.leftJoin('product.avatar', 'avatar');
// load product items
query.leftJoin('product.productItem', 'item');
query.select([
'product.id',
'product.path',
'info.name',
'info.description',
'info.info',
'info.lang',
'avatar.path',
'avatar.type',
'item.price'
]);
const [list, amount] = await query.getManyAndCount();
Now i have got:
{
"list": [
{
"id": 3,
"path": "admin-product-2",
"order": 1,
"shop": {
"id": 1
},
"info": [
{
"name": "Admin Name ;)",
"description": "Shorty",
"info": "",
"lang": "RU"
}
],
"avatar": null,
"productItem": [
{
"price": 1000
},
{
"price": 500
},
{
"price": 300
},
{
"price": 2000
},
{
"price": 3000
}
]
}
]
}
........
But I need:
{
"list": [
{
"id": 3,
"path": "admin-product-2",
"order": 1,
"shop": {
"id": 1
},
"info": [
{
"name": "Admin Name ;)",
"description": "Shorty",
"info": "",
"lang": "RU"
}
],
"avatar": null,
"minProductItemPrice": 300
}
]
}
Pls help me

You can find the answer for this on Stackoverflow already.
Here is a similar question Typeorm select max with specific column
Basically, getManyAndCount() method that you are using is useful when fetching entities. In your case, you are trying to obtain an aggregate value encompassing multiple entities.
You need to make separate selection, like so
query.select("MIN(item.price)", "min");
and then get the result with
return query.getRawOne();

Related

How to enhance a json object?

I am recieving a json object from an api as following:
[
{"id":998,
"client":{"id":"AF01072","name":"Vivek","lastName":"Joshi","phone":123},
"membership":{"type":"Semi-Annually","price":18000},
"start_date":"2019-11-19","end_date":"2020-05-16","payment":12000,"balance":0
},
{"id":1004,
"client":{"id":"AF01072","name":"Vivek","lastName":"Joshi","phone":123},
"membership":{"type":"Annually","price":30000},
"start_date":"2020-08-27","end_date":"2021-08-27","payment":30000,"balance":5000,
}
]
in angular when I loop to print the data 'client' information prints two times, because it is repeated two times.
<div *ngFor="let c of clientinfo">
{{c.client.name}}
{{c.membership.type}}
{{c.membership.price}}
{{c.start_date}}
{{c.end_date}}
.
.
.
</div>
How can I enhance this data so I get only client information once but the rest stay as they are?
thanks in advance
Whenever you need to group some data by some value you can use an object because object properties are guaranteed to never be duplicated. You have to be careful however because properties can be overwritten.
First, let's settle on a structure that works. The following is one such structure that can work:
{
[client.id] : {
client: client,
info : [
{ membership, start_date },
{ membership, start_date },
..
]
},
[client.id] : {
client: client,
info : [
{ membership, start_date },
{ membership, start_date },
..
]
},
..
}
There are other ways to arrange the data but the key is to exploit objects to implement a map/hash/associative array of key/value pairs. With this we can group the data from your array:
let group = {};
jsonData.forEach(x => {
// check if group already contain this client:
if (!group[x.client.id]) {
// auto-create array if does not exist
group[x.client.id] = {
client: x.client,
info: []
};
}
group[x.client.id].info.push({
membership: x.membership,
start_date: x.start_date
});
});
Now you have an object grouped by client id where each property is an object that has an info array that contains membership information.
If you need to loop over this data structure you can convert it back to an array by doing:
let groupArray = Object.values(group);
This will give you the following data structure:
[
{
client: client,
info : [
{ membership, start_date },
{ membership, start_date },
..
]
},
{
client: client,
info : [
{ membership, start_date },
{ membership, start_date },
..
]
},
..
]
An array of client objects with an info property containing an array of membership data. For your sample data it would be something like:
[
{
"client":{"id":"AF01072","name":"Vivek","lastName":"Joshi","phone":123},
"info": [
{
"membership":{"type":"Semi-Annually","price":18000},
"start_date":"2019-11-19",
"end_date":"2020-05-16",
"payment":12000,"balance":0
},
{
"membership":{"type":"Annually","price":30000},
"start_date":"2020-08-27",
"end_date":"2021-08-27",
"payment":30000,"balance":5000,
}
]
}
]
You need to create array of memberships for each client like below. And in the template file, you need to use two *ngFor loops. One to loop client details and another one to loop the respective client's membership details.
Working stackblitz
const result = [{
"id": 998,
"client": {
"id": "AF01072",
"name": "Vivek",
"lastName": "Joshi",
"phone": 123
},
"membership": {
"type": "Semi-Annually",
"price": 18000
},
"start_date": "2019-11-19",
"end_date": "2020-05-16",
"payment": 12000,
"balance": 0
},
{
"id": 1004,
"client": {
"id": "AF01072",
"name": "Vivek",
"lastName": "Joshi",
"phone": 123
},
"membership": {
"type": "Annually",
"price": 30000
},
"start_date": "2020-08-27",
"end_date": "2021-08-27",
"payment": 30000,
"balance": 5000,
},
{
"id": 1005,
"client": {
"id": "AF01073",
"name": "Some Other",
"lastName": "Joshi",
"phone": 123
},
"membership": {
"type": "Annually",
"price": 30000
},
"start_date": "2020-08-27",
"end_date": "2021-08-27",
"payment": 30000,
"balance": 5000,
}
].reduce((acc, item, i) => {
if (i == 0) {
acc.push(constructCorrectObject(item));
return acc;
}
let foundItem = acc.find(it => it.id === item.client.id);
if (foundItem) {
addMemeberShipDetails(foundItem, item);
return acc;
} else {
acc.push(constructCorrectObject(item));
return acc;
}
}, []);
function constructCorrectObject(item) {
return {
"id": item.client.id,
"name": item.client.name,
"lastName": item.client.lastName,
"phone": item.client.phone,
"membership": [{
"id": item.id,
"type": item.membership.type,
"price": item.membership.price,
"start_date": item.start_date,
"end_date": item.end_date,
"payment": item.payment,
"balance": item.balance,
}]
}
}
function addMemeberShipDetails(acc, item) {
acc.membership.push({
"id": item.id,
"type": item.membership.type,
"price": item.membership.price,
"start_date": item.start_date,
"end_date": item.end_date,
"payment": item.payment,
"balance": item.balance,
});
}
console.log(result);

Postman - How to count occurrences of a specific object in a JSON response

I am new to JSON and Postman. I believe I'm trying to do something very simple.
I have created a GET request which will get a JSON response like the one below.
In the example below I want to get the count of All "IsArchived" attributes in the response;
The number of those attributes will vary from response to response.
How can I do it? Thanks in advance
{
"Id": 1328,
"Name": "AAA Test",
"Owner": {
"Id": 208,
"Name": "The Boss"
},
"FieldGroups": [
{
"Id": "c81376f0-6ac3-4028-8d61-76a0f815dbf8",
"Name": "General",
"FieldDefinitions": [
{
"Id": 1,
"DisplayName": "Product Name",
"IsArchived": false
},
{
"Id": 2,
"DisplayName": "Short Description",
"IsArchived": false
},
{
"Id": 33,
"DisplayName": "Long Description",
"IsArchived": false
},
]
},
{
"Id": "5ed8746b-0fa8-4022-8216-ad3af17db91f",
"Name": "Somethingelse",
"FieldDefinitions": [
{
"Id": 123,
"DisplayName": "Attribution",
"IsArchived": false
},
{
"Id": 1584,
"DisplayName": "FC1",
"IsArchived": false
},
{
"Id": 623,
"DisplayName": "Sizes",
"IsArchived": false,
"Owner": {
"Id": 208,
"Name": "The Boss"
},
"Unit": "",
"Options": [
{
"Id": 1,
"Value": "XS"
},
{
"Id": 2,
"Value": "S"
},
{
"Id": 3,
"Value": "M"
}
]
}
]
}
],
"IsArchived": false
"Version": 1
}
It is a rather specific solution but I hope it helps. The description is added as comments:
// Convert the response body to a JSON object
var jsonData = pm.response.json()
// Create a count variable which will be increased by 1 everytime IsArchived occurs
var count = 0;
function countIsArchived() {
// Loop through the FieldGroupsArray
_.each(jsonData.FieldGroups, (fieldGroupsArray) => {
// Loop through the FieldDefinitionsArray
_.each(fieldGroupsArray.FieldDefinitions, (fieldDefinitionsArray) => {
// Check if IsArchived exists
if(fieldDefinitionsArray.IsArchived) {
// Increase count by 1
count++;
}
});
});
// IF you want it:
// Check if IsArchived exists on the top level of the JSON response and increase count
if(jsonData.IsArchived) {
count++;
}
// IF you want it:
// Create a Postman environment variable and assign the value of count to it
pm.environment.set("count", count);
}
Additional info:
The , after the following object is not needed. It invalidates the JSON:
{
"Id": 33,
"DisplayName": "Long Description",
"IsArchived": false
}, <--

Find nested object based on a parameter in Javascript

I have an array which looks like this:
[
{
"boxes": [
{
"id": 2,
"content": {
"name": "ABC",
"details": "some details for abc"
}
}
]
},
{
"boxes": [
{
"id": 3,
"content": {
"name": "XYZ",
"details": "some details for xyz"
}
},
{
"id": 4,
"content": {
"name": "UVW",
"details": "some details for uvw"
}
}
]
},
{}
]
And I have a variable: let id = 3
I want to be able to search through the nested array "boxes" to find the content property of the object that have the given id. Such that the result is:
{
"name": "XYZ",
"details": "some details for xyz"
}
Till now I have gathered that I can use a combination forEach and .filter do find this. But I'm not sure how. Also, I have control over the original data. So, if there's a better way to store the original data, I will be glad to have suggestions.
Actually I did attempt but got stuck:
Let's say the original array is called house.
let matches = []
let id = 3
house.forEach(function(e) {
matches = matches.concat(e.boxes.filter(function(b) {
return (b.id === id);
}));})
console.log(matches[0].content)
You could use map method by passing a callback function as argument. The scope of map method is to get all items from boxes array.
Also, I'm using filter(Boolean) statement in order to remove undefined value for those items which doesn't have boxes as property.
At least, use find method in order to get the desired output result.
let arr = [ { "boxes": [ { "id": 2, "content": { "name": "ABC", "details": "some details for abc" } } ] }, { "boxes": [ { "id": 3, "content": { "name": "XYZ", "details": "some details for xyz" } }, { "id": 4, "content": { "name": "UVW", "details": "some details for uvw" } } ] }, {} ]
let id = 1;
let result = [].concat(...arr.map(item => item.boxes))
.filter(Boolean)
.find(({id}) => id == id).content;
console.log(result);
First you need to put boxes object into one array, it seems it just has couple extra levels you don't need; You can create a helper function for that.
make a loop with if statement if there is what you are looking for or not
Here I use forEach() to loop through the data and get the boxes.
After that, I use filter() to get the object with the desired id. That's all.
const data = [
{
"boxes": [
{
"id": 2,
"content": {
"name": "ABC",
"details": "some details for abc"
}
}
]
},
{
"boxes": [
{
"id": 3,
"content": {
"name": "XYZ",
"details": "some details for xyz"
}
},
{
"id": 4,
"content": {
"name": "UVW",
"details": "some details for uvw"
}
}
]
},
{}
]
data.forEach(data => {
if (data.boxes){
let searchedData = data.boxes.filter(box => box.id == 3);
if (searchedData.length > 0){
console.log(searchedData[0].content);
}
}
});

Analysis Paralysis in looping over JSON data to populate select box with optgroup sections

I have Analysis Paralysis and need some input. I can modify the SQL query, the JavaScript, AND/or the CFML controller (all code has been posted below).
All I'm looking to do is to populate a select box with options and optgroups. The optgroup is what is tripping me up here.
The sql is pretty basic and looks like this:
SELECT
g.groupID,
g.groupLabel,
u.unitLabel,
u.unitID
FROM
group g
LEFT JOIN unit u ON g.groupID = u.groupID
And the CFML loop(s) is as follows (this is also where I believe the adjustment should be made with some logic such as if thisGroupLabel matches the preGroupLabel, stay within loop and keep adding unitLabel and unitIDs) but is there a more efficient way?:
local.data.unitLabels = [];
for(local.row in local.__unitLabels){
local.unit = {};
local.unit.groupLabel = local.row.groupLabel;
local.unit.unitLabel = local.row.unitLabel;
local.unit.unitID = local.row.unitID;
// loop over the array so that we can identify which one needs to be preselected
for(local.dataValue in local.data.unitDetails){
if (local.unit.unitID eq local.dataValue.unitID) {
local.unit.selected = 'selected';
} else {
local.unit.selected = '';
}
}
arrayAppend(local.data.unitLabels, local.unit);
}
The JSON data looks like this but I have access to the query so I can reformat it if needed:
{
"data": {
"selectDataOptions": [{
"groupLabel": "COMPLETION",
"selected": "",
"unitID": 1,
"unitLabel": "Completion"
}, {
"groupLabel": "DISTANCE",
"selected": "",
"unitID": 2,
"unitLabel": "Meters"
}, {
"groupLabel": "DISTANCE",
"selected": "",
"unitID": 3,
"unitLabel": "Miles"
}, {
"groupLabel": "DISTANCE",
"selected": "",
"unitID": 4,
"unitLabel": "Yards"
}, {
"groupLabel": "TIME",
"selected": "",
"unitID": 5,
"unitLabel": "Hours"
}, {
"groupLabel": "TIME",
"selected": "",
"unitID": 5,
"unitLabel": "minutes"
}, {
"groupLabel": "TIME",
"selected": "",
"unitID": 5,
"unitLabel": "Seconds"
}]
}
}
As it stands, my select box looks like this (roughly):
<select>
<optgroup>COMPLETION</optgroup>
<option>Complettion</option>
<optgroup>DISTANCE</OPTGROUP>
<option>Meters</option>
<optgroup>DISTANCE</optgroup>
<option>Miles</option>
<optgtroup>DISTANCE</optgroup>
<option>Yards</option>
<optgtroup>TIME</optgroup>
<option>Hours</option>
<optgtroup>TIME</optgroup>
<option>Minutes</option>
</select>
Notice that the optgroup Distance and TIME are repeated. The desired output would look like this:
<select>
<optgroup>COMPLETION</optgroup>
<option>Complettion</option>
<optgroup>DISTANCE</OPTGROUP>
<option>Meters</option>
<option>Miles</option>
<option>Yards</option>
<optgroup>TIME</optgroup>
<option>Hours</option>
<option>Mintues</option>
</select>
Is the issue how to construct a JSON string that Select2 can understand? I'd suggest creating a nested array of children for each GroupLabel, as described in the documentation under Grouped Data.
CF11+ and Lucee 4.5+ support cfloop "group", which would make things a lot easier. Just cfloop through the query and group by "groupLabel". (NB: Don't forget to modify the SQL query and ORDER BY g.groupLabel so the grouping works as expected.)
TryCF.com Example
Code:
data= [];
cfloop(query="qDemo", group="groupLabel") {
children = [];
cfloop() {
arrayAppend(children, {"id": qDemo.unitID, "text": qDemo.unitLabel});
}
arrayAppend(data, {"text" : qDemo.GroupLabel, "children" : children });
}
writeDump(serializeJSON(data));
Result:
[
{
"text": "COMPLETION",
"children": [
{
"text": "Completion",
"id": 1
}
]
},
{
"text": "DISTANCE",
"children": [
{
"text": "Meters",
"id": 2
},
{
"text": "Miles",
"id": 3
},
{
"text": "Yards",
"id": 4
}
]
},
{
"text": "TIME",
"children": [
{
"text": "Hours",
"id": 5
},
{
"text": "minutes",
"id": 5
},
{
"text": "Seconds",
"id": 5
}
]
}
]

MongoDB, Mongoose, How to remove duplicate items from the doc.find results after populating that data?

I have an array of items in my database, some of these items have an array called relatedItems, which are basically an array of IDs, and these IDs could match some of the items inside the same document. I can run populate to transform the array of IDs into an array of objects, where each object is the populated item with the matched ID. But I also want to remove those items from the main array of items. Here is the example of my data structure:
{
"data": {
"getItems": [
{
"title": "item 09",
"_id": "5a56215426004a22c17ba733",
"relatedItems": ["5a5621a526004a22c17ba773", "5a56214b26004a22c17ba72a", "5a56215326004a22c17ba732", "5a56215726004a22c17ba735"]
},
{
"title": "item 10",
"_id": "5a56215726004a22c17ba735",
"thumbnail": {
"Key": ".../5a56215726004a22c17ba735/thumbnail.jpg"
}
},
{
"title": "item 11",
"_id": "5a56215326004a22c17ba732",
"thumbnail": {
"Key": ".../5a56215326004a22c17ba732/thumbnail.jpg"
}
},
{
"_id": "5a56216b26004a22c17ba747",
"relatedItems": []
},
{
"_id": "5a56216c26004a22c17ba748",
"relatedItems": []
}
]
}
}
And I am running the following code to populate the data:
let items = await ItemModel.find({ category }).limit(limit).sort({ itemOrder: 1 })
.populate({path: 'relatedItems', select: '_id thumbnail'}).sort({ itemOrder: 1 });
But as you know, after populating, and I am going to end up having duplicate items (one copy in the related items array and one in the main array) like so:
{
"data": {
"getItems": [
{
"title": "item 09",
"_id": "5a56215426004a22c17ba733",
"relatedItems": [
"_id": "5a5621a526004a22c17ba773",
"thumbnail": {
"Key": ".../5a5621a526004a22c17ba773/thumbnail.jpg"
}
},
{
"_id": "5a56214b26004a22c17ba72a",
"thumbnail": {
"Key": ".../5a56214b26004a22c17ba72a/thumbnail.jpg"
}
},
{
"title": "item 11",
"_id": "5a56215326004a22c17ba732",
"thumbnail": {
"Key": ".../5a56215326004a22c17ba732/thumbnail.jpg"
}
},
{
"title": "item 10",
"_id": "5a56215726004a22c17ba735",
"thumbnail": {
"Key": ".../5a56215726004a22c17ba735/thumbnail.jpg"
}
}
]
},
{
"title": "item 10",
"_id": "5a56215726004a22c17ba735",
"thumbnail": {
"Key": ".../5a56215726004a22c17ba735/thumbnail.jpg"
}
},
{
"title": "item 11",
"_id": "5a56215326004a22c17ba732",
"thumbnail": {
"Key": ".../5a56215326004a22c17ba732/thumbnail.jpg"
}
},
{
"_id": "5a56216b26004a22c17ba747",
"relatedItems": []
},
{
"_id": "5a56216c26004a22c17ba748",
"relatedItems": []
}
]
}
}
I would like to remove the copy from the main array, and currently, I have accomplished it via the following code:
let items = await ItemModel.find({ category }).limit(limit).sort({ itemOrder: 1 })
.populate({path: 'relatedItems', select: '_id thumbnail'}).sort({ itemOrder: 1 });
Promise.all(items.map(async item => {
if (item.relatedItems && item.relatedItems.length > 0) {
Promise.all(item.relatedItems.map(async relatedItem => {
related.push(relatedItem._id);
}));
}
}));
items = items.filter(item => !JSON.stringify(related).includes(item._id));
And it works, but I am wondering if there is a faster/safer or more MongoDB native way to do this?
Edit
One important downside to using my filter method is that .limit(limit) does not work properly anymore since the array will be trimmed down if it finds duplicates, the workaround is using .slice(0, limit) on the final array after filtering, but doesn't sound like it's the best way. Also I kinda feel I am going to run into some issues if I decide to add pagination to it too.

Categories

Resources