4

I'm using Tire for Elastic Search. In my application I have 2 models; Price and Product.

I'm trying to search my Price class and use the Product it belongs to's :name attribute for the search field. Right now if I had a product called Product 1 and type in "pro", "prod" or "duct", no results come up. But typing "product" or "Product" shows the results. I believe the problem lies in my mapping. I looked at the query and its:

...localhost:3000/search/results?utf8=%E2%9C%93&query=product

When I think it should be:

...localhost:3000/search/results?utf8=%E2%9C%93&query:product=product

Judging by this question: ElasticSearch mapping doesn't work

I don't know how to make my params[:query] to take the product.name only though. I tried to use: string params[:query], default_field: "product.name" but that didn't work.

I don't want to use the _all field.

Here is my code:

Price.rb

  include Tire::Model::Search
  include Tire::Model::Callbacks

  def self.search(params)
    tire.search(load: true, page: params[:page], per_page: 20) do
       query do
         boolean do
            must { string params[:query] } if params[:query].present?
            must { term :private, false }
         end
       end
       sort do
         by :date, "desc"
         by :amount, "asc"
       end
    end
  end

  def to_indexed_json
    to_json( include: { product: { only: [:name] } } )
  end

  mapping do
    indexes :id,        type: "integer"
    indexes :amount,    type: "string", index: "not_analyzed"
    indexes :date,      type: "date",   index: "not_analyzed"
    indexes :private,   type: "boolean"
    indexes :product do
      indexes :name, type: "string", analyzer: "custom_analyzer"
    end
  end

  settings analysis: {
    analyzer: {
      custom_analyzer: {
        tokenizer: [ "whitespace", "lowercase" ],
        filter: [ "ngram_filter", "word_delimiter_filter" ],
        type: "custom"
      }
    },
    filter: {
      ngram_filter: {
        type: "nGram",
        min_gram: 2,
        max_gram: 15
      }
    },
    filter: {
      word_delimiter_filter: {
        type: "word_delimiter",
        generate_word_parts: true,
        generate_number_parts: true,
        preserve_original: true,
        stem_english_possessive: true
      }
    }
  }

So does anyone have any suggestions or know how to set the query field to use the Product name only?

Thank you.

Community
  • 1
  • 1
LearningRoR
  • 26,582
  • 22
  • 85
  • 150
  • 1
    I will have to work on this very same issue in a very near future. So I will update with my progress, unless you beat me to it! – Alain Aug 24 '12 at 14:37
  • @Alain I'm still working on this, so if you have any idea how to get this to work, let me know. Your progress would be great help. – LearningRoR Aug 26 '12 at 14:45

2 Answers2

5

When you do

 query   { string params[:query] } 

and the query doesn't specify a field, you're searching the magic _all field. This field has its own analyzer settings - it's won't be using the one you've setup for your name field.

You can either configure the _all field to use your analyser, change the default analyser or change your query to query the name field specifically, for example

 query { string params[:query], :default_field => 'name'}
Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
  • I couldn't find any documentation on `default_field => 'name'`. Where did you find this out at? If I was trying to use the `product.name` field. How would that happen? – LearningRoR Aug 26 '12 at 01:20
  • This isn't working for me. Is my mapping to the `product.name` correct? I think that's why it's not working. – LearningRoR Aug 26 '12 at 14:43
  • Oh, you probably want to set default field to product.name, hadn't spotted the nested-ness of your mapping – Frederick Cheung Aug 26 '12 at 15:22
  • So I just do: `must { string params[:query], :default_field => "product.name" }`? Because its not working. I have no clue where to go from here but I'm still looking around. – LearningRoR Aug 27 '12 at 01:38
  • Still doing what I can to figure this out. I changed around my code entirely up above. – LearningRoR Aug 27 '12 at 23:42
  • Have you tried a text query with an explicit field? Have you checked that elastic search has picked up your settings (for settings like analyser I think you have to recreate the index when you change them) – Frederick Cheung Aug 28 '12 at 06:31
  • If you mean this `:default_field => 'name'` or this `:default_field => 'product.name'`, then yes and both will return no search results. By pick up settings, the only thing I run is `$ rake environment tire:import CLASS='Price' FORCE=true`. Maybe it's my Rails and Tire version that is the problem ( `"rails", "3.1.0"`, `"tire", "0.4.2"`) – LearningRoR Aug 28 '12 at 12:52
  • 1
    I mean use a query of type text rather than string. Worth poking around to see if elasticsearch is actually using the mapping you've given (have a look at the settings and mappin apis) – Frederick Cheung Aug 28 '12 at 13:31
  • I think your right. The mapping is not happening I believe. I updated my code to show you a strange error. I change my `query_string` and got an error. – LearningRoR Aug 28 '12 at 14:24
  • OK, so I believe the `settings` block isn't working. Not sure why though. My Update 2 explains in more. – LearningRoR Aug 28 '12 at 23:03
  • 1
    I'd try deleting the index and then letting tire recreate it – Frederick Cheung Aug 29 '12 at 06:26
  • The only way I know of deleting is by doing `rake environment tire:import CLASS='Price' FORCE=true`. It doesn't have a rake command to delete I think. – LearningRoR Aug 29 '12 at 16:01
  • OK, so I don't think it's the settings, it's the mapping. I updated my question to reflect this. – LearningRoR Aug 29 '12 at 16:17
  • Thanks for all the help. Just went to use Ransack instead. Way easier to figure out. – LearningRoR Aug 30 '12 at 02:22
0

This was to much of a headache so I ended going to Ransack which was insanely much easier to figure out and use.

LearningRoR
  • 26,582
  • 22
  • 85
  • 150