searchkick-sinneduy 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +28 -0
  4. data/CHANGELOG.md +272 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +1109 -0
  8. data/Rakefile +8 -0
  9. data/ci/before_install.sh +14 -0
  10. data/gemfiles/activerecord31.gemfile +7 -0
  11. data/gemfiles/activerecord32.gemfile +7 -0
  12. data/gemfiles/activerecord40.gemfile +8 -0
  13. data/gemfiles/activerecord41.gemfile +8 -0
  14. data/gemfiles/mongoid2.gemfile +7 -0
  15. data/gemfiles/mongoid3.gemfile +6 -0
  16. data/gemfiles/mongoid4.gemfile +7 -0
  17. data/gemfiles/nobrainer.gemfile +6 -0
  18. data/lib/searchkick.rb +72 -0
  19. data/lib/searchkick/index.rb +550 -0
  20. data/lib/searchkick/logging.rb +136 -0
  21. data/lib/searchkick/model.rb +102 -0
  22. data/lib/searchkick/query.rb +567 -0
  23. data/lib/searchkick/reindex_job.rb +28 -0
  24. data/lib/searchkick/reindex_v2_job.rb +24 -0
  25. data/lib/searchkick/results.rb +158 -0
  26. data/lib/searchkick/tasks.rb +35 -0
  27. data/lib/searchkick/version.rb +3 -0
  28. data/searchkick.gemspec +28 -0
  29. data/test/autocomplete_test.rb +67 -0
  30. data/test/boost_test.rb +126 -0
  31. data/test/facets_test.rb +91 -0
  32. data/test/highlight_test.rb +58 -0
  33. data/test/index_test.rb +119 -0
  34. data/test/inheritance_test.rb +80 -0
  35. data/test/match_test.rb +163 -0
  36. data/test/model_test.rb +38 -0
  37. data/test/query_test.rb +14 -0
  38. data/test/reindex_job_test.rb +33 -0
  39. data/test/reindex_v2_job_test.rb +34 -0
  40. data/test/routing_test.rb +14 -0
  41. data/test/should_index_test.rb +34 -0
  42. data/test/similar_test.rb +20 -0
  43. data/test/sql_test.rb +327 -0
  44. data/test/suggest_test.rb +82 -0
  45. data/test/synonyms_test.rb +50 -0
  46. data/test/test_helper.rb +276 -0
  47. metadata +194 -0
@@ -0,0 +1,91 @@
1
+ require_relative "test_helper"
2
+ require "active_support/core_ext"
3
+
4
+ class TestFacets < Minitest::Test
5
+
6
+ def setup
7
+ super
8
+ store [
9
+ {name: "Product Show", latitude: 37.7833, longitude: 12.4167, store_id: 1, in_stock: true, color: "blue", price: 21, created_at: 2.days.ago},
10
+ {name: "Product Hide", latitude: 29.4167, longitude: -98.5000, store_id: 2, in_stock: false, color: "green", price: 25, created_at: 2.days.from_now},
11
+ {name: "Product B", latitude: 43.9333, longitude: -122.4667, store_id: 2, in_stock: false, color: "red", price: 5},
12
+ {name: "Foo", latitude: 43.9333, longitude: 12.4667, store_id: 3, in_stock: false, color: "yellow", price: 15}
13
+ ]
14
+ end
15
+
16
+ def test_basic
17
+ assert_equal ({1 => 1, 2 => 2}), store_facet(facets: [:store_id])
18
+ end
19
+
20
+ def test_where
21
+ assert_equal ({1 => 1}), store_facet(facets: {store_id: {where: {in_stock: true}}})
22
+ end
23
+
24
+ def test_field
25
+ assert_equal ({1 => 1, 2 => 2}), store_facet(facets: {store_id: {}})
26
+ assert_equal ({1 => 1, 2 => 2}), store_facet(facets: {store_id: {field: "store_id"}})
27
+ assert_equal ({1 => 1, 2 => 2}), store_facet({facets: {store_id_new: {field: "store_id"}}}, "store_id_new")
28
+ end
29
+
30
+ def test_limit
31
+ facet = Product.search("Product", facets: {store_id: {limit: 1}}).facets["store_id"]
32
+ assert_equal 1, facet["terms"].size
33
+ assert_equal 3, facet["total"]
34
+ assert_equal 1, facet["other"]
35
+ end
36
+
37
+ def test_ranges
38
+ price_ranges = [{to: 10}, {from: 10, to: 20}, {from: 20}]
39
+ facet = Product.search("Product", facets: {price: {ranges: price_ranges}}).facets["price"]
40
+
41
+ assert_equal 3, facet["ranges"].size
42
+ assert_equal 10.0, facet["ranges"][0]["to"]
43
+ assert_equal 20.0, facet["ranges"][2]["from"]
44
+ assert_equal 1, facet["ranges"][0]["count"]
45
+ assert_equal 0, facet["ranges"][1]["count"]
46
+ assert_equal 2, facet["ranges"][2]["count"]
47
+ end
48
+
49
+ def test_ranges_dates
50
+ ranges = [{to: 1.day.ago}, {from: 1.day.ago, to: 1.day.from_now}, {from: 1.day.from_now}]
51
+ facet = Product.search("Product", facets: {created_at: {ranges: ranges}}).facets["created_at"]
52
+
53
+ assert_equal 1, facet["ranges"][0]["count"]
54
+ assert_equal 1, facet["ranges"][1]["count"]
55
+ assert_equal 1, facet["ranges"][2]["count"]
56
+ end
57
+
58
+ def test_where_no_smart_facets
59
+ assert_equal ({2 => 2}), store_facet(where: {color: "red"}, facets: {store_id: {where: {in_stock: false}}})
60
+ end
61
+
62
+ def test_smart_facets
63
+ assert_equal ({1 => 1}), store_facet(where: {in_stock: true}, facets: [:store_id], smart_facets: true)
64
+ end
65
+
66
+ def test_smart_facets_where
67
+ assert_equal ({2 => 1}), store_facet(where: {color: "red"}, facets: {store_id: {where: {in_stock: false}}}, smart_facets: true)
68
+ end
69
+
70
+ def test_smart_facets_skip_facet
71
+ assert_equal ({1 => 1, 2 => 2}), store_facet(where: {store_id: 2}, facets: [:store_id], smart_facets: true)
72
+ end
73
+
74
+ def test_smart_facets_skip_facet_complex
75
+ assert_equal ({1 => 1, 2 => 1}), store_facet(where: {store_id: 2, price: {gt: 5}}, facets: [:store_id], smart_facets: true)
76
+ end
77
+
78
+ def test_stats_facets
79
+ options = {where: {store_id: 2}, facets: {store_id: {stats: true}}}
80
+ facets = Product.search("Product", options).facets["store_id"]["terms"]
81
+ expected_facets_keys = %w[term count total_count min max total mean]
82
+ assert_equal expected_facets_keys, facets.first.keys
83
+ end
84
+
85
+ protected
86
+
87
+ def store_facet(options, facet_key="store_id")
88
+ Hash[Product.search("Product", options).facets[facet_key]["terms"].map { |v| [v["term"], v["count"]] }]
89
+ end
90
+
91
+ end
@@ -0,0 +1,58 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestHighlight < Minitest::Test
4
+
5
+ def test_basic
6
+ store_names ["Two Door Cinema Club"]
7
+ assert_equal "Two Door <em>Cinema</em> Club", Product.search("cinema", fields: [:name], highlight: true).with_details.first[1][:highlight][:name]
8
+ end
9
+
10
+ def test_tag
11
+ store_names ["Two Door Cinema Club"]
12
+ assert_equal "Two Door <strong>Cinema</strong> Club", Product.search("cinema", fields: [:name], highlight: {tag: "<strong>"}).with_details.first[1][:highlight][:name]
13
+ end
14
+
15
+ def test_multiple_fields
16
+ store [{name: "Two Door Cinema Club", color: "Cinema Orange"}]
17
+ highlight = Product.search("cinema", fields: [:name, :color], highlight: true).with_details.first[1][:highlight]
18
+ assert_equal "Two Door <em>Cinema</em> Club", highlight[:name]
19
+ assert_equal "<em>Cinema</em> Orange", highlight[:color]
20
+ end
21
+
22
+ def test_fields
23
+ store [{name: "Two Door Cinema Club", color: "Cinema Orange"}]
24
+ highlight = Product.search("cinema", fields: [:name, :color], highlight: {fields: [:name]}).with_details.first[1][:highlight]
25
+ assert_equal "Two Door <em>Cinema</em> Club", highlight[:name]
26
+ assert_equal nil, highlight[:color]
27
+ end
28
+
29
+ def test_field_options
30
+ store_names ["Two Door Cinema Club are a Northern Irish indie rock band"]
31
+ assert_equal "Two Door <em>Cinema</em> Club are", Product.search("cinema", fields: [:name], highlight: {fields: {name: {fragment_size: 20}}}).with_details.first[1][:highlight][:name]
32
+ end
33
+
34
+ def test_multiple_words
35
+ store_names ["Hello World Hello"]
36
+ assert_equal "<em>Hello</em> World <em>Hello</em>", Product.search("hello", fields: [:name], highlight: true).with_details.first[1][:highlight][:name]
37
+ end
38
+
39
+ def test_json
40
+ store_names ["Two Door Cinema Club"]
41
+ json = {
42
+ query: {
43
+ match: {
44
+ _all: "cinema"
45
+ }
46
+ },
47
+ highlight: {
48
+ pre_tags: ["<strong>"],
49
+ post_tags: ["</strong>"],
50
+ fields: {
51
+ "name.analyzed" => {}
52
+ }
53
+ }
54
+ }
55
+ assert_equal "Two Door <strong>Cinema</strong> Club", Product.search(json: json).with_details.first[1][:highlight][:"name.analyzed"]
56
+ end
57
+
58
+ end
@@ -0,0 +1,119 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestIndex < Minitest::Test
4
+
5
+ def test_clean_indices
6
+ old_index = Searchkick::Index.new("products_test_20130801000000000")
7
+ different_index = Searchkick::Index.new("items_test_20130801000000000")
8
+
9
+ old_index.delete if old_index.exists?
10
+ different_index.delete if different_index.exists?
11
+
12
+ # create indexes
13
+ old_index.create
14
+ different_index.create
15
+
16
+ Product.clean_indices
17
+
18
+ assert Product.searchkick_index.exists?
19
+ assert different_index.exists?
20
+ assert !old_index.exists?
21
+ end
22
+
23
+ def test_clean_indices_old_format
24
+ old_index = Searchkick::Index.new("products_test_20130801000000")
25
+ old_index.create
26
+
27
+ Product.clean_indices
28
+
29
+ assert !old_index.exists?
30
+ end
31
+
32
+ def test_mapping
33
+ store_names ["Dollar Tree"], Store
34
+ assert_equal [], Store.search(query: {match: {name: "dollar"}}).map(&:name)
35
+ assert_equal ["Dollar Tree"], Store.search(query: {match: {name: "Dollar Tree"}}).map(&:name)
36
+ end
37
+
38
+ def test_json
39
+ store_names ["Dollar Tree"], Store
40
+ assert_equal [], Store.search(query: {match: {name: "dollar"}}).map(&:name)
41
+ assert_equal ["Dollar Tree"], Store.search(json: {query: {match: {name: "Dollar Tree"}}}, load: false).map(&:name)
42
+ end
43
+
44
+ def test_body
45
+ store_names ["Dollar Tree"], Store
46
+ assert_equal [], Store.search(query: {match: {name: "dollar"}}).map(&:name)
47
+ assert_equal ["Dollar Tree"], Store.search(body: {query: {match: {name: "Dollar Tree"}}}, load: false).map(&:name)
48
+ end
49
+
50
+ def test_block
51
+ store_names ["Dollar Tree"]
52
+ products =
53
+ Product.search "boom" do |body|
54
+ body[:query] = {match_all: {}}
55
+ end
56
+ assert_equal ["Dollar Tree"], products.map(&:name)
57
+ end
58
+
59
+ def test_tokens
60
+ assert_equal ["dollar", "dollartre", "tree"], Product.searchkick_index.tokens("Dollar Tree")
61
+ end
62
+
63
+ def test_tokens_analyzer
64
+ assert_equal ["dollar", "tree"], Product.searchkick_index.tokens("Dollar Tree", analyzer: "searchkick_search2")
65
+ end
66
+
67
+ def test_record_not_found
68
+ store_names ["Product A", "Product B"]
69
+ Product.where(name: "Product A").delete_all
70
+ assert_search "product", ["Product B"]
71
+ ensure
72
+ Product.reindex
73
+ end
74
+
75
+ def test_bad_mapping
76
+ Product.searchkick_index.delete
77
+ store_names ["Product A"]
78
+ assert_raises(Searchkick::InvalidQueryError) { Product.search "test" }
79
+ ensure
80
+ Product.reindex
81
+ end
82
+
83
+ def test_remove_blank_id
84
+ store_names ["Product A"]
85
+ Product.searchkick_index.remove(OpenStruct.new)
86
+ assert_search "product", ["Product A"]
87
+ ensure
88
+ Product.reindex
89
+ end
90
+
91
+ def test_missing_index
92
+ assert_raises(Searchkick::MissingIndexError) { Product.search "test", index_name: "not_found" }
93
+ end
94
+
95
+ def test_unsupported_version
96
+ raises_exception = ->(s) { raise Elasticsearch::Transport::Transport::Error.new("[500] No query registered for [multi_match]") }
97
+ Searchkick.client.stub :search, raises_exception do
98
+ assert_raises(Searchkick::UnsupportedVersionError) { Product.search("test") }
99
+ end
100
+ end
101
+
102
+ def test_invalid_query
103
+ assert_raises(Searchkick::InvalidQueryError) { Product.search(query: {}) }
104
+ end
105
+
106
+ if defined?(ActiveRecord)
107
+
108
+ def test_transaction
109
+ Product.transaction do
110
+ store_names ["Product A"]
111
+ raise ActiveRecord::Rollback
112
+ end
113
+
114
+ assert_search "product", []
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,80 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestInheritance < Minitest::Test
4
+
5
+ def test_child_reindex
6
+ store_names ["Max"], Cat
7
+ assert Dog.reindex
8
+ Animal.searchkick_index.refresh
9
+ assert_equal 1, Animal.search("*").size
10
+ end
11
+
12
+ def test_child_index_name
13
+ assert_equal "animals-#{Date.today.year}", Dog.searchkick_index.name
14
+ end
15
+
16
+ def test_child_search
17
+ store_names ["Bear"], Dog
18
+ store_names ["Bear"], Cat
19
+ assert_equal 1, Dog.search("bear").size
20
+ end
21
+
22
+ def test_parent_search
23
+ store_names ["Bear"], Dog
24
+ store_names ["Bear"], Cat
25
+ assert_equal 2, Animal.search("bear").size
26
+ end
27
+
28
+ def test_force_one_type
29
+ store_names ["Green Bear"], Dog
30
+ store_names ["Blue Bear"], Cat
31
+ assert_equal ["Blue Bear"], Animal.search("bear", type: [Cat]).map(&:name)
32
+ end
33
+
34
+ def test_force_multiple_types
35
+ store_names ["Green Bear"], Dog
36
+ store_names ["Blue Bear"], Cat
37
+ store_names ["Red Bear"], Animal
38
+ assert_equal ["Green Bear", "Blue Bear"], Animal.search("bear", type: [Dog, Cat]).map(&:name)
39
+ end
40
+
41
+ def test_child_autocomplete
42
+ store_names ["Max"], Cat
43
+ store_names ["Mark"], Dog
44
+ assert_equal ["Max"], Cat.search("ma", fields: [:name], autocomplete: true).map(&:name)
45
+ end
46
+
47
+ def test_parent_autocomplete
48
+ store_names ["Max"], Cat
49
+ store_names ["Bear"], Dog
50
+ assert_equal ["Bear"], Animal.search("bea", fields: [:name], autocomplete: true).map(&:name).sort
51
+ end
52
+
53
+ # def test_child_suggest
54
+ # store_names ["Shark"], Cat
55
+ # store_names ["Sharp"], Dog
56
+ # assert_equal ["shark"], Cat.search("shar", fields: [:name], suggest: true).suggestions
57
+ # end
58
+
59
+ def test_parent_suggest
60
+ store_names ["Shark"], Cat
61
+ store_names ["Tiger"], Dog
62
+ assert_equal ["tiger"], Animal.search("tige", fields: [:name], suggest: true).suggestions.sort
63
+ end
64
+
65
+ def test_reindex
66
+ store_names ["Bear A"], Cat
67
+ store_names ["Bear B"], Dog
68
+ Animal.reindex
69
+ assert_equal 1, Dog.search("bear").size
70
+ end
71
+
72
+ # TODO move somewhere better
73
+
74
+ def test_multiple_indices
75
+ store_names ["Product A"]
76
+ store_names ["Product B"], Animal
77
+ assert_search "product", ["Product A", "Product B"], index_name: [Product.searchkick_index.name, Animal.searchkick_index.name], conversions: false
78
+ end
79
+
80
+ end
@@ -0,0 +1,163 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "test_helper"
4
+
5
+ class TestMatch < Minitest::Test
6
+
7
+ # exact
8
+
9
+ def test_match
10
+ store_names ["Whole Milk", "Fat Free Milk", "Milk"]
11
+ assert_search "milk", ["Milk", "Whole Milk", "Fat Free Milk"]
12
+ end
13
+
14
+ def test_case
15
+ store_names ["Whole Milk", "Fat Free Milk", "Milk"]
16
+ assert_search "MILK", ["Milk", "Whole Milk", "Fat Free Milk"]
17
+ end
18
+
19
+ def test_cheese_space_in_index
20
+ store_names ["Pepper Jack Cheese Skewers"]
21
+ assert_search "pepperjack cheese skewers", ["Pepper Jack Cheese Skewers"]
22
+ end
23
+
24
+ # def test_cheese_space_in_query
25
+ # store_names ["Pepperjack Cheese Skewers"]
26
+ # assert_search "pepper jack cheese skewers", ["Pepperjack Cheese Skewers"]
27
+ # end
28
+
29
+ def test_middle_token
30
+ store_names ["Dish Washer Amazing Organic Soap"]
31
+ assert_search "dish soap", ["Dish Washer Amazing Organic Soap"]
32
+ end
33
+
34
+ def test_middle_token_wine
35
+ store_names ["Beringer Wine Founders Estate Chardonnay"]
36
+ assert_search "beringer chardonnay", ["Beringer Wine Founders Estate Chardonnay"]
37
+ end
38
+
39
+ def test_percent
40
+ store_names ["1% Milk", "2% Milk", "Whole Milk"]
41
+ assert_search "1%", ["1% Milk"]
42
+ end
43
+
44
+ # ascii
45
+
46
+ def test_jalapenos
47
+ store_names ["Jalapeño"]
48
+ assert_search "jalapeno", ["Jalapeño"]
49
+ end
50
+
51
+ def test_swedish
52
+ store_names ["ÅÄÖ"]
53
+ assert_search "aao", ["ÅÄÖ"]
54
+ end
55
+
56
+ # stemming
57
+
58
+ def test_stemming
59
+ store_names ["Whole Milk", "Fat Free Milk", "Milk"]
60
+ assert_search "milks", ["Milk", "Whole Milk", "Fat Free Milk"]
61
+ end
62
+
63
+ # fuzzy
64
+
65
+ def test_misspelling_sriracha
66
+ store_names ["Sriracha"]
67
+ assert_search "siracha", ["Sriracha"]
68
+ end
69
+
70
+ def test_misspelling_multiple
71
+ store_names ["Greek Yogurt", "Green Onions"]
72
+ assert_search "greed", ["Greek Yogurt", "Green Onions"]
73
+ end
74
+
75
+ def test_short_word
76
+ store_names ["Finn"]
77
+ assert_search "fin", ["Finn"]
78
+ end
79
+
80
+ def test_edit_distance_two
81
+ store_names ["Bingo"]
82
+ assert_search "bin", []
83
+ assert_search "bingooo", []
84
+ assert_search "mango", []
85
+ end
86
+
87
+ def test_edit_distance_one
88
+ store_names ["Bingo"]
89
+ assert_search "bing", ["Bingo"]
90
+ assert_search "bingoo", ["Bingo"]
91
+ assert_search "ringo", ["Bingo"]
92
+ end
93
+
94
+ def test_edit_distance_long_word
95
+ store_names ["thisisareallylongword"]
96
+ assert_search "thisisareallylongwor", ["thisisareallylongword"] # missing letter
97
+ assert_search "thisisareelylongword", [] # edit distance = 2
98
+ end
99
+
100
+ def test_misspelling_tabasco
101
+ store_names ["Tabasco"]
102
+ assert_search "tobasco", ["Tabasco"]
103
+ end
104
+
105
+ def test_misspelling_zucchini
106
+ store_names ["Zucchini"]
107
+ assert_search "zuchini", ["Zucchini"]
108
+ end
109
+
110
+ def test_misspelling_ziploc
111
+ store_names ["Ziploc"]
112
+ assert_search "zip lock", ["Ziploc"]
113
+ end
114
+
115
+ # spaces
116
+
117
+ def test_spaces_in_field
118
+ store_names ["Red Bull"]
119
+ assert_search "redbull", ["Red Bull"]
120
+ end
121
+
122
+ def test_spaces_in_query
123
+ store_names ["Dishwasher"]
124
+ assert_search "dish washer", ["Dishwasher"]
125
+ end
126
+
127
+ def test_spaces_three_words
128
+ store_names ["Dish Washer Soap", "Dish Washer"]
129
+ assert_search "dish washer soap", ["Dish Washer Soap"]
130
+ end
131
+
132
+ def test_spaces_stemming
133
+ store_names ["Almond Milk"]
134
+ assert_search "almondmilks", ["Almond Milk"]
135
+ end
136
+
137
+ def test_all
138
+ store_names ["Product A", "Product B"]
139
+ assert_search "*", ["Product A", "Product B"]
140
+ end
141
+
142
+ def test_no_arguments
143
+ assert_equal [], Product.search.to_a
144
+ end
145
+
146
+ def test_no_term
147
+ store_names ["Product A"]
148
+ assert_equal ["Product A"], Product.search(where: {name: "Product A"}).map(&:name)
149
+ end
150
+
151
+ def test_to_be_or_not_to_be
152
+ store_names ["to be or not to be"]
153
+ assert_search "to be", ["to be or not to be"]
154
+ end
155
+
156
+ def test_unsearchable
157
+ store [
158
+ {name: "Unsearchable", description: "Almond"}
159
+ ]
160
+ assert_search "almond", []
161
+ end
162
+
163
+ end