Commit b5763d73da146a2e492986eeb1c71103805e627b

Authored by Andrew Kane
1 parent 64301b0e

Added aggregates

README.md
... ... @@ -45,7 +45,7 @@ Add this line to your application’s Gemfile:
45 45 gem 'searchkick'
46 46 ```
47 47  
48   -For Elasticsearch 0.90, use version `0.6.3` and [this readme](https://github.com/ankane/searchkick/blob/v0.6.3/README.md).
  48 +For Elasticsearch 2.0, use version `0.9.2` or above. For Elasticsearch 0.90, use version `0.6.3` and [this readme](https://github.com/ankane/searchkick/blob/v0.6.3/README.md).
49 49  
50 50 Add searchkick to models you want to search.
51 51  
... ... @@ -547,11 +547,95 @@ products = Product.search "peantu butta", suggest: true
547 547 products.suggestions # ["peanut butter"]
548 548 ```
549 549  
550   -### Facets
  550 +### Aggregations
551 551  
552   -[Facets](http://www.elasticsearch.org/guide/reference/api/search/facets/) provide aggregated search data.
  552 +[Aggregations](http://www.elasticsearch.org/guide/reference/api/search/facets/) provide aggregated search data.
553 553  
554   -![Facets](http://ankane.github.io/searchkick/facets.png)
  554 +![Aggregations](http://ankane.github.io/searchkick/facets.png)
  555 +
  556 +```ruby
  557 +products = Product.search "chuck taylor", aggs: [:product_type, :gender, :brand]
  558 +products.aggs
  559 +```
  560 +
  561 +By default, `where` conditions apply to aggregations.
  562 +
  563 +```ruby
  564 +Product.search "wingtips", where: {color: "brandy"}, aggs: [:size]
  565 +# aggregations for brandy wingtips are returned
  566 +```
  567 +
  568 +Change this with:
  569 +
  570 +```ruby
  571 +Product.search "wingtips", where: {color: "brandy"}, aggs: [:size], smart_aggs: false
  572 +# aggregations for all wingtips are returned
  573 +```
  574 +
  575 +Set `where` conditions for each aggregation separately with:
  576 +
  577 +```ruby
  578 +Product.search "wingtips", aggs: {size: {where: {color: "brandy"}}}
  579 +```
  580 +
  581 +Limit
  582 +
  583 +```ruby
  584 +Product.search "apples", aggs: {store_id: {limit: 10}}
  585 +```
  586 +
  587 +#### Moving From Facets
  588 +
  589 +1. Replace `facets` with `aggs` in searches. **Note:** Range and stats facets are not supported at this time.
  590 +
  591 + ```ruby
  592 + products = Product.search "chuck taylor", facets: [:brand]
  593 + # to
  594 + products = Product.search "chuck taylor", aggs: [:brand]
  595 + ```
  596 +
  597 +2. Replace the `facets` method with `aggs` for results.
  598 +
  599 + ```ruby
  600 + products.facets
  601 + # to
  602 + products.aggs
  603 + ```
  604 +
  605 + The keys in results differ slightly. Instead of:
  606 +
  607 + ```json
  608 + {
  609 + "_type":"terms",
  610 + "missing":0,
  611 + "total":45,
  612 + "other":34,
  613 + "terms":[
  614 + {"term":14.0,"count":11}
  615 + ]
  616 + }
  617 + ```
  618 +
  619 + You get:
  620 +
  621 + ```json
  622 + {
  623 + "doc_count":45,
  624 + "doc_count_error_upper_bound":0,
  625 + "sum_other_doc_count":34,
  626 + "buckets":[
  627 + {"key":14.0,"doc_count":11}
  628 + ]
  629 + }
  630 + ```
  631 +
  632 + Update your application to handle this.
  633 +
  634 +3. By default, `where` conditions apply to aggregations. This is equivalent to `smart_facets: true`. If you have `smart_facets: true`, you can remove it. If this is not desired, set `smart_aggs: false`.
  635 +
  636 +### Facets [deprecated]
  637 +
  638 +Facets have been deprecated in favor of aggregations as of Searchkick 0.9.2. See [how to upgrade](#moving-from-facets).
555 639  
556 640 ```ruby
557 641 products = Product.search "chuck taylor", facets: [:product_type, :gender, :brand]
... ... @@ -693,6 +777,8 @@ City.search "san", boost_by_distance: {field: :location, origin: [37, -122], fun
693 777  
694 778 Searchkick supports [Elasticsearch’s routing feature](https://www.elastic.co/blog/customizing-your-document-routing).
695 779  
  780 +**Note:** Routing is not yet supported for Elasticsearch 2.0.
  781 +
696 782 ```ruby
697 783 class Contact < ActiveRecord::Base
698 784 searchkick routing: :user_id
... ...
lib/searchkick/query.rb
... ... @@ -354,9 +354,9 @@ module Searchkick
354 354 }
355 355 }
356 356  
357   - agg_options.deep_merge!(where: options.fetch(:where, {}).reject { |k| k == field }) if options[:smart_aggs] == true
358   - agg_filters = where_filters(agg_options[:where])
359   -
  357 + where = {}
  358 + where = (options[:where] || {}).reject { |k| k == field } unless options[:smart_aggs] == false
  359 + agg_filters = where_filters(where.merge(agg_options[:where] || {}))
360 360 if agg_filters.any?
361 361 payload[:aggs][field] = {
362 362 filter: {
... ...
test/aggs_test.rb
... ... @@ -32,34 +32,34 @@ class AggsTest &lt; Minitest::Test
32 32 assert_equal(1, agg["sum_other_doc_count"]) if Gem::Version.new(Searchkick.server_version) >= Gem::Version.new("1.4.0")
33 33 end
34 34  
35   - def test_where_no_smart_aggs
36   - assert_equal ({2 => 2}), store_agg(where: {color: "red"}, aggs: {store_id: {where: {in_stock: false}}})
37   - assert_equal ({2 => 2}), store_agg(where: {color: "blue"}, aggs: {store_id: {where: {in_stock: false}}})
  35 + def test_query_where
  36 + assert_equal ({1 => 1}), store_agg(where: {in_stock: true}, aggs: [:store_id])
38 37 end
39 38  
40   - def test_smart_aggs
41   - assert_equal ({1 => 1}), store_agg(where: {in_stock: true}, aggs: [:store_id], smart_aggs: true)
  39 + def test_two_wheres
  40 + assert_equal ({2 => 1}), store_agg(where: {color: "red"}, aggs: {store_id: {where: {in_stock: false}}})
42 41 end
43 42  
44   - def test_smart_aggs_where
45   - assert_equal ({2 => 1}), store_agg(where: {color: "red"}, aggs: {store_id: {where: {in_stock: false}}}, smart_aggs: true)
  43 + def test_where_override
  44 + assert_equal ({}), store_agg(where: {color: "red"}, aggs: {store_id: {where: {in_stock: false, color: "blue"}}})
  45 + assert_equal ({2 => 1}), store_agg(where: {color: "blue"}, aggs: {store_id: {where: {in_stock: false, color: "red"}}})
46 46 end
47 47  
48   - def test_smart_aggs_where_override
49   - assert_equal ({2 => 1}), store_agg(where: {color: "red"}, aggs: {store_id: {where: {in_stock: false, color: "blue"}}}, smart_aggs: true)
50   - assert_equal ({}), store_agg(where: {color: "blue"}, aggs: {store_id: {where: {in_stock: false, color: "red"}}}, smart_aggs: true)
  48 + def test_skip
  49 + assert_equal ({1 => 1, 2 => 2}), store_agg(where: {store_id: 2}, aggs: [:store_id])
51 50 end
52 51  
53   - def test_smart_aggs_skip_agg
54   - assert_equal ({1 => 1, 2 => 2}), store_agg(where: {store_id: 2}, aggs: [:store_id], smart_aggs: true)
  52 + def test_skip_complex
  53 + assert_equal ({1 => 1, 2 => 1}), store_agg(where: {store_id: 2, price: {gt: 5}}, aggs: [:store_id])
55 54 end
56 55  
57   - def test_smart_aggs_skip_agg_complex
58   - assert_equal ({1 => 1, 2 => 1}), store_agg(where: {store_id: 2, price: {gt: 5}}, aggs: [:store_id], smart_aggs: true)
  56 + def test_multiple
  57 + assert_equal ({"store_id" => {1 => 1, 2 => 2}, "color" => {"blue" => 1, "green" => 1, "red" => 1}}), store_multiple_aggs(aggs: [:store_id, :color])
59 58 end
60 59  
61   - def test_multiple_aggs
62   - assert_equal ({"store_id" => {1 => 1, 2 => 2}, "color" => {"blue" => 1, "green" => 1, "red" => 1}}), store_multiple_aggs(aggs: [:store_id, :color])
  60 + def test_smart_aggs_false
  61 + assert_equal ({2 => 2}), store_agg(where: {color: "red"}, aggs: {store_id: {where: {in_stock: false}}}, smart_aggs: false)
  62 + assert_equal ({2 => 2}), store_agg(where: {color: "blue"}, aggs: {store_id: {where: {in_stock: false}}}, smart_aggs: false)
63 63 end
64 64  
65 65 protected
... ...