rails 3.1 complex find_by_sql query on nested has_many through models - postgresql

My simplified rails app looks like this
# chapter.rb
has_many :sections
has_many :videos, through: :sections
# section.rb
belongs_to :chapter
has_many :videos
# video.rb
belongs_to :section
has_many :votes
# vote.rb
belongs_to :video
What I want to do is find the top 5 currently popular videos for a given chapter, which would mean ordering the videos by the number of votes it has received within the last month (and limiting to 5 results, obviously).
I wanted to write a popular_videos method in my Chapter model, and I think this requires a find_by_sql query, right? But I don't know enough sql to write this query.
The votes table has a created_at column, and I'm using postgres for my database. Any help is greatly appreciated.

I don't know anything about rails, but guessing at the table structures from what's in the question, I would think SQL like the following would be one way to provide what you want:
SELECT video.id, video.name, count(*) as vote_count
FROM video JOIN vote ON (video.id = vote.video_id)
WHERE vote.date > (current_date - interval '1 month')
GROUP BY video.id, video.name
ORDER BY vote_count DESC
LIMIT 5;
If you're running PostgreSQL 9.1 or later, you can probably omit video.name from the GROUP BY list.

Related

How to implement GraphQL Relay style cursor based pagination in postgres (with sqlalchemy and fastAPI)

I am trying to implement Relay-Style pagination on a postgres. I have gone through the relay specification, and it recommends having a globally unique cursor for each entity. The specification for pagination is that the client provides first and after for Forward pagination and last and before for Backward pagination.
Assume I have a table called Products, with an auto incrementing primary key integer ID. In the most basic case, pagination is very straight forward. For forward pagination it would be something like SELECT * FROM products WHERE id > cursor LIMIT 10 for forward pagination, and SELECT * FROM products WHERE id < cursor ORDER BY id DESC LIMIT 10
The problem arises when we have additional sorting requirements. Let us say, our product table has a name field on which we want to sort.
Name Id (cursor field)
Trendy 1
Autumn 2
Winter 3
Crazy 4
Antman 5
Marvel 6
And we want to paginate, with page size of 3, sorted on name ASC. The query would be:
SELECT * FROM products ORDER BY name ASC LIMIT 3, And we would get the following output:
(Antman, 5), (Autumn 1), (Crazy 4)
But now, how will I get the next three? This query SELECT * FROM products WHERE id >= 4 ORDER BY name ASC LIMIT 3; would not work, as Trendy has ID < 1. Do I create a different cursor for Name and use that for paginating with Name? Okay, what if we wanted to sort on multiple fields together (let us say we have a price field, we want to sort from decreasing to increasing price, and then sort based on created time, and then on name. The price might not be unique and any other additional constraints), how would I create a cursor then?
What is the standard approach to solve this problem? I have tried to research as much as I can but I couldn't find any information on how to accomplish this in fastapi and sqlalchemy. Django has FilterableConnectionField, but I couldn't understand the implementation. Any help is very appreciated, thank you.
edit: I figured out how to do this. It is called queryset pagination, and sqlakeyset repo on github had a good implementation of this pattern. https://github.com/djrobstep/sqlakeyset

Searchkick, how to use aggregations on a join table

I'm a searchkick newbie.
I read the docs and I understand how to use aggregations, and I also watch some video.
Every tutorial I read uses aggregation on attributes on the main indexed model.
By the way I understand that I can use also aggregation on associations.
But my situation is different.
I have a complex situation
class Product < ProductDatabase::Base
searchkick
has_many :products_features
end
class ProductFeature < ProductDatabase::Base
belongs_to :product
belongs_to :feature
end
class Feature < ProductDatabase::Base
has_many :products
end
ProductsFeatures is the join table between the features and the products, and in the join table I have the values I need in my aggregations...
t.string "raw_value"
t.string "presentional_value"
t.integer "product_id"
t.integer "feature_id"
t.boolean "searchable", default: false
Example:
For every category of products, for example, "smartphone", I need to add an aggregate search for the display size.
The smartphone display sizes for every product in my database is inside the ProductsFeatures table, with a specific feature_id (the one that corresponds to the display size).
I already create a search system with activerecord query and it works, but every time I need to manually add the part of the code I need to filter results.
I'm starting to learn ElasticSearch and SearchKick and I want to use a project that I already have...

How to perform Model Joins + Condition on Relations in Sails.js?

How can I make a model join query(condition) and sort on relation models on Sails?
Example: I have 4 tables(collections in mongodb) and 4 related models in mongodb:
User: user_id, name
Follow: user_id, following_id (user id is being followed)
Point: user_id, point
Post: name, content, user_id, created_at
So from the post table, I want to make a query to find the posts of users that I'm following and sort by their point. Like this raw sql:
SELECT post.* FROM post
LEFT JOIN user_point up ON up.user_id = post.user_id
WHERE post.user_id IN (1,2,3,4) // assume I got my following_user_ids result is 1,2,3,4 for this case so no need to join follow table
ORDER BY up.point DESC // high point then first return
I don't know how can do this by Sails model? I have read many instructions by got no helps. Almost people said: Sails Association, but it just helps return the relation instead of do the where or order by to sort original model results(is this case: post).
I have worked with Yii2, a PHP framework so with this case I can do it easily:
Post::model()->leftJoin('user_point up', 'up.user_id = post.user_id')->where(['post.user_id' => [1,2,3,4])->orderBy(['up.point' => SORT_DESC])->all();
I'm stucked in Sails, very thanks if someone help me!!!
Because you're using Mongo, and because you need the full power of normal JOIN's, you will probably be forced to use some other ORM solution (i.e. mongodb package on npm) for queries like that.
Why? See the API documentation for sendNativeQuery(), which states native query features are only available for SQL-like DBMS's.

Rails 4+: only select specific columns from an included association

I have a user model that belongs to to an account (pretty standard). On every request, I need to retrieve the current_user but I also want to return the account to reduce DB queries:
# ApplicationController
def current_user
#current_user ||= User.includes(:account).find_by(auth_token: cookies[:auth_token])
end
def current_account
#current_account ||= current_user.account
end
This works just fine. However, the account record has a lot of extra text data that I don't always want to return. A more permanent solution would be to move those extra fields to a new table called account_details and have a one-to-one between an account and account_details.
However, I need a shorter-term solution that will provide some optimizations until I can refactor that (perfection is an iterative process!).
I want to return ALL columns from the current_user, but I only want to select a few columns from the current_account.
I tried this and I didn't work:
#current_user ||= User.includes(:account).references(:account).select("users.*, account.id, account.uuid").find_by(auth_token: cookies[:auth_token])
When I look at #current_user.account it still displays all the columns with their values and I only wanted the id, uuid columns.
I couldn't find any clear syntax on using select(...) with includes(:association); is this something that's possible?
You need to add an extra association in your User model.
belongs_to :account_columns, -> { select(:id, :uuid) }, class_name: 'Accoun'
This association should be used in your query.
User.includes(:account_columns)
This is because Rails don't have the facility to pass the options for include query.
Also consider using join instead includes in your query, as it also caters your need.
User.joins(:accounts).select("accounts_ids, accounts.uuid")

Postgresql different where based on column value?

Is it possible to dynamically change the where clause as a query executes based on the value of one of the columns? That is, lets say (and this is a completely made up example) I have a table of students, classes attended, and if they were tardy. I want to see for each student a list of all classes they have attended since the last time they were tardy. So the query would look something like this:
SELECT student, class, classdate
FROM attendance
WHERE classdate>(<<SOME QUERY that selects the most recent tardy for each student>>)
ORDER BY student,classdate;
or, to put it into more programing terminology, to perhaps make it clearer:
for studentName in (SELECT distinct(student) FROM attendance):
SELECT student, class, classdate
FROM attendance
WHERE classdate>(SELECT classdate
FROM attendance
WHERE tardy=true AND student=studentName
ORDER BY classdate DESC
LIMIT 1)
ORDER BY classdate;
Is there any way to do this with a single query, or do I need to do a separate query for each student (essentially as per the loop above)? The actual use case is more complicated to explain (it has to do with certifying records, and which ones need to be looked at) but conceptually it is the same.
Just use multiple aliases (e.g. a1 and a2) for the attendance table, so you can refer to the "outer" table alias in the subquery:
SELECT student, class, classdate
FROM attendance a1
WHERE classdate>(SELECT classdate
FROM attendance a2
WHERE tardy=true AND a2.student=a1.student
ORDER BY classdate DESC
LIMIT 1)
ORDER BY classdate;