module Spree
Product.class_eval do
include Elasticsearch::Model
index_name Spree::ElasticsearchSettings.index
document_type 'spree_product'
mapping _all: {"index_analyzer" => "nGram_analyzer", "search_analyzer" => "whitespace_analyzer"} do
indexes :name, type: 'multi_field' do
indexes :name, type: 'string', analyzer:'nGram_analyzer', boost: 100
indexes :untouched, type: 'string', include_in_all: false, index: 'not_analyzed'
end
indexes :name_whole, type: 'multi_field' do
indexes :name, type: 'string', index_analyzer:'simple'
end
indexes :name_completion, type: 'multi_field' do
indexes :untouched, type: 'string', include_in_all: false, index: 'not_analyzed'
end
indexes :taxon_ids, type: 'multi_field' do
indexes :taxon_ids, type:'string', analyzer: 'simple'
indexes :taxon_ids_ngram, type:'string', analyzer:'nGram_analyzer'
end
indexes :description, analyzer: 'snowball', include_in_all:false
indexes :available_on, type: 'date', format: 'dateOptionalTime', include_in_all: false
indexes :price, type: 'double', include_in_all:false
indexes :sku, type: 'string', index: 'not_analyzed', include_in_all:false
indexes :properties, type: 'string', index: 'not_analyzed', include_in_all:false
end
mapping _suggest: {"index_analyzer" => "simple", "search_analyzer" => "whitespace_analyzer"} do
indexes :name_completion, type: 'multi_field' do
indexes :name, type: 'completion', index_analyzer:'simple', search_analyzer: 'simple', payloads: true
end
indexes :taxon_ids, type: 'multi_field' do
indexes :taxon_ids, type:'string', analyzer: 'simple'
indexes :taxon_ids_ngram, type:'string', analyzer:'nGram_analyzer'
end
indexes :description, analyzer: 'snowball', include_in_all:false
indexes :available_on, type: 'date', format: 'dateOptionalTime', include_in_all: false
indexes :price, type: 'double', include_in_all:false
indexes :sku, type: 'string', index: 'not_analyzed', include_in_all:false
indexes :properties, type: 'string', index: 'not_analyzed', include_in_all:false
end
def as_indexed_json(options={})
result = as_json({
methods: [:price, :sku],
only: [:available_on, :description, :name],
include: {
variants: {
only: [:sku],
include: {
option_values: {
only: [:name, :presentation]
}
}
}
}
})
result[:properties] = property_list unless property_list.empty?
result[:taxon_ids] = taxons.map(&:self_and_ancestors).flatten.uniq.map(&:id) unless taxons.empty?
result
end
# Inner class used to query elasticsearch. The idea is that the query is dynamically build based on the parameters.
class Product::ElasticsearchQuery
include ::Virtus.model
attribute :from, Integer, default: 0
attribute :price_min, Float
attribute :price_max, Float
attribute :properties, Hash
attribute :query, String
attribute :taxons, Array
attribute :browse_mode, Boolean
attribute :sorting, String
# When browse_mode is enabled, the taxon filter is placed at top level. This causes the results to be limited, but facetting is done on the complete dataset.
# When browse_mode is disabled, the taxon filter is placed inside the filtered query. This causes the facets to be limited to the resulting set.
# Method that creates the actual query based on the current attributes.
# The idea is to always to use the following schema and fill in the blanks.
# {
# query: {
# filtered: {
# query: {
# query_string: { query: , fields: [] }
# }
# filter: {
# and: [
# { terms: { taxons: [] } },
# { terms: { properties: [] } }
# ]
# }
# }
# }
# filter: { range: { price: { lte: , gte: } } },
# sort: [],
# from: ,
# facets:
# }
def to_hash
q = { match_all: {} }
unless query.blank? # nil or empty
q = { query_string: { query: query, fields: ['name^5','description','sku'], default_operator: 'AND', use_dis_max: true } }
end
query = q
and_filter = []
unless @properties.nil? || @properties.empty?
# transform properties from [{"key1" => ["value_a","value_b"]},{"key2" => ["value_a"]}
# to { terms: { properties: ["key1||value_a","key1||value_b"] }
# { terms: { properties: ["key2||value_a"] }
# This enforces "and" relation between different property values and "or" relation between same property values
properties = @properties.map {|k,v| [k].product(v)}.map do |pair|
and_filter << { terms: { properties: pair.map {|prop| prop.join("||")} } }
end
end
sorting = case @sorting
when "name_asc"
[ {"name.untouched" => { order: "asc" }}, {"price" => { order: "asc" }}, "_score" ]
when "name_desc"
[ {"name.untouched" => { order: "desc" }}, {"price" => { order: "asc" }}, "_score" ]
when "price_asc"
[ {"price" => { order: "asc" }}, {"name.untouched" => { order: "asc" }}, "_score" ]
when "price_desc"
[ {"price" => { order: "desc" }}, {"name.untouched" => { order: "asc" }}, "_score" ]
when "score"
[ "_score", {"name.untouched" => { order: "asc" }}, {"price" => { order: "asc" }} ]
else
[ {"name.untouched" => { order: "asc" }}, {"price" => { order: "asc" }}, "_score" ]
end
# facets
facets = {
price: { statistical: { field: "price" } },
properties: { terms: { field: "properties", order: "count", size: 1000000 } },
taxon_ids: { terms: { field: "taxon_ids", size: 1000000 } }
}
# basic skeleton
result = {
min_score: 0.1,
query: { filtered: {} },
sort: sorting,
from: from,
facets: facets
}
# add query and filters to filtered
result[:query][:filtered][:query] = query
# taxon and property filters have an effect on the facets
and_filter << { terms: { taxon_ids: taxons } } unless taxons.empty?
# only return products that are available
#and_filter << { range: { available_on: { lte: "now" } } }
result[:query][:filtered][:filter] = { "and" => and_filter } unless and_filter.empty?
# add price filter outside the query because it should have no effect on facets
if price_min && price_max && (price_min < price_max)
result[:filter] = { range: { price: { gte: price_min, lte: price_max } } }
end
result
end
end
private
def property_list
product_properties.map{|pp| "#{pp.property.name}||#{pp.value}"}
end
end
end
我有以下模型有两个映射,一个用于常规搜索,另一个用于搜索建议。根据ElasticSearch文档,后一个映射是必需的,以启用Completion Suggestor - https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-completion.html。
索引上述模型时,我收到以下错误:
[400] {&#34;错误&#34;:&#34; MapperParsingException [mapping [spree_product]];嵌套:MapperParsingException [解析后根类型映射不为空!剩余字段:[_ abstract :: {search_analyzer = whitespace_analyzer,index_analyzer = simple}]]; &#34;&#34;状态&#34; 400}
/usr/local/bundle/gems/elasticsearch-transport-1.0.12/lib/elasticsearch/transport/transport/base.rb:135:in __raise_transport_error'
/usr/local/bundle/gems/elasticsearch-transport-1.0.12/lib/elasticsearch/transport/transport/base.rb:227:in
perform_request&#39;
/usr/local/bundle/gems/elasticsearch-transport-1.0.12/lib/elasticsearch/transport/transport/http/faraday.rb:20:in perform_request'
/usr/local/bundle/gems/elasticsearch-transport-1.0.12/lib/elasticsearch/transport/client.rb:119:in
perform_request&#39;
/usr/local/bundle/gems/elasticsearch-api-1.0.12/lib/elasticsearch/api/namespace/common.rb:21:in perform_request'
/usr/local/bundle/gems/elasticsearch-api-1.0.12/lib/elasticsearch/api/actions/indices/create.rb:77:in
创建&#39;
/tmp/spree_elasticsearch/lib/tasks/load_products.rake:5:in块(2级)&#39;
任务:TOP =&gt; spree_elasticsearch:load_products
答案 0 :(得分:0)
发现了这个问题。将该字段嵌套完成可能不是一个好主意
设置你的映射: 映射做 索引:name_completion,type:&#39; completion&#39;,payloads:true 端
确保返回正确的JSON以进行索引:
[:name_completion] = { 输入:名称, 输出:名称, 有效载荷:&#34;&#34; }
从这个例子中引用: https://github.com/elastic/elasticsearch-rails/commit/ded20356920802c35d258756113acfd95b25ade6