search_flip 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +34 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +606 -0
- data/Rakefile +9 -0
- data/irb.rb +7 -0
- data/lib/search_flip/aggregatable.rb +69 -0
- data/lib/search_flip/aggregation.rb +57 -0
- data/lib/search_flip/bulk.rb +152 -0
- data/lib/search_flip/config.rb +21 -0
- data/lib/search_flip/criteria.rb +737 -0
- data/lib/search_flip/filterable.rb +240 -0
- data/lib/search_flip/http_client.rb +49 -0
- data/lib/search_flip/index.rb +545 -0
- data/lib/search_flip/json.rb +18 -0
- data/lib/search_flip/model.rb +21 -0
- data/lib/search_flip/post_filterable.rb +252 -0
- data/lib/search_flip/response.rb +319 -0
- data/lib/search_flip/result.rb +12 -0
- data/lib/search_flip/to_json.rb +31 -0
- data/lib/search_flip/version.rb +5 -0
- data/lib/search_flip.rb +82 -0
- data/search_flip.gemspec +35 -0
- data/test/database.yml +4 -0
- data/test/search_flip/aggregation_test.rb +212 -0
- data/test/search_flip/bulk_test.rb +55 -0
- data/test/search_flip/criteria_test.rb +825 -0
- data/test/search_flip/http_client_test.rb +35 -0
- data/test/search_flip/index_test.rb +350 -0
- data/test/search_flip/model_test.rb +39 -0
- data/test/search_flip/response_test.rb +136 -0
- data/test/search_flip/to_json_test.rb +30 -0
- data/test/search_flip_test.rb +26 -0
- data/test/test_helper.rb +243 -0
- metadata +258 -0
data/search_flip.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'search_flip/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "search_flip"
|
8
|
+
spec.version = SearchFlip::VERSION
|
9
|
+
spec.authors = ["Benjamin Vetter"]
|
10
|
+
spec.email = ["vetter@flakks.com"]
|
11
|
+
spec.description = %q{Compositional EasticSearch client library}
|
12
|
+
spec.summary = %q{Powerful ElasticSearch client library to easily build complex queries}
|
13
|
+
spec.homepage = "https://github.com/mrkamel/search_flip"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "minitest"
|
24
|
+
spec.add_development_dependency "mocha"
|
25
|
+
spec.add_development_dependency "factory_girl"
|
26
|
+
spec.add_development_dependency "sqlite3"
|
27
|
+
spec.add_development_dependency "activerecord", ">= 3.0"
|
28
|
+
spec.add_development_dependency "webmock"
|
29
|
+
spec.add_development_dependency "timecop"
|
30
|
+
|
31
|
+
spec.add_dependency "http"
|
32
|
+
spec.add_dependency "hashie"
|
33
|
+
spec.add_dependency "oj"
|
34
|
+
end
|
35
|
+
|
data/test/database.yml
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class SearchFlip::AggregationTest < SearchFlip::TestCase
|
5
|
+
def test_where
|
6
|
+
product1 = create(:product, category: "category1", title: "title", description: "description")
|
7
|
+
product2 = create(:product, category: "category2", title: "title", description: "description")
|
8
|
+
product3 = create(:product, category: "category1", title: "title", description: "description")
|
9
|
+
product4 = create(:product, category: "category2", title: "title", description: "other")
|
10
|
+
product5 = create(:product, category: "category1", title: "other", description: "description")
|
11
|
+
|
12
|
+
ProductIndex.import [product1, product2, product3, product4, product5]
|
13
|
+
|
14
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
15
|
+
aggregation.where(title: "title").where(description: "description").aggregate(:category)
|
16
|
+
end
|
17
|
+
|
18
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_where_with_array
|
22
|
+
product1 = create(:product, category: "category1", title: "title1", description: "description1")
|
23
|
+
product2 = create(:product, category: "category2", title: "title2", description: "description2")
|
24
|
+
product3 = create(:product, category: "category1", title: "title3", description: "description3")
|
25
|
+
product4 = create(:product, category: "category2", title: "title4", description: "other")
|
26
|
+
product5 = create(:product, category: "category1", title: "other", description: "description")
|
27
|
+
|
28
|
+
ProductIndex.import [product1, product2, product3, product4, product5]
|
29
|
+
|
30
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
31
|
+
aggregation.where(title: ["title1", "title2", "title3", "title4"]).where(description: ["description1", "description2", "description3"]).aggregate(:category)
|
32
|
+
end
|
33
|
+
|
34
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_where_with_range
|
38
|
+
product1 = create(:product, category: "category1", title: "title1", price: 100)
|
39
|
+
product2 = create(:product, category: "category2", title: "title2", price: 150)
|
40
|
+
product3 = create(:product, category: "category1", title: "title3", price: 200)
|
41
|
+
product4 = create(:product, category: "category2", title: "title4", price: 250)
|
42
|
+
product5 = create(:product, category: "category1", title: "other", price: 200)
|
43
|
+
|
44
|
+
ProductIndex.import [product1, product2, product3, product4, product5]
|
45
|
+
|
46
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
47
|
+
aggregation.where(title: "title1" .. "title3").where(price: 100 .. 200).aggregate(:category)
|
48
|
+
end
|
49
|
+
|
50
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_where_not
|
54
|
+
product1 = create(:product, category: "category1", title: "title1")
|
55
|
+
product2 = create(:product, category: "category2", title: "title2")
|
56
|
+
product3 = create(:product, category: "category1", title: "title3")
|
57
|
+
product4 = create(:product, category: "category2", title: "title4")
|
58
|
+
product5 = create(:product, category: "category1", title: "title5")
|
59
|
+
|
60
|
+
ProductIndex.import [product1, product2, product3, product4, product5]
|
61
|
+
|
62
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
63
|
+
aggregation.where_not(title: "title4").where_not(title: "title5").aggregate(:category)
|
64
|
+
end
|
65
|
+
|
66
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_where_not_with_array
|
70
|
+
product1 = create(:product, category: "category1", title: "title1")
|
71
|
+
product2 = create(:product, category: "category2", title: "title2")
|
72
|
+
product3 = create(:product, category: "category1", title: "title3")
|
73
|
+
product4 = create(:product, category: "category2", title: "title4")
|
74
|
+
product5 = create(:product, category: "category1", title: "title5")
|
75
|
+
product6 = create(:product, category: "category2", title: "title6")
|
76
|
+
product7 = create(:product, category: "category1", title: "title7")
|
77
|
+
|
78
|
+
ProductIndex.import [product1, product2, product3, product4, product5, product6, product7]
|
79
|
+
|
80
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
81
|
+
aggregation.where_not(title: ["title1", "title2"]).where_not(title: ["title6", "title7"]).aggregate(:category)
|
82
|
+
end
|
83
|
+
|
84
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_where_not_with_range
|
88
|
+
product1 = create(:product, category: "category1", title: "title1", price: 100)
|
89
|
+
product2 = create(:product, category: "category2", title: "title2", price: 150)
|
90
|
+
product3 = create(:product, category: "category1", title: "title3", price: 200)
|
91
|
+
product4 = create(:product, category: "category2", title: "title4", price: 250)
|
92
|
+
product5 = create(:product, category: "category1", title: "title5", price: 300)
|
93
|
+
product6 = create(:product, category: "category2", title: "title6", price: 350)
|
94
|
+
product7 = create(:product, category: "category1", title: "title7", price: 400)
|
95
|
+
|
96
|
+
ProductIndex.import [product1, product2, product3, product4, product5, product6, product7]
|
97
|
+
|
98
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
99
|
+
aggregation.where_not(price: 100 .. 150).where_not(title: "title6" .. "title7").aggregate(:category)
|
100
|
+
end
|
101
|
+
|
102
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_filter
|
106
|
+
product1 = create(:product, category: "category1", title: "title", price: 100)
|
107
|
+
product2 = create(:product, category: "category2", title: "title", price: 150)
|
108
|
+
product3 = create(:product, category: "category1", title: "title", price: 200)
|
109
|
+
product4 = create(:product, category: "category2", title: "other", price: 200)
|
110
|
+
product5 = create(:product, category: "category1", title: "title", price: 250)
|
111
|
+
|
112
|
+
ProductIndex.import [product1, product2, product3, product4, product5]
|
113
|
+
|
114
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
115
|
+
aggregation.filter(range: { price: { gte: 100, lte: 200 }}).filter(term: { title: "title" }).aggregate(:category)
|
116
|
+
end
|
117
|
+
|
118
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_range
|
122
|
+
product1 = create(:product, category: "category1", title: "title1", price: 100)
|
123
|
+
product2 = create(:product, category: "category2", title: "title2", price: 150)
|
124
|
+
product3 = create(:product, category: "category1", title: "title3", price: 200)
|
125
|
+
product4 = create(:product, category: "category2", title: "title4", price: 250)
|
126
|
+
product5 = create(:product, category: "category1", title: "title5", price: 300)
|
127
|
+
product6 = create(:product, category: "category2", title: "title6", price: 350)
|
128
|
+
product7 = create(:product, category: "category1", title: "title7", price: 400)
|
129
|
+
|
130
|
+
ProductIndex.import [product1, product2, product3, product4, product5, product6, product7]
|
131
|
+
|
132
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
133
|
+
aggregation.range(:price, gte: 100, lte: 200).range(:title, gte: "title1", lte: "title3").aggregate(:category)
|
134
|
+
end
|
135
|
+
|
136
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_match_all
|
140
|
+
product1 = create(:product, category: "category1")
|
141
|
+
product2 = create(:product, category: "category2")
|
142
|
+
product3 = create(:product, category: "category1")
|
143
|
+
|
144
|
+
ProductIndex.import [product1, product2, product3]
|
145
|
+
|
146
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
147
|
+
aggregation.match_all.aggregate(:category)
|
148
|
+
end
|
149
|
+
|
150
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_exists
|
154
|
+
product1 = create(:product, category: "category1", title: "title1", price: 10)
|
155
|
+
product2 = create(:product, category: "category2", title: "title2")
|
156
|
+
product3 = create(:product, category: "category1", title: "title3", price: 20)
|
157
|
+
product4 = create(:product, category: "category2", title: "title4", price: 30)
|
158
|
+
product5 = create(:product, category: "category1", price: 40)
|
159
|
+
|
160
|
+
ProductIndex.import [product1, product2, product3, product4, product5]
|
161
|
+
|
162
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
163
|
+
aggregation.exists(:title).exists(:price).aggregate(:category)
|
164
|
+
end
|
165
|
+
|
166
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_exists_not
|
170
|
+
product1 = create(:product, category: "category1")
|
171
|
+
product2 = create(:product, category: "category2", title: "title2")
|
172
|
+
product3 = create(:product, category: "category1")
|
173
|
+
product4 = create(:product, category: "category2")
|
174
|
+
product5 = create(:product, category: "category1", price: 40)
|
175
|
+
|
176
|
+
ProductIndex.import [product1, product2, product3, product4, product5]
|
177
|
+
|
178
|
+
query = ProductIndex.aggregate(category: {}) do |aggregation|
|
179
|
+
aggregation.exists_not(:title).exists_not(:price).aggregate(:category)
|
180
|
+
end
|
181
|
+
|
182
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).category.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_aggregate
|
186
|
+
product1 = create(:product, category: "category1", title: "title1", price: 10)
|
187
|
+
product2 = create(:product, category: "category1", title: "title2", price: 15)
|
188
|
+
product3 = create(:product, category: "category1", title: "title1", price: 20)
|
189
|
+
product4 = create(:product, category: "category2", title: "title2", price: 25)
|
190
|
+
product5 = create(:product, category: "category2", title: "title1", price: 30)
|
191
|
+
product6 = create(:product, category: "category2", title: "title2", price: 35)
|
192
|
+
|
193
|
+
ProductIndex.import [product1, product2, product3, product4, product5, product6]
|
194
|
+
|
195
|
+
query = ProductIndex.aggregate(:category) do |aggregation|
|
196
|
+
aggregation.aggregate(:title) do |_aggregation|
|
197
|
+
_aggregation.aggregate(price: { sum: { field: "price" }})
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
assert_equal Hash["category1" => 3, "category2" => 3], query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
202
|
+
|
203
|
+
assert_equal Hash["title1" => 2, "title2" => 1], query.aggregations(:category)["category1"].title.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
204
|
+
assert_equal Hash["title1" => 1, "title2" => 2], query.aggregations(:category)["category2"].title.buckets.each_with_object({}) { |bucket, hash| hash[bucket[:key]] = bucket.doc_count }
|
205
|
+
|
206
|
+
assert_equal 30, query.aggregations(:category)["category1"].title.buckets.detect { |bucket| bucket[:key] == "title1" }.price.value
|
207
|
+
assert_equal 15, query.aggregations(:category)["category1"].title.buckets.detect { |bucket| bucket[:key] == "title2" }.price.value
|
208
|
+
assert_equal 30, query.aggregations(:category)["category2"].title.buckets.detect { |bucket| bucket[:key] == "title1" }.price.value
|
209
|
+
assert_equal 60, query.aggregations(:category)["category2"].title.buckets.detect { |bucket| bucket[:key] == "title2" }.price.value
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class SearchFlip::BulkTest < SearchFlip::TestCase
|
5
|
+
def test_bulk
|
6
|
+
product1, product2 = create_list(:product, 2)
|
7
|
+
|
8
|
+
assert_difference "ProductIndex.total_entries", 2 do
|
9
|
+
ProductIndex.bulk do |bulk|
|
10
|
+
bulk.create product1.id, ProductIndex.serialize(product1)
|
11
|
+
bulk.create product2.id, ProductIndex.serialize(product1)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
assert_difference "ProductIndex.total_entries", -2 do
|
16
|
+
ProductIndex.bulk do |bulk|
|
17
|
+
bulk.delete product1.id
|
18
|
+
bulk.delete product2.id
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_bulk_with_options
|
24
|
+
product1, product2 = create_list(:product, 2)
|
25
|
+
|
26
|
+
ProductIndex.import [product1, product2]
|
27
|
+
|
28
|
+
assert_raises "SearchFlip::Bulk::Error" do
|
29
|
+
ProductIndex.bulk do |bulk|
|
30
|
+
bulk.create product1.id, ProductIndex.serialize(product1)
|
31
|
+
bulk.create product2.id, ProductIndex.serialize(product1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
ProductIndex.bulk(ignore_errors: [409]) do |bulk|
|
36
|
+
bulk.create product1.id, ProductIndex.serialize(product1)
|
37
|
+
bulk.create product2.id, ProductIndex.serialize(product1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_bulk_with_item_options
|
42
|
+
product = create(:product)
|
43
|
+
|
44
|
+
ProductIndex.bulk do |bulk|
|
45
|
+
bulk.index product.id, ProductIndex.serialize(product), version: 1, version_type: "external_gt"
|
46
|
+
end
|
47
|
+
|
48
|
+
assert_raises "SearchFlip::Bulk::Error" do
|
49
|
+
ProductIndex.bulk do |bulk|
|
50
|
+
bulk.index product.id, ProductIndex.serialize(product), version: 1, version_type: "external_gt"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|