Combining joins with other joins or regular queries - join

I'd like to combine Solr join queries with regular queries. As an example, suppose I want to find all stores in Jyväskylä (Finland) selling guide books. If documents for stores have the fields city and productIds and documents for products have the fields productType and productId in my index, I'd expect something like this to work:
{!join from=productIds to=productId}productType:"guide book" city:Jyväskylä
However, join queries are a particular kind of LocalParams and those are effective for the entire query. Therefore, this query would select documents that have productType=guide book and city=Jyväskylä, which doesn't make any sense.
Worse, suppose I want to look for stores that carry guide books and are located in cities with a population over 1000 people. I'd need two joins for that (to select products and cities).
Of course, I can split this into a query (q) and a filter query (fq), but that limits me to two kinds of queries (so, either one regular query and one join query or two joins), and more importantly abuses the concept of queries and filter queries.
My question therefore: how can I combine regular and join queries and how can I have multiple join queries?

I think I've got it: there can be more than one filter query and each can have its own join clause. Solr's admin interface won't let you enter more than one query in the fq field, but it's possible through the "Raw Query Parameters" field.
I'm not sure how efficient this is, though.

Related

How to order by optional association with ActiveRecord

Scenario:
Pupils have many grade progressions.
A grade progression belongs to a polymorphic progressable (either a subject or topic), and a score (decimal).
Pupils may not yet have a grade progression recorded for
all progressables.
I need:
A list of Pupils ordered by grade score (highest first) for any given progressable.
Pupils without grades for the progressable should appear last.
If a Pupil has multiple grades for the progressable then the most recent should be used.
I've tried
Pupil.left_joins(:grade_progressions).where(grade_progressions: { progressable: progressable })
.order('grade_progressions.score DESC')
however, this does not work because it excludes any pupils without a grade for the progressable.
With raw SQL this should be possible by specifying the condition on the LEFT JOIN however I can't find a way to do this with ActiveRecord object syntax. I'd like to avoid specifying the join as a string if possible.
How can I author an ActiveRecord query to order by an optional polymorphic association in this way?
Rails 5 has a native .left_outer_joins method. Previously you needed to specify the join as a string or use .includes which often has unintended results.
An OUTER JOIN unlike the SQL default INNER JOIN includes rows that have no matches in the joined table.

ActiveRecord 4.2 includes and ordering by count

I'm writing a search for a project I'm working on. It is meant to be able to search the body of articles and produce a list of their authors, ordered by the number of matching articles and including the relevant articles only, not all of their articles.
I currently have the following query:
Author.includes(:articles).where('articles.body ilike ?', '%foo%').references(:articles)
The use of includes in this case makes it so that all the relevant articles (not all articles) are preloaded, that's exactly what I want. However, when it comes to ordering by the number of included articles, I'm not sure how to proceed.
I should note I want to do this in ActiveRecord because pagination will be applied after the query. Not after a Ruby solution.
I should note I'm using PostgreSQL 9.3.
Edit: using raw SQL
This seems to work on its own like so:
Author.includes(:articles).where('articles.body ilike ?', '%foo%').references(:articles).select('authors.*, (SELECT COUNT(0) FROM articles WHERE articles.author_id = authors.id) AS article_count').order('article_count DESC')
This works fine. However, if I add .limit(1) it breaks.
PG::UndefinedColumn: ERROR: column "article_count" does not exist
Any idea why adding limit breaks it? The query seems very different too
SELECT DISTINCT "authors"."id", article_count AS alias_0 FROM "authors" LEFT OUTER JOIN "articles" ON "articles"."author_id" = "authors"."id" WHERE (articles.body ilike '%microsoft%') ORDER BY article_count DESC LIMIT 1
I don't think there's an out of the box solution for this. You have to write raw sql to do this but you can combine it with existing ActiveRecord queries.
Author
.includes(:articles)
.select('authors.*, (SELECT COUNT(0) FROM articles WHERE articles.author_id = authors.id) AS article_count')
.order('article_count DESC')
So the only thing to explain here is the select part. The first part, authors.*, selects all fields under the authors table and this is the default. Since we want to also count the number of articles, we create a subquery and pass its result as one of the pseudo columns of authors (we called it article_count). The last part is to just call order using article_count.
This solution assumes a couple of things which you'll have to fine tune depending on your setup.
Author by convention in rails maps to an authors table. If it is an STI (inherits from a User class and is using users table), you'll need to change authors to users.
articles.author_id assumes that the foreign key is author_id (and essentially, an article is only written by a single author). Change to whatever the foreign key is.
So given that, you'll have an array of authors ordered by the number of articles they've written.

Can I Order Results Using Two Columns from Different Tables?

I have a Rails application featuring a city in the US. I'm working on a database process that will feature businesses that pay to be on the website. The goal is to feature businesses within an hour's drive of the city's location in order to make visitors aware of what is available. My goal is to group the businesses by city where the businesses in the city are listed first then the businesses from the next closest city are displayed. I want the cities to be listed by distance and the businesses within the city group to be listed by the business name.
I have two tables that I want to join in order to accomplish this.
city (has_many :businesses) - name, distance
business (belongs_to :city) - name, city_id, other columns
I know I can do something like the statement below that should only show data where business rows exist for a city row.
#businesses = City.order(“distance ASC").joins('JOIN businesses ON businesses.city_id = cities.id')
I would like to add order by businesses.name. I've seen an example ORDER BY a.Date, p.title which referencing columns from two databases.
ORDER BY a.Date, p.title
Can I add code to my existing statement to order businesses by name or will I have to embed SQL code to do this? I have seen examples with other databases doing this but either the answer is not Rails specific or not using PostgreSQL.
After lots more research I was finally able to get this working the way I wanted to.
Using .joins(:businesses) did not yield anything because it only included the columns for City aka BusinessCity and no columns for Business. I found that you have to use .pluck or .select to get access to the columns from the table you are joining. This is something I did not want to do because I foresee more columns being added in the future.
I ended up making Business the main table instead of BusinessCity as my starting point since I was listing data from Business on my view as stated in my initial question. When I did this I could not use the .joins(:business_cities) clause because it said the relation did not exist. I decided to go back to what I had originally started with using Business as the main table.
I came up with the following statement that provides all the columns from both tables ordered by distance on the BusinessCity table and name on the Business table. I was successful in added .where clauses as needed to accommodate the search functionality on my view.
#businesses = Business.joins("JOIN business_cities ON business_cities.id = businesses.business_city_id").order("business_cities.distance, businesses.name")

Rails : get fields from join tables in one single query

Starting with rails, i want to create a request with dynamic selection and dynamic sorting, like following examples (in SQL):
select * from books join authors on author_id = books.id
where books.title like '%something%'
order by author.name, books.title
or
select * from books join authors on author_id = books.id
where books.title like '%something%'
order by books.title, author.name
Author has_many books, book belongs to author.
I code this with two nested loops. In the first case, Author (sorted by name) is read first then Book (sorted by title), in the second case, Book first then author.
I can then print together books fields and authors fields.
The loops must reflect the hierarchy of sort fields.
But many other fields exist, and dynamic selection/ordering may be any field(s).
Is there a way to write a single 'each' loop, where books fields and authors fields would be available together, like with above sql examples.
My problem is to get fields from several tables on one single line.
What would the 'find' request be?
Thanks for your help.
Your basic query would be something like:
#books = Book.where("title LIKE ?", "%{something}%").joins(:author).order("author.name ASC, books.title ASC")
As for controlling the sorting, you can break that into scopes that get conditionally added depending on your params.

Neo4j - Searching from an array of nodes

In my situation I've a bunch of nodes that represent the users
and they have relation to books they read.
This user have a property that says where they are from, and I added them to an index, based on their country.
So I would like to search in the index for users from one country, and list the books that people from there read more, some sorting by grouping.
could any one give me a help how to do this?
I'm having some trouble getting users from the index, and doing the query
Couple of assumptions based on your descriptions:
users have a country property, it contains e.g. France as value
you have a index called users and you store the user node's country property there
relationship type to connect users and books is READ
book nodes have a title property
Based on these assumptions the cypher query would look like:
start user=node:users(country='France')
match user-[:READ]->book
return book.title, count(*) as rank
order by rank desc
limit 20
side note: best approach to ask this kind of questions is to create a sample graph on http://console.neo4j.org and share your setup on SO.

Resources