How to fetch associated model name in search results with seachkick - ruby-on-rails

I used gem 'searchkick' for searching in my application.
here is my association and searchkick settings .
product.rb
belongs_to :sub_category
belongs_to :brand
belongs_to :product_type
has_one :category, through: :sub_category
searchkick match: :word_start,word_start: [:name], suggest: [:name]
scope :search_import, -> { includes(:brand, :sub_category, :category, :product_type) }
def search_data
{
name: name,
category_name: category.name,
sub_category_name: sub_category.name,
brand: brand.name,
product_type: product_type.name
}
end
I don't how searchkick works exactly. but i want to show the category name, sub category name and product type name in search results.
For an example,
I have list of alcohols products which belongs to different categories like beer, wine, whiskey etc.
if i search for beer, it should show beer in search results because beer is category which associated to product. i don't want the products which are associated to beer category, i want to beer category in search results.
here is my query for search query
response = Product.search( params[:query], suggest: true, fields: ["name^10", "description"], limit: 5, operator: "or",misspellings: {below: 5} ).results
it is similar functionality of any e commerce application, like if i search iPhone 7 in flipkart application it will show iPhone 7 as category in search result, if i click on that all products related to iPhone 7 will show in one page.
i don't know how to achieve this , any help will appreciated.

I think you are looking for aggregation feature. Just look the searchkick doc, you will find your answer.
Note: I am giving answer from Stackoverflow app. So I will later update it with some code from laptop.

I have to do multi search , Here is the service which i made for getting associated model with multi search.
module Search
class RetriveData
def self.multi_search(params)
#params = params
create_searchable_variables_for_multi_model_search
Searchkick.multi_search(array_of_searchkick_objects)
array_of_searchkick_objects.map(&:results).flatten
end
private
def self.array_of_searchkick_objects
array_of_models.map do |klass|
instance_variable_get("##{klass.to_s.downcase}s")
end
end
def self.searchable_fields
["name^10","sub_category_name","keyword_name",
"category_name","product_type_name","brand_name"]
end
def self.create_searchable_variables_for_multi_model_search
array_of_models.each do |klass|
instance_variable_set("##{klass.to_s.downcase}s",
klass.search( #params[:query],
constraints(klass) ))
end
end
def self.constraints(klass)
{
fields: searchable_fields,
suggest: true,
operator: "or",
misspellings: {below: 5},
where: klass == Product ? { or: [[{available_cities_name:
#params[:city]},{available_cities_short_name:
#params[:short_name]}]] } : {},
execute: false
}
end
def self.array_of_models
[Category, SubCategory, Brand, ProductType, Product]
end
end
end
Source is here

Related

Ruby on Rails: Find all records that match a list

I have 2 models:
class Item < ApplicationRecord
has_many :tags, dependent: :destroy
end
class Tag < ApplicationRecord
belongs_to :item
end
My current query will return Items that have ANY of the tags in a list, I need it to only return Items that have ALL of the tags.
tag_list = ['Foo', 'Bar', 'Baz'] # may contain more
Item.distinct
.joins(:tags)
.where(tags: { name: tag_list })
What can I do to return only the Items that have ALL the Tag names from my list using ActiveRecord?
Thanks for the help.
Edit: I'm using postgres
This should work:
tag_list = ['Foo', 'Bar', 'Baz']
Item.distinct
.joins(:tags)
.where(tags: { name: tag_list })
.group('tags.item_id')
.having("COUNT(DISTINCT tags) = #{tag_list.size}")
Can we use where("tags.name in (?)", tag_list) ?
(If anyone has any suggestions, I would appreciate it)
Item.joins(:tags)
.where("tags.name in (?)", tag_list).uniq
You can filter all the tags having name 'Bar', 'Baz', 'Foo' (order matters) and group them by their item_id, then you can get the items with those ids;
Item.where(
id: Tag
.group(:item_id)
.having("ARRAY_AGG(DISTINCT name ORDER BY name) = ARRAY[?]::VARCHAR[]", ['Bar', 'Baz', 'Foo'])
.select(:item_id)
)
Here the ARRAY[?]::VARCHAR[] is because tags.name is of type character varying, so you have to map the value to the right of the = in HAVING to whatever is the output of ARRAY_AGG(DISTINCT name ORDER BY name).

add a custom Filter Active Admin Rails

I work on a foodtech project where people can order meals from restaurants; So I have the following relationship in my DB:
each order belongs_to a meal and each meal belongs_to a restaurant.
I would like to be able to filter by restaurant name on the /admin/orders page.
Also if possible, I'd like to add a 'restaurant name' column on the page.
Any Idea ?
OK so here's what I finally did; It solves the problem perfectly.
I added has_one :restaurant, through: :meal in model/order.rb and added the filter simply this way: filter :restaurant.
This alowed me to add the restaurant filter.
To get the restaurant column, here's the code:
in admin/order.rb
index do
column("Order", :sortable => :id) {|order| link_to "##{order.id} ", admin_order_path(order) }
column("pick up time", :pick_up_time)
column("status", :status)
column("User", :user_id ) {|order| link_to "#{order.user.first_name} #{order.user.last_name}", admin_user_path(order.user) }
column("Meal", :meal)
column("Restaurant", :restaurant) // This line adds the restaurant column
column("Created at", :created_at)
column("Updated at", :updated_at)
end

how to find id of product on click of autosuggestion using elastic search in rails

i am using elasticsearch with my rails application(railscasts #306,#307) now i want to know how i can get id of a product on click of any autosuggestion, so that i can find that product and can show user only that perticular product record. i am using tire gem and my model query is as follow:
include Tire::Model::Search
include Tire::Model::Callbacks
settings index: {
number_of_shards: 1,
number_of_replicas: 0,
} do
mapping do
indexes :name, :type => 'string'
end
end
def self.search(params)
if params[:query].present?
tire.search(load: true) do
query { string "#{params[:query]}*"}
end
else
tire.search(load: true) do
query { string "*#{params["term"]}*"}
end
end
end
Please help me as soon as possible and thanks in advance.

Filter for values OR IS NULL in activeadmin

Given I have two models:
class Post < ActiveRecord::Base
belongs_to :district
end
class District < ActiveRecord::Base
has_many :posts
end
I need to make a check_boxes filter in ActiveAdmin on Posts page for with ability for user to get posts that belong to some exact districts or does not belong to any districts at all.
Before ActiveAdmin changed MetaSearch to Ransack, that could be done with custom scope. And now I don't have any idea. When I do the following:
filter :district_id_null, as: :boolean
filter :district, as: :check_boxes
It makes condition WHERE district_id IN (1,2,3) AND district_id IS NULL (I need OR instead of AND). And when I do
filter :district, as: :check_boxes, collection: proc { District.unscoped.map { |x| [x.title, x.id] }.unshift ['Empty', 'null'] }
It makes condition WHERE district_id IN (0,1,2,3) (but in most SQL databases NULL is not 0).
I think something like this might work
class Post
def self.ransack_with_or(search_params)
params = search_params.deep_clone
#check if we need OR
if search_params.has_key?('district_id_in') && search_params.has_key?('district_id_null')
params.delete('district_id_null')
district_id_in = params.delete('district_id_in')
#old behaviour without district_id_null and district_id_null attributes
q = ransack_without_or(params)
#here we're adding OR group
q.build_grouping({m: 'or', district_id_null: true, district_id_in: district_id_in})
else
#old behaviour we don't need OR
q = ransack_without_or(params)
end
q
end
#wrapping ransack method
class << self
alias_method_chain :ransack, :or
end
end

Using Delta Indexes for associations in Thinking Sphinx

I have a Product model:
class Product < ActiveRecord::Base
belongs_to :subcategory
define_index do
# fields
indexes subcategory.name, :as => :subcategory, :sortable => true, :facet => true
# attributes
has subcategory_id, created_at, updated_at
#properties
set_property :delta => true
Now, suppose that a user updates a subcategory name, which is the proper way to update the products delta index?
According to this documentation: http://freelancing-god.github.com/ts/en/deltas.html, a save message should be sent to the product, so in this case I should go for each product related with the subcategory and send the save message, something like this:
class Subcategory < ActiveRecord::Base
has_many :products
after_save :set_product_delta_flag
private
def set_product_delta_flag
products.each { |product|
product.delta = true
product.save
}
end
end
I think that this is overkilling because we have like 100.000 products per subcategory.
Is this the correct way to update the delta index? Am I missing something?
After adding this:
def set_product_delta_flag
Product.update_all ['delta = ?', true], ['subcategory_id = ?', id]
Product.index_delta
end
I'm always receiving this error:
NoMethodError (undefined method `index_delta' for #):
So, the solution to this problem was to send the message *define_indexes* to the Product model.
After fixing this issue, everything was ok, but the delta_index was not correctly updated, I needed to do save twice to the subcategory model.
So my final solution is this one:
after_commit :set_product_delta_flag
private
def set_product_delta_flag
Product.define_indexes
Product.update_all ['delta = ?', true], ['subcategory_id = ?', id]
Product.index_delta
end
Using after_commit and define_indexes is the correct solution? Its the only one that I've found.
Try the following instead:
def set_product_delta_flag
Product.update_all ['delta = ?', true], ['subcategory_id = ?', id]
Product.index_delta
end
A single SQL statement, a single delta re-indexing. Should perform far better :)

Resources