How to get sum of of sums in laravel on to many to many relationship in laravel - sql

I have a User table that have many TeamMember and each TeamMember have many Order. My query look like this at the moment:
$users = User::select('id', 'username')
->with(['userTeamMembers' => function ($query) {
$query->select('team_lead_id', 'team_member_id')
->withSum('completedOrders', 'amount')
->whereHas('completedOrders');
}])
->whereHas('userTeamMembers.completedOrders')
->get()
->toArray();
The output:
[
{
"id": 1,
"username": "user1",
"user_team_members": [
{
"team_lead_id": 1,
"team_member_id": 19,
"completed_orders_sum_amount": "100.00"
},
{
"team_lead_id": 1,
"team_member_id": 34,
"completed_orders_sum_amount": "200.00"
},
]
},
{
"id": 5,
"username": "user5",
"user_team_members": [
{
"team_lead_id": 5,
"team_member_id": 25,
"completed_orders_sum_amount": "50.00"
},
{
"team_lead_id": 5,
"team_member_id": 67,
"completed_orders_sum_amount": "50.00"
},
]
}
]
But what I'm trying to achieve is a result like below:
[
{
"id": 1,
"username": "user1",
"user_team_members_sum": "300.00"
},
{
"id": 5,
"username": "user5",
"user_team_members_sum": "100.00"
}
]
As you can see, all of the team members total are added together compared to the former output. Is the result like above achievable? My Models are like below:
User Model
class User extends Authenticatable
{
...
public function userTeamMembers()
{
return $this->hasMany(TeamMember::class, 'team_lead_id');
}
}
Team Member Model
class TeamMember extends Model
{
....
public function completedOrders()
{
return $this->hasMany(Order::class, 'user_id', 'team_member_id')->select('user_id', 'amount')->where('status', 1);
}
}

// Try this. It could serve your desire output
$users = User::select('id', 'username')
->withSum('userTeamMembers.completedOrders', 'amount')
->whereHas('userTeamMembers.completedOrders')
->get()
->toArray();

Related

Laravel: query JSON column that has array of objects

I have a table that looks like this:
id
json_col
35
[{"key_one":4,"key_two":{"value":1,"type":"static"}},{"key_one":27,"key_two":{"value":26,"type":"dynamic"}}]
36
[{"key_one":2,"key_two":{"value":33,"type":"static"}},{"key_one":9,"key_two":{"value":1,"type":"any"}}]
[
{
"id": 35,
"json_col": [
{
"key_one": 4,
"key_two": {
"value": 1,
"type": "static"
}
},
{
"key_one": 27,
"key_two": {
"value": 26,
"type": "dynamic"
}
}
],
"created_at": "2023-02-13T16:54:13.000000Z",
"updated_at": "2023-02-13T16:54:13.000000Z"
},
{
"id": 36,
"json_col": [
{
"key_one": 2,
"key_two": {
"value": 33,
"type": "static"
}
},
{
"key_one": 9,
"key_two": {
"value": 1,
"type": "any"
}
}
],
"created_at": "2023-02-13T16:54:56.000000Z",
"updated_at": "2023-02-13T16:54:56.000000Z"
}
]
How to get the row 35 that has key_two with value = 1 and type = static, using the query builder or a raw query?
you can use whereJsonContains with multi-dimentional array base on how you store them.
return Model::whereJsonContains('json_col', [ ['key_two' => [ 'value' => 1] ] ])
->whereJsonContains('json_col', [ ['key_two' => [ 'type' => 'static'] ] ])
->paginate(10); // or get()
just double check the sql output actually looks like your json format which should look something like
WHERE json_contains(`json_col`, '[{\"key_two\":{\"value\":1}}]')
AND json_contains(`json_col`, '[{\"key_two\":{\"type\":\"static\"}}]')
EDIT
If you need to search multiple match in single object, then this should do
return Model::whereJsonContains('json_col', [ ['key_two' => [ 'value' => 1, 'type' => 'static' ] ] ])
->paginate(10); // or get()

Mongodb aggregation to find outliers

In my mongodb collection documents are stored in the following format:
{ "_id" : ObjectId("62XXXXXX"), "res" : 12, ... }
{ "_id" : ObjectId("63XXXXXX"), "res" : 23, ... }
{ "_id" : ObjectId("64XXXXXX"), "res" : 78, ... }
...
I need to extract id's for the document for which the value of "res" is outlier (i.e. value < Q1 - 1.5 * IQR or value > Q3 + 1.5 * IQR (Q1, Q3 are percentiles)). I have done this using pandas functionality by retrieving all documents from the collection, which may become slow if the number of documents in collection become too big.
Is there a way to do this using mongodb aggregation pipeline (or just calculating percentiles)?
If I understand how you want to retrieve outliers, here's one way you might be able to do it.
db.collection.aggregate([
{ // partition res into quartiles
"$bucketAuto": {
"groupBy": "$res",
"buckets": 4
}
},
{ // get the max of each quartile
"$group": {
"_id": "$_id.max"
}
},
{ // sort the quartile maxs
"$sort": {
"_id": 1
}
},
{ // put sorted quartile maxs into array
"$group": {
"_id": null,
"maxs": {"$push": "$_id"}
}
},
{ // assign Q1 and Q3
"$project": {
"_id": 0,
"q1": {"$arrayElemAt": ["$maxs", 0]},
"q3": {"$arrayElemAt": ["$maxs", 2]}
}
},
{ // set IQR
"$set": {
"iqr": {
"$subtract": ["$q3", "$q1"]
}
}
},
{ // assign upper/lower outlier thresholds
"$project": {
"outlierThresholdLower": {
"$subtract": [
"$q1",
{"$multiply": ["$iqr", 1.5]}
]
},
"outlierThresholdUpper": {
"$add": [
"$q3",
{"$multiply": ["$iqr", 1.5]}
]
}
}
},
{ // get outlier _id's
"$lookup": {
"from": "collection",
"as": "outliers",
"let": {
"oTL": "$outlierThresholdLower",
"oTU": "$outlierThresholdUpper"
},
"pipeline": [
{
"$match": {
"$expr": {
"$or": [
{"$lt": ["$res", "$$oTL"]},
{"$gt": ["$res", "$$oTU"]}
]
}
}
},
{
"$project": {
"_id": 1
}
}
]
}
}
])
Try it on mongoplayground.net.
One more option based on #rickhg12hs's answer, is to use $setWindowFields:
db.collection.aggregate([
{$setWindowFields: {
sortBy: {res: 1},
output: {
totalCount: {$count: {}},
index: {$sum: 1, window: {documents: ["unbounded", "current"]}}
}
}
},
{$match: {
$expr: {$lte: [
{$abs: {$subtract: [
{$mod: [
{$multiply: [
{$add: ["$index", {$round: {$divide: ["$totalCount", 4]}}]}, 2]},
"$totalCount"
]}, 0]}
}, 1]}
}},
{$group: {_id: null, res: {$push: "$res"}}},
{$project: {_id: 0, q1: {$first: "$res"}, q3: {$last: "$res"},
iqr: {"$subtract": [{$last: "$res"}, {$first: "$res"}]}
}},
{$project: {
outlierThresholdLower: {$subtract: ["$q1", {$multiply: ["$iqr", 1.5]}]},
outlierThresholdUpper: {$add: ["$q3", {$multiply: ["$iqr", 1.5]}]}
}
},
{$lookup: {
from: "collection",
as: "outliers",
let: {oTL: "$outlierThresholdLower", oTU: "$outlierThresholdUpper"},
pipeline: [
{$match: {$expr: {$or: [{$lt: ["$res", "$$oTL"]}, {$gt: ["$res", "$$oTU"]}]}}},
{$project: {_id: 1}}
]
}
}
])
See how it works on the playground example

knex json array of object where condition

I have a json array (composition) in my db and i need to make conditions
[
{
"name": "Rock",
"prices": [
{
"price": 400,
"duration": 40
}
],
"description": "Hello",
"nb_musicians": 40
}
]
And i try something like this but dont work
.where(qb => {
if (nbMusicians) {
qb.whereRaw(
`composition->
'$.nb_musicians' = ?`,
[40],
);
}
})
Thanks for help

Nest JS - Serialization not work with nested object

I want to append a new field called logo_img_url after data manipulation. It works with the object but the nested array object seems not to work. Any recommendations or solution
import { Expose } from 'class-transformer';
import {} from 'class-validator';
import { Entity, Column } from 'typeorm';
import { BaseEntity } from '#common/base/entity.base';
import { getAdminImageUrl } from '#common/util/vault-file.util';
#Entity()
export class Agency extends BaseEntity {
#Expose()
#Column({
type: 'varchar',
length: '255',
nullable: true,
})
public logo_key!: string;
#Expose({ name: 'logoImgUrl' })
logo_img_url = this.logoImgUrl;
#Expose()
get logoImgUrl(): any {
return "http://localhost:30001/". this.logo_key;
}
}
Actaul response:
{
"data": [
{
"logo_img_url": "",
"id": 22,
"logo_url": "9597e74d-aaea-4502-b5cd-b2867504fd80",
},
{
"logo_img_url": "",
"id": 21,
"logo_url": "0a655497-1318-4276-a21f-cfdc259241a2",
},
],
"meta": {
"status_code": 200,
"pagination": {
"totalPages": 1,
"currentPage": 1,
"nextPage": null,
"paginationToken": null,
"previousPage": null,
"totalItems": 9,
"itemsPerPage": 10
}
}
}
Expected result:
{
"data": [
{
"logo_img_url": "http://localhost:3000/9597e74d-aaea-4502-b5cd-b2867504fd80",
"id": 22,
"logo_url": "9597e74d-aaea-4502-b5cd-b2867504fd80",
},
{
"logo_img_url": "http://localhost:3000/0a655497-1318-4276-a21f-cfdc259241a2",
"id": 21,
"logo_url": "0a655497-1318-4276-a21f-cfdc259241a2",
},
],
"meta": {
"status_code": 200,
"pagination": {
"totalPages": 1,
"currentPage": 1,
"nextPage": null,
"paginationToken": null,
"previousPage": null,
"totalItems": 9,
"itemsPerPage": 10
}
}
}
You can use #AfterLoad decorator from typeorm. Its calls the decorated method once the entity is loaded and then one can perform the required manipulations.
logo_img_url:string
#AfterLoad()
async transform(){
this.logo_img_url=`http://localhost:30001/${this.logo_key}`
}

How to returning only as string column in json response object from eloquent relationship using eager loading

I'm trying to figure out how to eager load data as a string instead of array object from a related table.
I have 3 models and here are the relations
Product.php
class Product extends Model
{
public function ProductTag()
{
return $this->hasMany(ProductTag::class);
}
public function Category()
{
return $this->belongsTo(Category::class);
}
}
ProductTag.php
class ProductTag extends Model
{
public function Product()
{
return $this->belongsTo(Product::class);
}
}
Category.php
class Category extends Model
{
public function Product()
{
return $this->hasMany(Product::class);
}
}
I've tried doing it like so:
public function tag(){
return $this->hasMany(ProductTag::class)
->selectRaw('GROUP_CONCAT(tag) as tag,id')
->groupBy('id');
}
public static function list(){
$result = Category::with(['Product'=>function($q){
$q->with(['tag'=>function($q1){
$q1->first();
}]);
}])->get();
}
Here is the reponse:
{
"data": {
"categories": [
{
"id": 1,
"category": "test 1",
"product": [
{
"id": 46,
"name": "test prod 1",
"tag": []
},
{...}
]
},
{
"id": 2,
"category": "test 2",
"product": [
{
"id": 45,
"name": "test prod 2",
"tag": [
{
"product_tag": "Test1, test12, test123"
}
]
},
{...}
]
},
{
"id": 3,
"category": "test 3",
"product": []
}
]
}
}
The Response is as expected except tag array so, instead of an array named "tag" can I get "product_tag" within the "product" array
"product": [
{
"id": 45,
"name": "test prod 2",
"tag": [
{
"product_tag": "Test1, test12, test123"
}
]
}
]
Here is what I want it to look like:
"product": [
{
"id": 45,
"name": "test prod 2",
"product_tag": "Test1, test12, test123"
}
]
Is it possible and any smart way of doing this in Laravel using Eloquent?
Simple :)
Btw, if you can - rename product_tag in response to tag_line or same - it's not right way to take same name for relation and mutator.
class Product extends Model
{
public function getTagLineAttribute()
{
//tag - is "name" field in ProductTag
return $this->ProductTag->pluck('tag')->implode(',');
}
public function ProductTag()
{
return $this->hasMany(ProductTag::class);
}
public function Category()
{
return $this->belongsTo(Category::class);
}
}
//eager loading with tags
Product::with('ProductTag')->find(..)->tagLine;