Sequelize js get count of associated model - sql

Say for example I have a discussions table and replies table.
How do I get the count of replies for each discussion record ?
Models.course_discussions.count({
where: { 'course_id': 105 },
include: [
{model: Models.course_discussions_replies, as: 'replies'}
]
})
.then(function (discussions) {
if (!discussions) {
reply(Hapi.error.notFound());
} else {
console.log("91283901230912830812 " , discussions);
}
});
The above code is converted into the following query -
SELECT COUNT(`course_discussions`.`id`) as `count` FROM `course_discussions` LEFT OUTER JOIN `course_discussions_replies` AS `replies` ON `course_discussions`.`id` = `replies`.`discussion_id` WHERE `course_discussions`.`course_id`=105;
The above code gets me count of discussions. But how do I get the count of the replies for each discussion ?
The following sql query works, but how do I write it in the sequelize way ?
SELECT COUNT( `replies`.`discussion_id` ) AS `count`
FROM `course_discussions`
LEFT OUTER JOIN `course_discussions_replies` AS `replies` ON `course_discussions`.`id` = `replies`.`discussion_id`
WHERE `course_discussions`.`course_id` =105

If you need the discussions and the replies count maybe .replies.length should work for you.
Models.course_discussions.findAll(
where: { 'course_id': 105 },
include: [
{model: Models.course_discussions_replies, as: 'replies'}
]
})
.then(function (discussions) {
// each discussion in discussions will have a
// replies array
discussions[0].repies.length // replies for current discussion
});

Related

Get Article list by subscription

I am trying to make a filter with which I could get subscription records
Entity 'Subscription'
export class Subscription {
#PrimaryColumn()
id: string;
#Column('uuid')
userId: string;
#Column('uuid')
targetUserId: string;
#CreateDateColumn()
createdAt: Date;
}
Filter
applyFilter(query: QueryArticle, qb: SelectQueryBuilder<Article>, userId?: string) {
if (query.filter) {
switch (query.filter) {
....
case 'subscriptions':
qb.select(
`article.authorId WHERE targetUserId IN (SELECT targetUserId FROM Subscription WHERE userId=${userId})`,
);
break;
}
}
return qb;
}
SQL code
Select * FROM article WHERE authorId=targetUserId IN (SELECT targetUserId FROM Subscription WHERE userId=userId)
Error
syntax error at or near "f5779e5" +3974ms
QueryFailedError: syntax error at or near "f5779e5"
How can I get all the posts of people followed by a person use TypeORM?
Thanks in advance for your answer!
DO NOT DO WHAT YOU ARE DOING. You are risking a SQL Injection. If you really want to do a manual query, you can do manager.query:
const output = manager.query('article."authorId" WHERE "targetUserId" IN (SELECT "targetUserId" FROM Subscription WHERE "userId" = :userId)',
{ userId: userId }
);
Notice the second parameter that contains parameters which is referenced by key with :userId. If you're using template strings for queries, you're probably doing something wrong.
If you want to use the QueryBuilder, then it's going to look a little different (more info on QueryBuilder here)
const output = articleRepo.createQueryBuilder('article')
.select('article.authorId')
.where('article."authorId" IN (SELECT "targetUserId" FROM subscription WHERE "userId" = :userId)',
{ userId: userId }
)
.getRawMany(); // If you remove the .select('article.authorId'), you can use .getMany()

Is this SQL possible in Sequelize?

I've just started with sequelize and am trying to reproduce the below query.
I have the following Model structure: Review, Entity, ReviewThank
Each Entity can have many Reviews, and each Review can have many ReviewThanks.
An attribute of each review is a 'thumbUp' (boolean) rating.
I'm trying to generate the below query to get a 'thankCount' for each review, along with the Entity rating - thumbUpCount and totalCount - for each Review:
SELECT * FROM (
SELECT COUNT("Review"."id") AS "totalCount", "Review"."EntityId", COUNT(CASE WHEN "Review"."thumbUp" THEN 1 END) AS "thumbUpCount"
FROM "Reviews" AS "Review" GROUP BY "Review"."EntityId"
) AS "EntityRatingTable" LEFT JOIN (
SELECT "Review"."id", "Review"."EntityId", "Review"."uid", "Review"."thumbUp", "Review"."caption", COUNT("ReviewThanks"."id") AS "thankCount"
FROM "Reviews" AS "Review" LEFT OUTER JOIN "ReviewThanks" AS "ReviewThanks" ON "Review"."id" = "ReviewThanks"."ReviewId"
WHERE "Review"."UserId" IN (1) GROUP BY "Review"."id"
) AS "ReviewsTable" ON "ReviewsTable"."EntityId" = "EntityRatingTable"."EntityId";
Is it possible to produce this in sequelize? I've got the "ReviewsTable" query working ok, but unsure how (or if possible) I can join this with the "EntityRatingTable"?
This is what I've got so far:
models.Review.findAll({
attributes: {
include: [
[models.sequelize.fn('COUNT', models.sequelize.col('ReviewThanks.id')), 'thankCount'],
[models.sequelize.fn('COUNT', models.sequelize.col('Review.id')), 'reviewCount'],
],
exclude: ["EntityId", "UserId"],
},
include: [
{
model: models.ReviewThank,
attributes: [],
}, {
model: models.Entity,
}
],
group: ['"Review"."id"', '"Entity.id"'],
})

find using model association

Dialect: postgres
Database version: #latest
Sequelize version: #latest
I'm trying to find out how to use an associate model. I've got 3 models: post, postCity and region. They have the following relation:
postCity (post_id, region_id) associate to post (post_id) and region (region_id). I am using a search function like this:
include: [
{
model: models.postCity,
include:[{model:models.region}],
attributes: [[models.sequelize.fn('count', 'post_id'), 'count']],
}
],
where: {
$or: [
{
"create_by" : {$not: 67}
},
{
// "postCities.region_name":{$iLike: "%Guangazhou2%"}
},
{
"description":{$iLike: "%India%"}
}
]
}
which leads to:
SELECT "post"."post_id", "post"."description", "post"."create_by",
"post"."create_time", "post"."update_time", "post"."country_id",
"postCities"."post_id" AS "postCities.post_id",
"postCities"."region_id" AS "postCities.region_id",
"postCities"."order_no" AS "postCities.order_no",
"postCities.region"."region_id" AS "postCities.region.region_id",
"postCities.region"."region_name" AS "postCities.region.region_name",
"postCities.region"."country_id" AS "postCities.region.country_id",
"postCities.region"."province_id" AS "postCities.region.province_id"
FROM "t_post" AS "post"
LEFT OUTER JOIN "t_post_city" AS "postCities"
ON "post"."post_id" = "postCities"."post_id"
LEFT OUTER JOIN "t_region" AS "postCities.region"
ON "postCities"."region_id" = "postCities.region"."region_id"
WHERE ("post"."create_by" != 67 OR "post"."description" ILIKE '%India%');
When I uncomment "postCities.region_name":{$iLike: "%Guangazhou2%"} then I get this error
column post.postCities.region_name does not exist
I simply like to my query to be like this
... WHERE ("post"."create_by" != 67
OR "post"."description" ILIKE '%India%'
OR "postCities.region_name" ILIKE: "%Guangazhou2%")
Update
I also tried to include [{model:models.region, where:{"region_name":{$iLike: "%Guangazhou2%"}}}] but this doesn't give me the appropriate result.
In order to add condition to included tables, you should wrap condition with $ symbol, like it:
include: [{
model: models.postCity,
include:[{model:models.region}],
attributes: [[models.sequelize.fn('count', 'post_id'), 'count']],
}],
where: {
$or: [{
"create_by" : {$not: 67}
}, {
"$postCities.region.region_name$":{$iLike: "%Guangazhou2%"}
}, {
"description":{$iLike: "%India%"}
}]
}

Missing left join when using bookshelf/knex

for some reason, in some cases knex doesn't add left join to the query. I tried to reproduce the bug with minimal code, So I tried to use knex with in-memory sqlite3.
var knex = require('knex')({
client:'sqlite3',
connection: {
filename: ":memory:"
},
debug: true
});
var bookshelf = require('bookshelf')(knex);
var Conversation = bookshelf.Model.extend({
tableName: 'conversations',
});
Conversation
.where(qb => {
qb
.leftJoin('conversations_recipients', function () {
this.on('conversations_recipients.conversationId', 'conversations.id');
});
})
.fetch();
When I check the debug messages on the console, I get:
{ method: 'select',
options: {},
bindings: [ 1 ],
sql: 'select "conversations".* from "conversations" limit ?' }
And there's the left join is missing. Someone knows what's wrong with this code, and how can I include the desired join?
Thanks in advance.
You're calling where instead of query. Also you don't need to use the join callback unless you're doing complex join logic. Refer to knex documentation for where and join. And Bookshelf documentation for query
Conversation.query(qb =>
qb.leftJoin(
'conversations_recipients',
'conversations_recipients.conversationId',
'conversations.id'
);
).fetch().then(conversations => { // ...

Counting cascaded distincts in RavenDB

I'm facing an index problem for which I can't see a solution yet.
I have the following document structure per board:
{
"Name": "Test Board",
...
"Settings": {
"Admins": [ "USER1", "USER2" ],
"Members": [ "USER3", "USER4", "USER5" ]
...
},
...
"CreatedBy": "USER1",
"CreatedOn": "2014-09-26T18:14:20.0858945"
...
}
Now I'd like to be able to retrieve the count of all users which are somewhere registered in a board. Of course this should not only count the number of user occurences but rather count the number of distinct users. One user can be member of multiple boards.
This operation should perform as fast as possible since it is displayed in a global statistics dashboard visible on each page. Therefor I chose to try it with an index instead of retrieving all boards and their users and do the work on client side.
Trying to achieve this by using a Map/Reduce index:
Map = boards => from board in boards
select new
{
Aggregation = "ALL",
Users = new object[]
{
board.CreatedBy,
board.Settings.Admins,
board.Settings.Members
},
NumberOfUsers = 1
};
Reduce = results => from res in results
group res by new
{
res.Aggregation
}
into g
select new
{
g.Key.Aggregation,
Users = g.Select(x => x.Users),
NumberOfUsers = g.Sum(x => x.Users.Length)
};
Obviously this results in a wrong count. I don't have any experience with Reduce yet so I appreciate any tip! The solution will be probably pretty easy...
What would be the best way to globally distinct CreatedBy, Admins and Members of all documents and return the count?
Use an index like this:
from board in docs.Boards
select new
{
Users = board.Settings.Admins.Count + board.Settings.Members.Count + 1 /* created by */
}
from r in results
group r by "all" into g
select new
{
Users = g.Sum(x=>x.Users)
}
The best I could come up so far is:
Map = boards => from board in boards
select new
{
Users = new object[]
{
board.CreatedBy,
board.Settings.Admins,
board.Settings.Members
}
};
Reduce = results => from r in results
group r by "all" into g
select new
{
Users = g.SelectMany(x => x.Users)
};
And then query for the distinct user count:
var allUsersQuery = _documentSession.Query<AllUsersIndex.Result, AllUsersIndex>();
return allUsersQuery.Any() ? allUsersQuery.First().Users.Distinct().Count() : 0;
At least the query only returns a list of all usernames on all boards instead of bigger object trees. But the uniqueness still has to be done client-side.
If there is any better way please let me know. It would be beautiful to have only one integer returned from the server...
Then use this:
from board in docs.Boards
from user in board.Settings.Admins.Concat(board.Settings.Members).Concat(new[]{board.CreatedBy})
select new
{
User = user,
Count = 1
}
from r in results
group r by r.User into g
select new
{
User = g.Key,
Count = g.Sum(x=>x.Count)
}
I'm not really happy about the fanout, but this will give you all the discint users and the number of times they appear.
If you want just the number of distinct users, just get the total results from the index.