Search using Sunspot:solr - ruby-on-rails

I have two models, named as Products and Variants, in which Variant model have association
with Products as a Product have many Variants. Variant model have field named as
"available_on" ... I want to implement search using two dates as check-in n checkout dates.
.
if variants for a product available for each date , check-in date to checkout date, result will map all those products and it is the result....
.
.
guide me how i should give conditions using Sunspot:solr
roughly my models are like this
product
{
product_id integer
has_many variants
}
variant
{
variant_id integer
available_on date
belongs_to product
}
check-in n checkout are the inputs for the search.

it will be easier to answer if you put your models in question but in general way if you have has_many relationship than you should index nested model
#variant model
searchable do
integer :product_id
time :check_in
time :check_out
end
if you need to index something from parent has_many model you can use :multiple=>true option in this way
#product model
def variant_ids
variants.collect(&:id)
end
searchable do
integer :variant_ids, :multiple=>true
...
end

Index each model, and return the variant/product you need from SOLR. If you're using Spree by the way, there's a gem for integrating w/ sunspot and spree. I just forked it: https://github.com/banane/spree_sunspot_search

Related

Rails association, nested forms and STI

Here are my simplified models
class Offer < ApplicationRecord
has_many :rooms
end
class Room < ApplicationRecord
belongs_to :offer
end
class Kitchen < Room
end
I'm using STI for Kitchen, cause it seemed the right way to express what I wanted to do (I'm probably wrong).
I want to be able to create an Offer with Rooms in it. I have 'regular' rooms that are instances of Room directly, and more specified rooms such as a Kitchen which can have an extra attribute.
I'm using cocoon to create nested form, it works great to create an offer and add regular rooms. But how can I add kitchens ?
Maybe it's an architecture issue more than an implementation issue. How would you manage to do something like this ?
One solution would be to add JSONB column data to your rooms table. Then you can use jsonb_accessor gem to elegantly manage STI.
I assume you have type column already so you can do something like:
Kitchen.create(offer_id: 1, name: "Great Kitchen", description: "It is great!")
Notice: description would be attribute of JSONB data column. In addition you can create indices for JSONB attributes and even do queries. From my experience, you want to keep JSONB as place to store additional info and less for heavy queries.
I hope this helps.

Rails Active Record Query - multiple values condition (AND instead of OR)

I am using the acts_as_taggable_on gem in a Rails 4.1 app. I am using a scope to search based on a tag or multiple tags. When multiple tags are given as the param I want the search to return only tagged items that contain ALL tags. Currently the scope I has returns tagged items that contain at least one of the tags (tag_a OR tag_b OR tag_c). I want a scope that instead is an AND condition (tag_a AND tag_b AND tag_c).
Example
document_a_tags = ['programming', 'ruby']
document_b_tags = ['programming', 'python']
class User < ActiveRecord::Base
has_many :documents, dependent: :destroy
scope :with_fields, proc { |fields|
if fields.reject!(&:empty?).present?
joins(documents: [{ taggings: :tag }]).where(tags: { slug: fields })
end
}
end
class Document < ActiveRecord::Base
acts_as_taggable
belongs_to :user
end
Currently, if the search params include programming and ruby both document_a and document_b will be returned. What I want is a scope where if the search params include programming and ruby only document_a will be returned.
Given the way mysql works I would think this is hard to accomplish in a single active record query.
The join adds one row for each tag that is connected to your entry and conditions are only applied to one row at the time. That is, a row can't be excluded by a condition saying that certain other rows in your result.
I think it might be possible to write some complex recursive query adding an undetermined number of columns to the result and applying the conditions on them. But I think it will be much easier and more performant to simply use your current scope and then in ruby code discard entries without all tags.
edit:
This might be done in the gem you are using. Look at:
User.tagged_with(["awesome", "cool"], :match_all => true)

Rails use Boolean similar to counter_cache?

A Miniatures model has many Collections. Users can have and vote for the best Collection version of a Miniature. The votes are in a model called Imagevotes which update a counter_cache attribute in the Collections model.
What I want to do is flag Collections which are ranked first for a Miniature as GOLD, then rank the 2nd, 3rd and 4th as SILVER. I realise I can do this on the Miniature model by selecting the #miniature.collection.first, but I would like to be able to store that like you would store the vote-count in a counter_cache so that I could display the total number of GOLDS or SILVERS for any one user.
Is there a way that each model could have Boolean fields called GOLD and SILVER which would be updated as new votes are cast in the same way that a counter_cache is updated?
Any pointers and further reading much appreciated.
Update:
It occurs to me that this could also be done with a sort of second index column. A vote_position column if you will, that updated with a number from "1" for the record with the highest counter_cache number and ascended from there. Then I could use #miniature.collection.where(:vote_position => "1") or similar. Perhaps this is more ideal?
As it seems for me you just need to implement method in Miniature model:
def set_gold_and_silver
top_collections = self.collections.order("imagevotes_count desc").limit(4)
gold = top_collections.shift
gold.update_attribute :is_gold, true if gold
top_collections.each {|s| s.update_attribute :is_silver, true}
end
after that you can add it to after_create filter of Imagevotes model:
class Imagevotes < ActiveRecord::Base
after_create :set_gold_and_silver
def set_gold_and_silver
self.miniature.set_gold_and_silver
end
end

Ruby on Rails - Counting goals of a team in many matches

I've got a Match model and a Team model.
I want to count how many goals a Team scores during the league (so I have to sum all the scores of that team, in both home_matches and away_matches).
How can I do that? What columns should I put into the matches and teams database tables?
I'd assume your Match model looks something like this:
belongs_to :home_team, class_name:"Team"
belongs_to :away_team, class_name:"Team"
attr_accessible :home_goal_count, :away_goal_count
If so, you could add a method to extract the number of goals:
def goal_count
home_matches.sum(:home_goal_count) + away_matches.sum(:away_goal_count)
end
Since this could be expensive (especially if you do it often), you might just cache this value into the team model and use an after_save hook on the Match model (and, if matches ever get deleted, then an after_destroy hook as well):
after_save :update_team_goals
def update_team_goals
home_team.update_attribute(:goal_count_cache, home_team.goal_count)
away_team.update_attribute(:goal_count_cache, away_team.goal_count)
end
Since you want to do this for leagues, you probably want to add a belongs_to :league on the Match model, a league parameter to the goal_count method (and its query), and a goal_count_cache_league column if you want to cache the value (only cache the most recently changed with my suggested implementation, but tweak as needed).
You dont put that in any table. Theres a rule for databases: Dont ever store data in your database that could be calculated from other fields.
You can calcuate that easyly using this function:
def total_goals
self.home_matches.collect(&:home_goals).inject(&:+)+self.away_matches.collect(&:away_goals).inject(&:+)
end
that should do it for you. If you want the mathes filtered for a league you can use a scope for that.

Using Retrieval Multiple Objects for another Retrieval in Active Records/Ruby on Rails

Kind of new to Ruby/Rails, coming from c/c++, so I'm doing my baby steps.
I'm trying to find the most elegant solution to the following problem.
Table A, among others has a foreign key to table B (let's call it b_id), and table B contains a name field and a primary (id).
I wish to get a list of object from A, based on some criteria, use this list's b_id to access Table B, and retrieve the names (name field).
I've been trying many things which fail. I guess I'm missing something fundamental here.
I tried:
curr_users = A.Where(condition)
curr_names = B.where(id: curr_users.b_id) # fails
Also tried:
curr_names = B.where(id: curr_users.all().b_id) # fails, doesn't recognize b_id
The following works, but it only handles a single user...
curr_names = B.where(id: curr_users.first().b_id) # ok
I can iterate the curr_users and build an array of foreign keys and use them to access B, but it seems there must be more elegant way to do this.
What do I miss here?
Cheers.
Assuming you have following models:
class Employee
belongs_to :department
end
class Department
has_many :employees
end
Now you can departments based on some employee filter
# departments with employees from California
Department.include(:employees).where(:employees => {:state => "CA"}).pluck(:name)
For simplicity, let's take an example of Article and Comments, instead of A and B.
A Comment has a foreign key article_id pointing at Article, so we can setup a has_many relationship from Article to Comment and a belongs_to relationship from Comment to Article like so:
class Article < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :article
end
Once you have that, you will be able do <article>.comments and Rails will spit out an array of all comments that have that article's foreign key. No need to use conditionals unless you are trying to set up a more complicated query (like all comments that were created before a certain date, for example).
To get all the comment titles (names in your example), you can do <article>.comments.map(&:title).

Resources