Knex, Objection.js, How to sort by number of associations - postgresql

I have an Express API using Postgres via Knex and Objection.
I want to set up a Model method or scope that returns an array of Parent model instances in order of number of associated Children.
I have looked through the Knex an Objection docs
I have seen the following SQL query that will fit, but trying to figure out how to do this in Knex:
SELECT SUM(O.TotalPrice), C.FirstName, C.LastName
FROM [Order] O JOIN Customer C
ON O.CustomerId = C.Id
GROUP BY C.FirstName, C.LastName
ORDER BY SUM(O.TotalPrice) DESC

This is how it should be done with knex (https://runkit.com/embed/rj4e0eo1d27f):
function sum(colName) {
return knex.raw('SUM(??)', [colName]);
}
knex('Order as O')
.select(sum('O.TotalPrice'), 'C.FirstName', 'C.LastName')
.join('Customer C', 'O.CustomerId', 'C.Id')
.groupBy('C.FirstName', 'C.LastName')
.orderBy(sum('O.TotalPrice'), 'desc')
// Outputs:
// select SUM("O"."TotalPrice"), "C"."FirstName", "C"."LastName"
// from "Order" as "O"
// inner join "Customer C" on "O"."CustomerId" = "C"."Id"
// group by "C"."FirstName", "C"."LastName"
// order by SUM("O"."TotalPrice") desc
But if you are really using objection.js then you should setup models for your Order and Customer tables and do something like this:
await Order.query()
.select(sum('TotalPrice'), 'c.FirstName', 'c.LastName')
.joinRelated('customer as c')
.groupBy('c.FirstName', 'c.LastName')
.orderBy(sum('TotalPrice'), 'desc')

In case anyone is interested, I just included the raw SQL query as follows:
router.get('/leaderboard', async (req, res) => {
let results = await knex.raw('SELECT users_id, username, COUNT(acts.id) FROM acts INNER JOIN users ON acts.users_id = users.id GROUP BY users_id, username ORDER BY COUNT(acts.id) DESC');
console.log(results);
res.json(results.rows);
});

Related

Compare two arrays in LINQ query

I want to write LINQ query to compare two arrays. I want the query to be translated to the following query:
SELECT id, name
FROM persons
WHERE '{"dance", "acting", "games"}' && (hobbies);
That condition work in that way:
'{"dance"}' && '{"dance", "acting", "games"}'; -- true
'{"dance","singins"}' && '{"dance", "acting", "games"}'; -- true
'{"singins"}' && '{"dance", "acting", "games"}'; -- false
I wrote this query:
List<string> arr = new List<string>(){ "dance", "acting", "games" };
var query = (from p in _context.Persons
where arr.Any(kw => p.hobbies.Contains(kw))
select new
{
id = p.id,
name = p.name
}).ToList();
The translated query is:
SELECT p."id" AS id, p."name" AS name
FROM dataBase."Persons" AS p
It can understand that the filter performs in the server. so the query brings all the data from DB and filtered on the server. This causes to performance problems and not pass Load-Testing.
I need a query that will not only do the job but will also be translated to the above query with '&&'.
Is there any way in LINQ to execute this query?
Thanks
If your database looks similar like I am predicting this could be a solution. Some more code though but I think this should be executed as a SQL IN statement:
List<string> arr = new List<string>(){ "dance", "acting", "games" };
var matches = from hobby_person in _context.Hobbies_Persons
join person in _context.Persons on person.Id equals hobby_person.PersonId
join hobby in _context.Hobbies on hobby.Id equals hobby_person.HobbyId
where arr.Contains(hobby.Name)
select new
{
id = p.id,
name = p.name
}).ToList();

I have a issue in Loopback

I have the following SQL:
SELECT *
FROM table1 INNER JOIN table2 ON table1.id = table2.id_table1
WHERE table2.column_name = 'value';
I have tried
{include:'table2',where:{'table2.column':'value'}}
but can't. What should I do?
this works for me
include: {
relation: 'table2',
scope: {
fields: ['fields', 'you', 'want'],
where: {
column: 'value'
}
}
}
in case it's not working, you need to make sure you have right relations in your table1.json and table2.json file
loopback does not support inner join it only support left join you can use raw query to achieve your requirement try something like this
yourmodalname.customremotemethod= function(ctx,options, cb) {
const ds = yourmodalname.dataSource
var query ="SELECT * FROM table1 INNER JOIN table2 ON table1.id =table2.id_table1 WHERE table2.column_name = 'value'";
ds.connector.query(query, function(err, res){
if(err){
cb(null,err)
}else{
cb(null,res)
}
});
};

Nested Query Entity Framework

Need help in converting below SQL nested query to a LINQ query?
select P.ProductId, P.Name, C.Name, I.Image
from Product P
join ProductImage I on P.ProductId = I.ProductId
join ProductCategory C on P.Category = C.CategoryId
where P.ProductId in (select distinct ProductId
from ProductVariantMapping M
where M.GUID in (select top 3 V.Guid
from [Order] O
join Inventory V on V.InventoryId = O.InventoryId
group by O.InventoryId, V.Guid
order by Sum(O.Quantity) desc))
Below is my attempt in converting to LINQ query :
var a = (from product in ekartEntities.Products
join productImage in ekartEntities.ProductImages
on product.ProductId equals productImage.ProductId
join category in ekartEntities.ProductCategories
on product.Category equals category.CategoryId
where product.ProductId
select new ProductDTO()
{
ProductId = product.ProductId,
Name = product.Name,
Category = category.Name,
Image = productImage.Image
}).ToList();
what is the equivalent of "IN" when converting to LINQ .
I got the solution for 'IN' clause.
But how do I use sum(Quantity) in order by after grouping?
I am new to Entity Framework. Can anyone help me?
In LINQ, you will need to use the "contains()" method to generate the 'IN' You need to put a list in the Contains method. If sends a query, that query will be repeated for completions and this will lead to performance loss.
Sample:
var sampleList = (from order ekartEntities.Order
join inventory in ekartEntities.Inventory on order.InventoryId equals inventory.InventoryId
select order).toList();
var query = (from product in ekartEntities.Products
join productImage in ekartEntities.ProductImages
on product.ProductId equals productImage.ProductId
join category in ekartEntities.ProductCategories
on product.Category equals category.CategoryId
where sampleList.Contains(product.ProductId)
select new ProductDTO()
{
ProductId = product.ProductId,
Name = product.Name,
Category = category.Name,
Image = productImage.Image
}).ToList();
Do not apply ToList() in the first query
I made some test.
Test 1 (with my own data):
var phIds = new List<string>
{
//List of Ids
};
using (var db = new ApplicationDbContext())
{
var studentsId = db.Relations
.Where(x => phIds.Contains(x.RelationId))
.Select(x => x.Id)
.Distinct(); //IQueryable here
var studentsQuery = from p in db.Students
where studentsId.Contains(p.Id)
select p;
var students= studentsQuery .ToList();
}
The generated query looks like :
SELECT
[Extent1].[Id] AS [Id],
[...]
FROM [dbo].[Students] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM ( SELECT DISTINCT
[Extent2].[StudentId] AS [StudentId]
FROM [dbo].[Relations] AS [Extent2]
WHERE ([Extent2].[RelationId] IN (N'ccd31c3d-dfa3-4b40-...', N'd2cb05a2-ece3-4060-...'))
) AS [Distinct1]
WHERE [Distinct1].[StudentId] = [Extent1].[Id]
)
The query looks exactly like I wanted
However, if you add the ToList() in the first query to get the ids, you no longer have an IQueryable but a list.
Test 2 : wrong (I added ToList):
var phIds = new List<string>
{
//List of Ids
};
using (var db = new ApplicationDbContext())
{
var studentsId = db.Relations
.Where(x => phIds.Contains(x.RelationId))
.Select(x => x.Id)
.Distinct().ToList(); // No longer IQueryable but a list of 3000 int
var studentsQuery = from p in db.Students
where studentsId .Contains(p.Id)
select p;
var students= studentsQuery .ToList();
}
The generated query is ugly:
SELECT
[Extent1].[Id] AS [Id],
[...]
FROM [dbo].[Patients] AS [Extent1]
WHERE [Extent1].[Id] IN (611661, 611662, 611663, 611664,....
//more than 3000 ids here
)

JPQL SELECT ElementCollection

I have an entity "Post" with this property:
#ElementCollection
#CollectionTable(name ="tags")
private List<String> tags = new ArrayList<>();
Then i have a native select query with a group by. The problem is now how can i select the property tags?
My select query:
Query query = em.createQuery("SELECT p.id,MAX(p.createdAt),MAX(p.value) FROM Post p JOIN p.tags t WHERE t IN (?1,?2,?3) GROUP BY p.id ORDER BY COUNT(p) DESC");
/* ...
query.setFirstResult(startIndex);
query.setMaxResults(maxResults);
List<Object[]> results = query.getResultList();
List<Post> posts = new ArrayList<>();
for (Object[] result : results) {
Post newPost = new Post();
newPost.setId(((Number) result[0]).longValue());
newPost.setCreatedAt((Date) result[1]);
newPost.setValue((String) result[2]);
posts.add(newPost);
}
return posts;
how to select the property tags?
Don't know if it will help, but in JPA2.1 Spec, part 4.4.6 Collection Member Declarations, you can do that:
SELECT DISTINCT o
FROM Order o, IN(o.lineItems) l
WHERE l.product.productType = ‘office_supplies’
So I guess with your case, you could try:
SELECT p.id,MAX(p.createdAt),MAX(p.value), t
FROM Post p, IN(p.tags) t
WHERE t IN (?1,?2,?3)
GROUP BY p.id, t
ORDER BY COUNT(p) DESC
Note: I added t to the GROUP BY, since it would not work with the query without using an aggregate function.

joining a table with more than one field in same table

I try to create a join query in Linq. I want to join a table more than one field with same
table. Please see my code below.
var roles = (from ords in _orderRepository.Table
join customers in _customerRepository.Table on ords.CustomerId equals customers.Id
join ordprvrnts in _orderProductVariantRepository.Table on ords.Id equals ordprvrnts.OrderId
join prdvrnts in _productVariantRepository .Table on ordprvrnts.ProductVariantId equals prdvrnts.Id
**join cstevntrle in _customerEventRoleRepository.Table on
new{ customers.Id equals cstevntrle.CustomerId } &&
new { cstevntrle.EventId == model.Event}**
orderby customers.Email ascending
select new CustomerEventRolesModel
{
Customer = customers.Email,
CUstomerId =customers.Id
});
I try to filter customerEventRoleRepository.Table with CustomerId and EventId
how can i do this in this join query.
Please Help.
you have boolean comparisons in your anonymous type definitions...
change your on clause to the following:
join cstevntrle in _customerEventRoleRepository.Table on
new { CustomerId = customers.Id, EventId = model.Event.EventId } equals
new { CustomerId = cstevntrle.CustomerId, EventId = cstevntrle.EventId }
I don't see "model" defined anywhere, so I'm not sure this is going to work, but it should be enough to demonstrate how joins based on multiple fields works - each anonymous class contains the fields from one "side" of the join.