简体   繁体   中英

ElasticSearch add mapping to a numeric, gives me error

I'm using elasticsearch in a Rails application. I want my search results can be sorted by price, so i add the price attribute to mapping. Before this i was able to search with success for the other fields that i had specify. Now i'm getting an error like this:

Elasticsearch::Transport::Transport::Errors::BadRequest in Search#search: [400] {"error":"SearchPhaseExecutionException[Failed to execute phase [query], all shards failed; shardFailures {[7aIAvW_pSlCg7HDBXwNvXA][products][0]: SearchParseException[[products][0]: from[-1],size[-1]: Parse Failure [Failed to parse source [{\\"query\\":{\\"bool\\":{\\"should\\":[{\\"multi_match\\":{\\"query\\":\\"electronics\\",\\"fuzziness\\":2,\\"fields\\":[\\"name^2\\",\\"description\\",\\"category.name\\",\\"price\\"],\\"prefix_length\\":2,\\"operator\\":\\"and\\"}}]}}}]]]; nested: NumberFormatException[For input string: \\"electronics\\"]; }

If i remove price attribute from the mapping i get search results but the order is not correct. It seems that the results are sorted by the first digit, treat like a string i think, eg 1111 appeared to be smaller than 200 because first digit '1' is smaller than '2'.

Any ideas?

Search controller:

class SearchController < ApplicationController
  def search
    options = { sort: params[:s] }
    @products = Product.search(params[:q], options).paginate(page: params[:page], per_page: 5).records
  end
end

Product model:

require "elasticsearch/model"
class Product < ActiveRecord::Base

  include Elasticsearch::Model
  include Elasticsearch::Model::Callbacks

  def self.search(query, options={})
    @search_definition = {
      query: {} }

    unless query.blank?
      @search_definition[:query] = {
        bool: {
          should: [
            { multi_match: {
                query: query,
                fuzziness: 2,
                fields: ['name^2', 'description','category.name', 'price'],
                prefix_length: 2,
                operator: 'and'}}]}}
    else
      @search_definition[:query] = { match_all: {} }
      @search_definition[:sort] = [{ created_at: { order: "desc" }}]
    end

    if options[:sort] == 'Newest'
      @search_definition[:sort] = [{ created_at: { order: "desc" }}]
      @search_definition[:track_scores] = true
    elsif options[:sort] == 'Price - Descending'
      @search_definition[:sort] = [{ price: { order: "desc" }}]
      @search_definition[:track_scores] = true
    elsif options[:sort] == 'Price - Ascending'
      @search_definition[:sort] = [{ price: { order: "asc" }}]
      @search_definition[:track_scores] = true
    end
    __elasticsearch__.search @search_definition

  end

  settings analysis: {
              analyzer: {
                my_index_analyzer: {
                  type: "custom",
                  tokenizer: "standard",
                  filter: ["standard", "lowercase", "translation"] },
                my_search_analyzer: {
                  type: "custom",
                  tokenizer: "standard",
                  filter: ["standard", "lowercase"] }
              },
              filter: {
                translation: {
                  type: "nGram",
                  min_gram: 2,
                  max_gram: 20 }}
  }  
  mapping do
    indexes :name, type: 'string', index_analyzer: 'my_index_analyzer', search_analyzer: 'my_search_analyzer'
    indexes :description, type: 'string', index_analyzer: 'my_index_analyzer', search_analyzer: 'my_search_analyzer'
    indexes :created_at, type: 'date'
    indexes :price, type: 'double', index: "not_analyzed"
    indexes :category do
      indexes :name, type: 'string', index_analyzer: 'my_index_analyzer', search_analyzer: 'my_search_analyzer'
    end
  end


  def as_indexed_json(options={})
    as_json(
      only: [:name, :description, :price, :created_at],
      include: { category: { only: :name } }  
    )
  end

search.html.erb:

<div class="container main-body">
      <h2>Search results</h2>
  <div class="clearfix">
    <ul class="list-inline pull-right">
      <li><h5>Show search results by:</h5></li>
    <li>
      <div class="btn-group">

        <button class="btn btn-default btn-md dropdown-toggle" type="button" data-toggle="dropdown">
          <% sort = case
              when params[:s] then params[:s]
              when params[:q].blank? then 'Newest'
              else 'Relevancy'
             end
          %>
          <%= sort.humanize %> <span class="caret"></span>
        </button>
        <ul class="dropdown-menu" role="menu">
          <li><%= link_to "Relevancy", search_path(params.except(:controller, :action).merge(s: nil)), class: 'btn-xs' %></li>
          <li><%= link_to "Newest", search_path(params.except(:controller, :action).merge(s: 'Newest')), class: 'btn-xs' %></li>
          <li><%= link_to "Price - Descending", search_path(params.except(:controller, :action).merge(s: 'Price - Descending')), class: 'btn-xs' %></li>
          <li><%= link_to "Price - Ascending", search_path(params.except(:controller, :action).merge(s: 'Price - Ascending')), class: 'btn-xs' %></li>
        </ul>
      </div></li>
    </ul>
    </div>

  <div class="clearfix">
    <%= render partial: 'products/products_form' %>
  </div>
  <div class="centered"><%= will_paginate @products %></div>
</div>

The issue and the solution is listed at https://github.com/elastic/elasticsearch/issues/3975

The problem is the multi_match query. It works only with strings.

EDIT:

@search_definition[:query] = {
        bool: {
          should: [
            { multi_match: {
                query: query,
                fuzziness: 2,
                fields: ['name^2', 'description','category.name', 'price'],
                lenient: true
                prefix_length: 2,
                operator: 'and'}}]}}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM