diff --git a/app/helpers/spree/base_helper_decorator.rb b/app/helpers/spree/base_helper_decorator.rb index 91b7612..fa2cec8 100644 --- a/app/helpers/spree/base_helper_decorator.rb +++ b/app/helpers/spree/base_helper_decorator.rb @@ -3,20 +3,22 @@ module Spree # parses the properties facet result # input: Facet(name: "properties", type: "terms", body: {"terms" => [{"term" => "key1||value1", "count" => 1},{"term" => "key1||value2", "count" => 1}]}]) # output: Facet(name: key1, type: terms, body: {"terms" => [{"term" => "value1", "count" => 1},{"term" => "value2", "count" => 1}]}) - def expand_properties_facet_to_facet_array(facet) + def expand_properties_aggregation_to_aggregation_array(aggregation) # first step is to build a hash # {"property_name" => [{"term" => "value1", "count" => 1},{"term" => "value2", "count" => 1}]}} property_names = {} - facet["terms"].each do |term| - t = term["term"].split("||") + aggregation[:buckets].each do |term| + t = term[:key].split('||') property_name = t[0] property_value = t[1] - # add a search_term to each term hash to allow searching on the element later on - property = {"term" => property_value, "count" => term["count"], "search_term" => term["term"]} - if property_names.has_key?(property_name) - property_names[property_name] << property - else - property_names[property_name] = [property] + if property_value + # add a search_term to each term hash to allow searching on the element later on + property = { term: property_value, count: term[:doc_count], search_term: term[:key] } + if property_names.has_key?(property_name) + property_names[property_name] << property + else + property_names[property_name] = [property] + end end end # next step is to transform the hash to facet objects @@ -24,10 +26,9 @@ def expand_properties_facet_to_facet_array(facet) # format: Facet(name: "property_name", type: type, body: {"terms" => [{"term" => "value1", "count" => 1},{"term" => "value2", "count" => 1}]}]) result = {} property_names.each do |key,value| - value.sort_by!{|h| [-h["count"],h["term"].downcase]} # first sort on desc, then on term asc + value.sort_by!{ |value| [-value[:count], value[:term].downcase] } # first sort on desc, then on term asc # result << Spree::Search::Elasticsearch::Facet.new(name: key, search_name: facet.name, type: facet.type, body: {"terms" => value}) result[key] = { - '_type' => facet['_type'], 'terms' => value } end @@ -37,19 +38,19 @@ def expand_properties_facet_to_facet_array(facet) # Helper method for interpreting facets from Elasticsearch. Something like a before filter. # Sorting, changings things, the world is your oyster # Input is a hash - def process_facets(facets) - new_facets = {} + def process_aggregations(aggregations) + new_aggregations = {} delete_keys = [] - facets.map do |key, facet| - if key == "properties" - new_facets.merge! expand_properties_facet_to_facet_array(facet) + aggregations.map do |key, aggregation| + if key == 'properties' + new_aggregations.merge! expand_properties_aggregation_to_aggregation_array(aggregation) delete_keys << :properties else - facet + aggregation end end - delete_keys.each {|key| facets.delete(key) } - facets.merge! new_facets + delete_keys.each { |key| aggregations.delete(key) } + aggregations.merge! new_aggregations end end -end \ No newline at end of file +end diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 09c042e..264c98b 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -5,11 +5,12 @@ module Spree 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 + mapping _all: { analyzer: 'nGram_analyzer', search_analyzer: 'whitespace_analyzer' } do + indexes :name, type: 'string' do indexes :name, type: 'string', analyzer: 'nGram_analyzer', boost: 100 indexes :untouched, type: 'string', include_in_all: false, index: 'not_analyzed' end + indexes :description, analyzer: 'snowball' indexes :available_on, type: 'date', format: 'dateOptionalTime', include_in_all: false indexes :price, type: 'double' @@ -24,6 +25,7 @@ def as_indexed_json(options={}) only: [:available_on, :description, :name], include: { variants: { + methods: [:total_on_hand], only: [:sku], include: { option_values: { @@ -62,8 +64,8 @@ class Product::ElasticsearchQuery # The idea is to always to use the following schema and fill in the blanks. # { # query: { - # filtered: { - # query: { + # bool: { + # must: { # query_string: { query: , fields: [] } # } # filter: { @@ -77,12 +79,18 @@ class Product::ElasticsearchQuery # filter: { range: { price: { lte: , gte: } } }, # sort: [], # from: , - # facets: + # aggregations: # } 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 } } + q = { query_string: { + query: query, + fields: ['name^10','description','sku', 'variants.sku', 'variant.*', 'name.*^.1'], + default_operator: 'AND', + use_dis_max: true + } + } end query = q @@ -92,50 +100,50 @@ def to_hash # 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("||")} } } + properties = @properties.map{ |key, value| [key].product(value) }.map do |pair| + and_filter << { terms: { properties: pair.map { |property| property.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" }} ] + when 'default' + [ { 'variants.total_on_hand' => { order: 'desc' } }, { price: { order: 'asc' } }, '_score' ] + when 'score' + [ '_score', { price: { order: 'asc' } }, { 'name.untouched' => { order: 'asc' } } ] + when 'price_asc' + [ { 'price' => { order: 'asc' } }, { 'name.untouched' => { order: 'asc' } }, '_score' ] + when 'price_desc' + [ { 'price' => { order: 'desc' } }, { 'name.untouched' => { order: 'asc' } }, '_score' ] + when 'new_arrival' + [ { 'available_on' => { order: 'desc' } }, { price: { order: 'asc' } }, '_score' ] else - [ {"name.untouched" => { order: "asc" }}, {"price" => { order: "asc" }}, "_score" ] + [ '_score', { 'variants.total_on_hand' => { order: 'desc' } }, { price: { order: 'asc' } } ] end - # facets - facets = { - price: { statistical: { field: "price" } }, - properties: { terms: { field: "properties", order: "count", size: 1000000 } }, - taxon_ids: { terms: { field: "taxon_ids", size: 1000000 } } + # aggregations + aggregations = { + price: { stats: { field: 'price' } }, + properties: { terms: { field: 'properties', size: 1000000 } }, + taxon_ids: { terms: { field: 'taxon_ids', size: 1000000 } } } # basic skeleton result = { - min_score: 0.1, - query: { filtered: {} }, + min_score: 1, + query: { bool: {} }, sort: sorting, from: from, size: Spree::Config.products_per_page, - facets: facets + aggregations: aggregations } - # add query and filters to filtered - result[:query][:filtered][:query] = query + # add query and filters to bool + result[:query][:bool][:must] = query # taxon and property filters have an effect on the facets - and_filter << { terms: { taxon_ids: taxons } } unless taxons.empty? + 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? + and_filter << { range: { available_on: { lte: 'now' } } } + result[:query][:bool][:filter] = 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) diff --git a/app/views/spree/shared/_filter_price.html.erb b/app/views/spree/shared/_filter_price.html.erb index b8fcd90..5ede448 100644 --- a/app/views/spree/shared/_filter_price.html.erb +++ b/app/views/spree/shared/_filter_price.html.erb @@ -1,14 +1,14 @@ -
-

<%= facet.name %>

-
-
-
- +
+

<%= aggregation.name %>

+
+
+
+
-
- "> +
+ to - "> +
@@ -16,22 +16,22 @@ <% content_for :head do %>