Count multiple associated models in sequelize - mysql

I have 3 models defined, Companies, Projects and Users.
Companies has many Projects and Users, so Projects and users has one company.
The problem that i am facing is when i try to count how many users and projects a company has, i get wrong results, somehow querying multiple associations is modifying the value of each other.
This is the models association:
CompaniesModel.hasMany(AppUsersModel)
AppUsersModel.belongsTo(CompaniesModel)
CompaniesModel.hasMany(ProjectsModel)
ProjectsModel.belongsTo(CompaniesModel)
This is where i try to query the count of Users and Projects for each Company:
const companiesInfo = await models.companies.findAll({
attributes: {
include: [
[sequelize.fn("COUNT", sequelize.col("projects.id")), "projectsCount"],
[sequelize.fn("COUNT", sequelize.col("app_users.id")), "usersCount"],
],
},
include: [
{ model: models.projects, attributes: [] },
{ model: models.app_users, attributes: [] },
],
group: ['companies.id']
})
SQL Query produced:
SELECT `companies`.`id`, COUNT(`projects`.`id`) AS `projectsCount`, COUNT(`app_users`.`id`) AS `usersCount` FROM `companies` AS `companies` LEFT OUTER JOIN `projects` AS `projects` ON `companies`.`id` = `projects`.`companyId` LEFT OUTER JOIN `app_users` AS `app_users` ON `companies`.`id` = `app_users`.`companyId` GROUP BY `companies`.`id`
With this query i expected to get an array of companies like this:
[
{name: companyX, usersCount: 1, projectsCount: 2},
{name: companyY, usersCount: 5, projectsCount: 1},
...
]
But what i am getting is almost what i want but for some reasons the values are being multiplied by each other, for example:
[
{name: companyX, usersCount: 2, projectsCount: 2},
{name: companyY, usersCount: 5, projectsCount: 5},
...
]
This is what i get event though companyX only has 1 user and companyY only has 1 project, but it seems to me that, in the case of companyX, usersCount is being multiplied by projectsCount.
For instance if companyX has 2 users and 2 projects i will get the following results:
[
{name: companyX, usersCount: 4, projectsCount: 4},
...
]
I would be grateful for any insight in to a solution for this.

You need a distinct count if you are counting 2 columns in the same query.
include: [
[sequelize.fn("COUNT", sequelize.fn("DISTINCT", sequelize.col("projects.id"))), "projectsCount"],
[sequelize.fn("COUNT", sequelize.fn("DISTINCT", sequelize.col("app_users.id"))), "usersCount"],
],

Related

Is such a result possible with a query from an SQL database?

I want to fire a query to get such a result:
[{
id: 1,
brandName: "x"
brandModels: [
{id: 1, modelName: "abc", createdAt: "yyyy-mm-dd"},
{id: 2, modelName: "def", createdAt: "yyyy-mm-dd"},
]
},
{
id: 2,
brandName: "y"
brandModels: [
{id: 4, modelName: "ghi", createdAt: "yyyy-mm-dd"},
{id: 5, modelName: "jkl", createdAt: "yyyy-mm-dd"},
]
}]
Tables Schema
BrandsTable
{id, brandName, brand_id}
ModelsTable
{id, modelName, createdAt}
I guess it's not possible like that? I don't have any experience with text-based databases, but I can well imagine that this can be achieved with a MongoDB. Because ultimately I want to have a js object at the end of the day.
Here's an example but I have not tested it:
SELECT JSON_ARRAYAGG(
JSON_OBJECT(
'id', b.id,
'brandName', b.brandName,
'brandModels', m.modelArray
)
) AS joined_result
FROM BrandTable AS b
JOIN (
SELECT brand_id, JSON_ARRAYAGG(
JSON_OBJECT(
'id', id,
'modelName', modelName,
'createdAt', createdAt
)
) AS modelArray
FROM ModelsTable
GROUP BY brand_id
) AS m USING (brand_id);
Note that I had to assume the ModelsTable also contains a column for brand_id, otherwise there is nothing to use in the join condition.
Read documentation for JSON_OBJECT() and JSON_ARRAYAGG() for more information about how these functions work.

Select from json column

There is a task to obtain most ordered products from orders table for given period of time. Table have JSON column called details and ordered products IDs are stored there:
{
products: [
{
id: 1,
quantity: 2,
},
{
id: 6,
quantity: 1
}
]
}
Currently, I'm solving it this way, which kind of inefficient:
Order::whereBetween('created_at', $period)
->get('details->products as details')
->flatMap(fn ($order) => $order->details)
->groupBy('id')
->map(fn ($products) => $products->sum('quantity'));
The result contains pairs, where key - product id, and value total count from orders:
[
1 => 2,
6 => 1,
];
Is it possible to query product id and quantity directly?

Retrive all the value that satisfy the condition of first table

I have two tables users and location. I need to join both tables
what i need is get all the area number of all the users which are present in the user table.
ie user 1 has 3 entries in the second table so i need to join the table in such a way that is,
id1 = 1
area = 2,3
area 2 is repeating so do not include it twice
i tried the join but now getting the correct way to doing it.
What i tried?
$location = User::
join('addresses','users.id1','=','addresses.id1') ->select('users.id1','addresses.area')
->get();
Expected Output
User 1 -> area ->2,3
Here are the two ways to do this.
Firstly you can use Laravel relationship:-
In your model User create relationship:-
function addresses()
{
return $this->hasMany(Address::class, 'id1', 'id1');
}
Now in your User controller you can get User addresses (areas) like this
$users = User::with('addresses')->get();
dd($users->toArray());
This will print something like this
[
{
id1: 1,
name: abaa
pwd: 12345
addresses: [
{
id2: 1,
id1: 1,
area: 2
},
{
id2: 2,
id1: 1,
area: 3
},
{
id2: 3,
id1: 1,
area: 3
}
]
},
{
...
}
]
Second you can use Laravel relationship:-
$builder = new User;
$builder->join('addresses','users.id1','=','addresses.id1')
->selectRaw("users.*, GROUP_CONCAT(DISTINCT addresses.area SEPARATOR ',') as distinct_areas")
->groupBy("users.id1")
->get();
This query will give you result something like this
[
{
id1: 1,
name: abaa,
pwd: 12345,
distinct_areas: 2,3
},
{
...
}
]
I think this will help you.

Get records with nested items node-mysql

Im using node-mysql for database query manpulation in my expressjs app, And I want to get a nested results from a query. But seems I can't find a way how to do it in a simple way.
In their document I found Joins with overlapping column names
which can query a table name and nested with a columns. However this
is not suitable work around for one-to-many or many-to-many structure.
Following are examples of table structure and expected results .
Tables
tbl_order
id customer_name date
1 Perona 6/7/2018
2 Zorro 6/8/2018
tbl_order_items
id order_id item_name
1 2 compass
2 2 sword
3 2 yakuta
4 1 umbrella
5 1 doll
Expected Results
I want to get all orders and items containing the order.
[
{
id: 1,
customer_name: perona,
data: 6/7/2018,
items: [
{ id: 4, item_name: umbrella },
{ id: 5, item_name: doll },
]
},
{
id: 2,
customer_name: zorro,
data: 6/8/2018,
items: [
{ id: 1, item_name: compass },
{ id: 2, item_name: sword },
{ id: 3, item_name: yakuta },
]
}
]
Is there any option how to do the same in node-mysql?
You can use INNER JOIN to get nested data based on your association.
SELECT [order].*,
[items].[id] AS [items.id]
[items].[item_name] AS [items.item_name]
FROM
(
SELECT [id],
[customer_name],
[date]
FROM [tbl_order]
) AS [order]
INNER JOIN
[tbl_order_items] AS [items]
ON
[order].[id] = [items].[order_id];

Select from two tables and create object with data

I'm trying to fetch data from two MySQL tables and push each into array.
I have to tables item and user with the same column - item_id. Now im requesting the data from them with
var query = "SELECT * FROM items, users WHERE items.item_id=?";
query = connection.format(query,req.params.item_id);
After this, im getting a wall of data. Like this:
{
{ user_id: 13213,
user_name: 'John',
item_id: 1337,
item_name: Leet stuff,
item_price: 13,37
},
{ user_id: 12345,
user_name: 'Mike',
item_id: 1337,
item_name: Leet stuff,
item_price: 13,37
}
But what I actualy need is this:
{
users: [
{ user_id: 13213,
user_name: 'John'
},
{ user_id: 12345,
user_name: 'Mike'
}
],
item_id: 1337,
item_name: Leet stuff,
item_price: 13,37
}
You can't do that in a single query. However, you can use two different queries and combine the output, e.g.:
SELECT u.user_id, u.user_name
FROM users u JOIN ITEMS i ON j.user_id = i.user_id
WHERE i.item_id = ?;
This will give you the list of users. You can then use the below query to get the item details:
SELECT item_id, item_name, item_price
FROM items
WHERE item_id = ?
You can then construct the required structure in your application.