Searchick filter with scope
Has anybody used searchkick with a scoped model?
I have an Article
model with scope :published, ->{ where(status: "Published") }
.
So In my ArticleController
I did the following:
query = params[:q].presence || "*"
@search = Article.published.search(query, operator: "or", suggest: true)
but it didn't work https://github.com/ankane/searchkick/issues/140 and kept including all the records
So I did:
query = params[:q].presence || "*"
@search = Article.search(query, where:{status: "Published"}, operator: "or", suggest: true)
Something like http://stackoverflow.com/questions/34797144/how-to-use-scopes-with-searchkick-in-controller
Didn't work either... and returned nothing.
Not sure what I am doing wrong.
Could you give some examples of how your data's indexed and the actual terms you're searching with that don't seem to be working, alongside your queries?
One random guess is that maybe you're expecting partial matches, as in "some" to return an article that has the word "sometime" in its title. SearchKick can do that, but you've gotta explicitly tell it to:
class Article < ActiveRecord::Base
searchkick word_start: [:title]
end
Reindex, then
@search = Article.search(query, where:{status: "Published"}, operator: "or", suggest: true, fields: [:title], match: :word_start)
Not sure how the order and nesting of your arguments might affect your queries in this situation, so maybe worth experimenting with that until you get the behavior you want.
Thanks for your help Chris.
The query was initially working fine but returning all records even the ones that are not "Published" so I figure .published
was not working.
class Article < ActiveRecord::Base
searchkick highlight: [:title], text_start: [:title], language: "spanish"
scope :draft, ->{ where(status: "Draft") }
scope :published, ->{ where(status: "Published") }
scope :scheduled, ->{ where(status: "Scheduled") }
def search_data
{
title: title,
content: content
}
end
I am searching for a simple word that should be returning many records that are "Published". If I remove where:{status: "Published"}
the query works again but including all records.
Here is the output from rails c
:
Article.search("Amor", {where:{status: "Published"}, operator: "or", suggest: true})
Article Search (6.0ms) curl http://localhost:9200/articles_development/_search?pretty -d '{"query":{"filtered":{"query":{"dis_max":{"queries":[{"match":{"_all":{"query":"Amor","operator":"or","boost":10,"cutoff_frequency":0.001,"analyzer":"searchkick_search"}}},{"match":{"_all":{"query":"Amor","operator":"or","boost":10,"cutoff_frequency":0.001,"analyzer":"searchkick_search2"}}},{"match":{"_all":{"query":"Amor","operator":"or","boost":1,"analyzer":"searchkick_search","fuzziness":1,"prefix_length":0,"max_expansions":3,"fuzzy_transpositions":true}}},{"match":{"_all":{"query":"Amor","operator":"or","boost":1,"analyzer":"searchkick_search2","fuzziness":1,"prefix_length":0,"max_expansions":3,"fuzzy_transpositions":true}}}]}},"filter":{"and":[{"term":{"status":"Published"}}]}}},"size":1000,"from":0,"fields":[]}'
=> #<Searchkick::Results:0x007f9228714280 @klass=Article(id: integer, title: string, author: string, content: text, introduction: text, description: text, facebook: boolean, twitter: boolean, published_at: datetime, status: string, error: text, views_count: integer, created_at: datetime, updated_at: datetime, image_file_name: string, image_content_type: string, image_file_size: integer, image_updated_at: datetime, slug: string, user_id: integer), @response={"took"=>3, "timed_out"=>false, "_shards"=>{"total"=>5, "successful"=>5, "failed"=>0}, "hits"=>{"total"=>0, "max_score"=>nil, "hits"=>[]}}, @options={:page=>1, :per_page=>1000, :padding=>0, :load=>true, :includes=>nil, :json=>false, :match_suffix=>"analyzed", :highlighted_fields=>[]}>
I did try to reindex without seeing any changes.
Hey Christophe,
Didn't see this until just now. You actually need to provide the status in the search_data
so that ElasticSearch can index it and allow you to query off it. Right now you don't have it, so the where query isn't going to be able to find anything with that attribute. You'll want to just add that status into the hash and then reindex and your search should work then.
Hey Chris or Christophe,
I am having the exact same problem, where the empty query on my indexed model is returning all records rather than the records in my scope.
How exactly should the updated search_data
look?
For context, this is my Profile.rb
model:
enum status: { unpublished: 0, published: 1 }
And this is what I tried with my search_data
.
def search_data
{
name: name,
bib_color: bib_color,
player_type: player_type,
school_name: school.name,
age: age,
status: published,
position_name: positions.map(&:name)
}
end
And even after I reindexed my Profile
model, I am still seeing unpublished records show up in my empty query.
[66] pry(main)> query
=> "*"
[67] pry(main)> Profile.search(query).count
Profile Search (50.0ms) curl http://localhost:9200/profiles_development/_search?pretty -d '{"query":{"match_all":{}},"size":1000,"from":0,"timeout":"11s","_source":false}'
Profile Load (2.8ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" IN (14, 22, 24, 25, 26, 29, 52, 5, 9, 12, 21, 33, 34, 4, 6, 15, 27, 35, 36, 38, 7, 13, 16, 18, 28, 39, 46, 17, 23, 31, 37, 47)
=> 32
[68] pry(main)> Profile.published.count
(0.7ms) SELECT COUNT(*) FROM "profiles" WHERE "profiles"."status" = $1 [["status", 1]]
=> 30
[75] pry(main)> Profile.where(status: :published).search(query).count
Profile Search (10.9ms) curl http://localhost:9200/profiles_development/_search?pretty -d '{"query":{"match_all":{}},"size":1000,"from":0,"timeout":"11s","_source":false}'
Profile Load (1.4ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" IN (14, 22, 24, 25, 26, 29, 52, 5, 9, 12, 21, 33, 34, 4, 6, 15, 27, 35, 36, 38, 7, 13, 16, 18, 28, 39, 46, 17, 23, 31, 37, 47)
=> 32
[80] pry(main)> Profile.published.search(query).count
Profile Search (23.8ms) curl http://localhost:9200/profiles_development/_search?pretty -d '{"query":{"match_all":{}},"size":1000,"from":0,"timeout":"11s","_source":false}'
Profile Load (9.3ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" IN (14, 22, 24, 25, 26, 29, 52, 5, 9, 12, 21, 33, 34, 4, 6, 15, 27, 35, 36, 38, 7, 13, 16, 18, 28, 39, 46, 17, 23, 31, 37, 47)
=> 32
What am I missing?
Thanks!
@Marc because you're using an enum, the database and search is only going to store 0
or 1
. You have to query for the number, not the string :published
. That's probably your issue there.
Interesting idea Chris....but still getting the same issue:
[83] pry(main)> Profile.where(status: 1).search(query).count
Profile Search (19.8ms) curl http://localhost:9200/profiles_development/_search?pretty -d '{"query":{"match_all":{}},"size":1000,"from":0,"timeout":"11s","_source":false}'
Profile Load (13.8ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" IN (14, 22, 24, 25, 26, 29, 52, 5, 9, 12, 21, 33, 34, 4, 6, 15, 27, 35, 36, 38, 7, 13, 16, 18, 28, 39, 46, 17, 23, 31, 37, 47)
=> 32
So what worked was:
[31] pry(main)> query
=> "*"
[32] pry(main)> Profile.search(query, where: { status: :published}).count
Profile Search (8.3ms) curl http://localhost:9200/profiles_development/_search?pretty -d '{"query":{"bool":{"must":{"match_all":{}},"filter":[{"term":{"status":"published"}}]}},"size":1000,"from":0,"timeout":"11s","_source":false}'
Profile Load (3.5ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" IN (14, 22, 29, 26, 24, 25, 21, 34, 5, 9, 33, 4, 15, 27, 35, 36, 38, 6, 16, 39, 46, 7, 18, 13, 28, 23, 31, 37, 17)
=> 29
I was facing the similar issue and adding the field to to search_data
worked for me. In my case I needed to filter records based on institute_id
of the creator
of a Course
.
sharing the solution below:
In course.rb
model
# elastic search client to query for partial strings
searchkick word_middle: [ :name, :description, :instructor_name, :unit_name, :play_list_name ]
scope :search_import, -> { includes(:creator, :units, :instructors) }
def search_data
{
name: name,
description: description,
}.merge(
unit_name: units.pluck(:'units.name').reject(&:nil?),
instructor_name: instructors.includes(:teacher).pluck(:'users.name').reject(&:nil?),
play_list_name: units.includes(:play_lists).pluck(:'play_lists.name').reject(&:nil?),
institute_id: creator.try(:institute_id)
)
end
In my controller:
@items = Searchkick.search search_params[:search], operator: "or",
index_name: [Course],
where:{institute_id: institute_id},
fields: ['name^100', 'instructor_name^100', 'play_list_name^70', 'description^50', 'unit_name^50', 'institute_id^100' ],
match: :word_middle,
misspellings: {below: 5, edit_distance: 2},
page: search_params[:page],
per_page: 20,
order: {_score: :desc}
Hi, I still have this problem. When I create a user whose role is not "a" and search, I can get that user. Should I specify where
in the search function? But when I explicitly call People.reindex, I can not get that user any more.
This is what I write
People.rb
scope :search_import, ->{where(role:"a")}
def should_index?
self.search_import
end
def search_data
{
username: username,
description:description,
role:role,
}
end
When I search, i use
result=People.search(params[:search], {fields: [role], autocomplete: true, load: false, misspellings: {below: 4}})