Ask A Question

Notifications

You’re not receiving notifications from this thread.

Searchick filter with scope

Christophe Estanol asked in Gems / Libraries

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.

Reply

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.

Reply

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.

Reply

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.

Reply

Well that was easy! Thanks very much Chris.

Reply

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!

Reply

@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.

Reply

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
Reply

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
Reply

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}
Reply

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}}) 
Reply

@ChristopheEstanol390XP could you show what your final solution was?

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 85,831+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.