Rails combine multiple activerecord relations - ruby

I want to union multiple active record relation
For example,
apple_companies = Company.where("name like ?","%apple%")
banana_companies = Company.where("name like ?","%banana%")
I want to combine these two relation.
not merge, merge is
apple_companies.merge(banana_companies)
=> Company.where("name like ? and name like ?", "%apple%","%banana%")
I want to
Company.where("name like ? or name like ?", "%apple%","%banana%")
afterward,
I will code
companies = Company.none
company_name_list.each do |name|
like = "%"+name+"%"
companies += Company.where("name like ?",like)
end
but code which I wrote make companies to array.....
So I cannot order and page to companies... :(
thank you

apple_companies = Company.where("name like ?","%apple%")
banana_companies = Company.where("name like ?","%banana%")
apples = apple_companies.where_values.reduce(:and)
bananas = banana_companies.where_values.reduce(:and)
Company.where(apples.or(bananas))
See ActiveRecord Arel OR condition for more examples.

The best result for this that I've come across is to grab and merge the ids of the two queries and then search for them like this:
apple_companies = Company.where("name like ?","%apple%").pluck(:id)
banana_companies = Company.where("name like ?","%banana%").pluck(:id)
ids = apple_companies + banana_companies.uniq
Company.where(id: ids)
It's four lines that seems like it should be doable in one but it works.

In this case you can use any of the other answers. However in more general cases, I strongly recommend using any_of gem. With this gem you can do:
apple_companies = Company.where("name like ?","%apple%")
banana_companies = Company.where("name like ?","%banana%")
Company.where.any_of(apple_companies, banana_companies)
There already is a pull request to add this functionality to future rails releases.

Please try using
Company.where('name LIKE ? OR name LIKE ?','%apple%', '%banana%')
OR
Company.where('name IN (?)', ['%apple%', '%banana%'])
According to your code:
names = []
company_name_list.each do |name|
names << "%"+name+"%"
end
Then you can do:
companies = Company.where('name LIKE ANY(Array[?])', names)

Late answer, but Arel will solve it.
Company.where(Company.arel_table[:name].matches_any(["%apple%", "%banana%"]))

Use
Company.where(name: IN ('%apple%','%banana%'))

Related

Common part in associations rails, should I use merge?

Update 2
Cama::PostType.first.posts.joins(:custom_field_values)
.where("cama_custom_fields_relationships.custom_field_slug = ? AND
cama_custom_fields_relationships.value LIKE ?","localization",
"%Paris%").merge(Cama::PostType.first.posts.joins(:custom_field_values)
.where("cama_custom_fields_relationships.custom_field_slug = ? AND cama_custom_fields_relationships.value = ?","type-localization", "2"))
Why this merge doesn't work ?
It returns me same result when executed seperately... Merge should work as intersection so common part should be result. I dont get it
Update
I will try to ask in more conceptual way.
I have model B that have slug:text, value:text, belongs_to: Model A
I have model A that have name:string, has_many: Model B
#posts_one = I search for model B where slug="something", value = "city"
#posts_two = I search for model B where slug="mood", value="good"
I have 2 results based on diffrent parameters. Both belongs_to: model A
Now I want to return only the common belongs_to.
so if
#posts_one will return me 20 results with model_a_ids
#posts_two will return me 20 results with model_a_ids
I want to return only common model_a_ids of those 2 queries and right away to find posts. I try to make it in one query but dont know if its possible
Oryginal post
I use Camaleon CMS and I try to create filters based on additional "custom fields". I think to answer this question you dont have to know this cms.
I want to find common part of 2 queries or make it in one query(that would be the best)
I have
#posts = Cama::PostType.first.posts.includes(:custom_field_values)
#param_localization = "Paris"
#param_type_localization = "House"
#posts_one = #posts.merge(CamaleonCms::CustomFieldsRelationship.
where("cama_custom_fields_relationships.custom_field_slug = ? AND
LOWER(cama_custom_fields_relationships.value) LIKE ?", "localization",
"%#{#param_localization}%"))
puts #posts_one.count => 2
#posts_two = #posts.merge(CamaleonCms::CustomFieldsRelationship.where(custom_field_slug:
"type-localization", value: #param_type_localization))
puts #posts_two.count => 2
Question is how can I merge it together or make it one query ? When I made it in one where clause it returns me 0 results since I need to find 2 diffrent custom fields relationships that has diffrent values and slugs but it have relations to posts throught :custom_fields_values, so I have to make 2 queries I guess(like I did). First I find customFieldRelationship with slug = localization and second with slug = type_localization and then I need to find common part
I tried to #result = #posts_one.merge(#posts_two) but I got no result then. I thought it will return me "common part" of association which means 2 results
How can I combine it to find me posts that fullfil both queries ?
Let me know if I explained my problem not well enought.
You'll want to combine it in SQL: (untested)
#posts_combined = #posts.merge(CamaleonCms::CustomFieldsRelationship.
where("(cama_custom_fields_relationships.custom_field_slug = ?
OR cama_custom_fields_relationships.custom_field_slug = 'type-localization')
AND LOWER(cama_custom_fields_relationships.value) LIKE ? ", "localization",
"%#{#param_localization}%"))

Ecto where like query acts like where ==

I'm trying to get an ecto like query working like this:
def find(searchterm) do
query = from c in Contact,
#where: fragment("? % ?", c.company_name, ^searchterm),
where: like(c.company_name, ^searchterm),
contacts = Repo.all(query)
{:ok, contacts}
end
In my table, I have a company_name "Asymptote". Using where: like/2 my query looks like this:
SELECT c0."id", c0."company_id", c0."company_name" FROM "contacts" AS c0 WHERE (c0."company_name" LIKE $1) ["Asym"] (1.0ms)
when the pg_trm search uncommented, it looks like this:
SELECT c0."id", c0."company_id", c0."company_name" FROM "contacts" AS c0 WHERE (c0."company_name" % $1) ["Asym"] (1.0ms)
As far as I can see, the queries look good, but there are no results. Since I added the index after adding "Asymptote" to the database, I expect that is why it isn't found in the pg_trm index, but why won't like/2 or ilike/2 work? When entering in the full name "Asymptote", I am able to find the record.
I faced some similar problem. Unfortunately I had no pg_trgm available. I used the LIKE as in:
from candidate in query,
where: like(candidate.first_name, ^("%#{text}%"))
This matched the text in any place of the candidate.first_name.
With the help of Mitchell Henke of Rokkincat, here's a way to get pg_trgm working with custom match percentages:
def find(searchterm) do
limit = 0.1
query = from c in Contact,
where: fragment("similarity(?, ?) > ?", c.company_name, ^searchterm, ^limit),
#where: like(c.company_name, ^searchterm),
contacts = Repo.all(query)
{:ok, contacts}
end
But I still can't get like/2 working. Also, I don't see where in Ecto source this function exists.

how can I use like query in ruby with sinatra?

Here's the query I tried:
#blogs = DB[:blogs].where(:title => params[:s_txt]).reverse_order(:id)
In this query, I'd like to find blogs in my database. I also need to create a query that gives users more results. How can I do this?
It is better to use dynamic parameters to avoid SQL injection:
#blogs = DB[:blogs].where("title LIKE ?", "%#{params[:s_txt]}%").reverse_order(:id)
or
#blogs = DB[:blogs].where("title LIKE :text", text: "%#{params[:s_txt]}%").reverse_order(:id)
You can easily add more parameters to this:
#blogs = DB[:blogs].where("title LIKE ? OR content LIKE ?", "%#{params[:s_txt]}%", "%#{params[:s_txt]}%").reverse_order(:id)
or
#blogs = DB[:blogs].where("title LIKE :text OR content LIKE :text", text: "%#{params[:s_txt]}%").reverse_order(:id)
I also had this problem and only found this solution
#blogs = DB[:blogs].where("title LIKE '%#{params[:s_txt]}%'").reverse_order(:id)
Curious if there are better ways..
Searching in two fields is repeating the LIKE and seperating by AND or OR
#blogs = DB[:blogs].where("title LIKE '%#{params[:s_txt]}%'
or comment LIKE '%#{params[:c_txt]}%'").reverse_order(:id)
or do it like this
#blogs = DB[:blogs].where("title||comment LIKE '%#{params[:s_txt]}%'").reverse_order(:id)
Tested in Sqlite and it works. One advise: in lage tables you 'd better drop the first % if possible because the database will do a full table scan otherwise without using indexes but you can find all that stuff in the sql questions.

Post.order method does not work with Sequel and Sinatra

I am trying to order my posts based on the created date ( newest to oldest ) so i do this :
#posts = Post.order('created_at DESC')
But the posts are not being affected by this. What can the problem be ?
Try something like this
#posts = Post.order(:created_at)
This way you order your data according to the created_at property
and if you want reversed order then
#posts = Post.order(:created_at).reverse

How to exclude an array of ids from query in Rails (using ActiveRecord)?

I would like to perform an ActiveRecord query that returns all records except those records that have certain ids. The ids I would like excluded are stored in an array. So:
ids_to_exclude = [1,2,3]
array_without_excluded_ids = Item. ???
I'm not sure how to complete the second line.
Background: What I've already tried:
I'm not sure background is necessary, but I've already tried various combinations of .find and .where. For example:
array_without_excluded_ids = Item.find(:all, :conditions => { "id not IN (?)", ids_to_exclude })
array_without_excluded_ids = Item.where( "items.id not IN ?", ids_to_exclude)
These fail. This tip might be on the right track, but I have not succeeded in adapting it. Any help would be greatly appreciated.
Rails 4 solution:
ids_to_exclude = [1,2,3]
array_without_excluded_ids = Item.where.not(id: ids_to_exclude)
This should work:
ids_to_exclude = [1,2,3]
items_table = Arel::Table.new(:items)
array_without_excluded_ids = Item.where(items_table[:id].not_in ids_to_exclude)
And it's fully object-oriented with no strings :-)
You can also use Squeel gem to accomplish such query.
Documentation of it, goes here
As nslocum wrote, the following works well:
Item.where.not(id: ids_to_exclude)
If your "ids to exclude" come from a query (here with an example condition), you can even take it a step further:
Item.where.not(id: Item.where(condition: true))
This is useful if you need to filter another model:
OtherModel.where.not(item_id: Item.where(condition: true))

Resources