scalastic 0.4.0 → 0.5.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 +4 -4
- data/README.md +17 -0
- data/Rakefile +2 -0
- data/lib/elasticsearch/transport_client.rb +1 -1
- data/lib/scalastic/config.rb +26 -2
- data/lib/scalastic/partition.rb +9 -19
- data/lib/scalastic/partition_selector.rb +14 -0
- data/lib/scalastic/partitions_client.rb +10 -10
- data/lib/scalastic/version.rb +1 -1
- data/lib/scalastic.rb +0 -1
- data/regression/regression.rake +16 -0
- data/regression/regression_tests/bulk_operations.rb +71 -0
- data/regression/regression_tests/create_partition.rb +25 -0
- data/regression/regression_tests/custom_partition_prefix.rb +28 -0
- data/regression/regression_tests/delete_by_query.rb +31 -0
- data/regression/regression_tests/delete_partition.rb +30 -0
- data/regression/regression_tests/document_get.rb +34 -0
- data/regression/regression_tests/extend_partition.rb +29 -0
- data/regression/regression_tests/list_partitions.rb +35 -0
- data/regression/regression_tests/nested_selector.rb +43 -0
- data/regression/regression_tests/nested_selector_bulk.rb +85 -0
- data/regression/regression_tests/partition_operations.rb +45 -0
- data/regression/regression_tests/string_selector_field.rb +32 -0
- data/regression/regression_tests.rb +15 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c137698c420bc7867a4dbf8caf041372c7c0ba5
|
4
|
+
data.tar.gz: d1a06b6c6fbf723fe018ba57a450b6cf446d1455
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 141ecd5e0ae057f91fb67a374e58dae1a9dee830fc8697560506458ea2aa509b24fac08d34210a7453056a091519aa62cee7b1a0a99b0ac46debe7afd13856d8
|
7
|
+
data.tar.gz: aee055ffa89bfe275214bae37f02feddb0366dfc17e465fc366723f9095d61d7ae24c5ed19852bb0dcb831261c91ad401736703dc6938b6d34a3ad3ba9683939
|
data/README.md
CHANGED
@@ -238,6 +238,23 @@ actual_hits = p.search['hits']['hits']
|
|
238
238
|
raise "Unexpected results!: #{actual_hits}" unless actual_hits == expected_hits
|
239
239
|
```
|
240
240
|
|
241
|
+
### Customization
|
242
|
+
Scalastic lets you choose prefix for partition aliases (the default value is `scalastic`):
|
243
|
+
```ruby
|
244
|
+
partitions.config.partition_prefix = 'my_partition'
|
245
|
+
```
|
246
|
+
This makes scalastic create aliases 'my_partition_123_index' and 'my_partition_123_search' for a partition 123.
|
247
|
+
|
248
|
+
Also, you can customize the field that should be used for storing partition ids:
|
249
|
+
```ruby
|
250
|
+
partitions.config.partition_selector = 'my_partition_id'
|
251
|
+
```
|
252
|
+
OR
|
253
|
+
```ruby
|
254
|
+
partitions.config.partition_selector = 'metadata.partition_id'
|
255
|
+
```
|
256
|
+
(note that the period in the partition selector indicates a nested field inside an object)
|
257
|
+
|
241
258
|
### Notes
|
242
259
|
* Indices must be *prepared* before they can be used by Scalastic by calling "prepare" on the partitions client; doing so will create critical field mappings. Each index must be prepared only once.
|
243
260
|
* All hash keys in arguments must be symbols; using anything else may result in unexpected behavior.
|
data/Rakefile
CHANGED
data/lib/scalastic/config.rb
CHANGED
@@ -2,14 +2,16 @@ module Scalastic
|
|
2
2
|
class Config
|
3
3
|
attr_reader(:partition_prefix)
|
4
4
|
attr_reader(:partition_selector)
|
5
|
+
attr_reader(:partition_selector_type)
|
5
6
|
|
6
7
|
def self.default
|
7
8
|
@default ||= new
|
8
9
|
end
|
9
10
|
|
10
11
|
def initialize
|
11
|
-
@partition_prefix =
|
12
|
-
@partition_selector =
|
12
|
+
@partition_prefix = 'scalastic'
|
13
|
+
@partition_selector = 'scalastic_partition_id'
|
14
|
+
@partition_selector_type = 'long'
|
13
15
|
end
|
14
16
|
|
15
17
|
def index_endpoint(partition_id)
|
@@ -25,6 +27,28 @@ module Scalastic
|
|
25
27
|
m && m[1].to_i
|
26
28
|
end
|
27
29
|
|
30
|
+
def partition_prefix=(value)
|
31
|
+
raise(ArgumentError, 'Empty partition prefix') if value.nil? || value.empty?
|
32
|
+
@partition_prefix = value
|
33
|
+
end
|
34
|
+
|
35
|
+
def partition_selector=(value)
|
36
|
+
raise(ArgumentError, 'Empty partition selector') if value.nil? || value.empty?
|
37
|
+
@partition_selector = value
|
38
|
+
end
|
39
|
+
|
40
|
+
def partition_selector_type=(value)
|
41
|
+
value = value.to_s
|
42
|
+
raise(ArgumentError, "Unsupported selector type: #{value}. Supported types are: (string, long)") unless %w(string long).include?(value)
|
43
|
+
@partition_selector_type = value
|
44
|
+
end
|
45
|
+
|
46
|
+
def partition_selector_mapping
|
47
|
+
parts = partition_selector.to_s.split('.').reverse
|
48
|
+
field = parts.shift
|
49
|
+
parts.reduce(field => {type: partition_selector_type}){|acc, p| {p => {type: 'object', properties: acc}}}
|
50
|
+
end
|
51
|
+
|
28
52
|
private
|
29
53
|
|
30
54
|
def partition_regex
|
data/lib/scalastic/partition.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'scalastic/es_actions_generator'
|
2
|
+
require 'scalastic/partition_selector'
|
2
3
|
|
3
4
|
module Scalastic
|
4
5
|
class Partition
|
@@ -39,8 +40,8 @@ module Scalastic
|
|
39
40
|
end
|
40
41
|
|
41
42
|
def index(args)
|
42
|
-
args
|
43
|
-
args[:body]
|
43
|
+
args[:body] ||= {}
|
44
|
+
selector.apply_to(args[:body])
|
44
45
|
args = args.merge(index: config.index_endpoint(id))
|
45
46
|
es_client.index(args)
|
46
47
|
end
|
@@ -58,8 +59,6 @@ module Scalastic
|
|
58
59
|
|
59
60
|
def bulk(args)
|
60
61
|
body = args.clone[:body] || raise(ArgumentError, 'Missing required argument :body')
|
61
|
-
index = config.index_endpoint(id)
|
62
|
-
selector = config.partition_selector
|
63
62
|
|
64
63
|
new_ops = body.map{|entry| [operation_name(entry), entry]}.reduce([]){|acc, op| acc << [op.first, update_entry(acc, *op)]; acc}
|
65
64
|
args[:body] = new_ops.map{|_op, entry| entry}
|
@@ -93,25 +92,12 @@ module Scalastic
|
|
93
92
|
if (operation)
|
94
93
|
op_data = entry[operation]
|
95
94
|
op_data[:_index] = config.index_endpoint(id)
|
96
|
-
|
97
|
-
if (operation == :update)
|
98
|
-
op_data[:data][:doc] ||= {}
|
99
|
-
op_data[:data][:doc][config.partition_selector] = id
|
100
|
-
else
|
101
|
-
op_data[:data][config.partition_selector] = id
|
102
|
-
end
|
103
|
-
end
|
95
|
+
selector.apply_to(op_data[:data]) if op_data.has_key?(:data) && [:index, :create].include?(operation)
|
104
96
|
else
|
105
97
|
parent = acc.last
|
106
98
|
# A previous record must be create/index/update/delete
|
107
99
|
raise(ArgumentError, "Unexpected entry: #{entry}") unless parent && parent.first
|
108
|
-
|
109
|
-
if (parent.first == :update)
|
110
|
-
entry[:doc] ||= {}
|
111
|
-
entry[:doc][config.partition_selector] = id
|
112
|
-
else
|
113
|
-
entry[config.partition_selector] = id
|
114
|
-
end
|
100
|
+
selector.apply_to(entry) if [:index, :create].include?(parent.first)
|
115
101
|
end
|
116
102
|
entry
|
117
103
|
end
|
@@ -119,5 +105,9 @@ module Scalastic
|
|
119
105
|
def delete_op(hit)
|
120
106
|
{delete: {_index: hit['_index'], _type: hit['_type'], _id: hit['_id']}}
|
121
107
|
end
|
108
|
+
|
109
|
+
def selector
|
110
|
+
@selector ||= PartitionSelector.new(config.partition_selector, id)
|
111
|
+
end
|
122
112
|
end
|
123
113
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Scalastic
|
2
|
+
class PartitionSelector
|
3
|
+
def initialize(full_field_name, id)
|
4
|
+
@objects = full_field_name.split('.').map{|p| p.to_sym}
|
5
|
+
@field = @objects.pop
|
6
|
+
@id = id
|
7
|
+
end
|
8
|
+
|
9
|
+
def apply_to(document_body)
|
10
|
+
@objects.reduce(document_body){|body, obj| body[obj] ||= {}}[@field] = @id
|
11
|
+
document_body
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -6,9 +6,9 @@ module Scalastic
|
|
6
6
|
attr_reader(:es_client)
|
7
7
|
attr_reader(:config)
|
8
8
|
|
9
|
-
def initialize(es_client, config = Config.default)
|
10
|
-
raise(ArgumentError,
|
11
|
-
raise(ArgumentError,
|
9
|
+
def initialize(es_client, config = Config.default.dup)
|
10
|
+
raise(ArgumentError, 'ES client is nil') if es_client.nil?
|
11
|
+
raise(ArgumentError, 'Config is nil') if config.nil?
|
12
12
|
@es_client = es_client
|
13
13
|
@config = config
|
14
14
|
end
|
@@ -23,8 +23,8 @@ module Scalastic
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def delete(args = {})
|
26
|
-
id = args[:id] || raise(ArgumentError,
|
27
|
-
pairs = es_client.indices.get_aliases.map{|i, d| d[
|
26
|
+
id = args[:id] || raise(ArgumentError, 'Missing required argument :id')
|
27
|
+
pairs = es_client.indices.get_aliases.map{|i, d| d['aliases'].keys.select{|a| config.get_partition_id(a) == id}.map{|a| [i, a]}}.flatten(1)
|
28
28
|
unless pairs.any?
|
29
29
|
#TODO: log a warning
|
30
30
|
return
|
@@ -39,15 +39,15 @@ module Scalastic
|
|
39
39
|
|
40
40
|
def to_a
|
41
41
|
aliases = es_client.indices.get_aliases
|
42
|
-
partition_ids = aliases.map{|_, data| data[
|
42
|
+
partition_ids = aliases.map{|_, data| data['aliases'].keys}.flatten.map{|a| config.get_partition_id(a)}.compact.uniq
|
43
43
|
partition_ids.map{|id| Partition.new(es_client, config, id)}
|
44
44
|
end
|
45
45
|
|
46
46
|
def prepare_index(args)
|
47
|
-
index = args[:index] || raise(ArgumentError,
|
48
|
-
mapping = {properties:
|
49
|
-
es_client.indices.put_mapping(index: index, type:
|
50
|
-
es_client.indices.put_mapping(index: index, type:
|
47
|
+
index = args[:index] || raise(ArgumentError, 'Missing required argument :index')
|
48
|
+
mapping = {properties: config.partition_selector_mapping}
|
49
|
+
es_client.indices.put_mapping(index: index, type: '_default_', body: {'_default_' => mapping})
|
50
|
+
es_client.indices.put_mapping(index: index, type: 'scalastic', body: {'scalastic' => mapping})
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
data/lib/scalastic/version.rb
CHANGED
data/lib/scalastic.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'regression_tests'
|
2
|
+
|
3
|
+
namespace :regression do
|
4
|
+
task :run do
|
5
|
+
RegressionTests.to_a.shuffle.each do |t|
|
6
|
+
puts "Running #{t.name}"
|
7
|
+
t.cleanup
|
8
|
+
t.run
|
9
|
+
t.cleanup
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
task :cleanup do
|
14
|
+
RegressionTests.each{|t| t.cleanup}
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module BulkOperations
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'bulk_operations' if client.indices.exists? index: 'bulk_operations'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
client = Elasticsearch::Client.new
|
12
|
+
partitions = client.partitions
|
13
|
+
|
14
|
+
client.indices.create(index: 'bulk_operations')
|
15
|
+
partitions.prepare_index(index: 'bulk_operations')
|
16
|
+
|
17
|
+
partition = partitions.create(index: 'bulk_operations', id: 1)
|
18
|
+
sleep 1.5
|
19
|
+
|
20
|
+
partition.bulk(body: [
|
21
|
+
{index: {_type: 'test', _id: 1, data: {subject: 'test1'}}},
|
22
|
+
{create: {_type: 'test', _id: 2, data: {subject: 'test2'}}}
|
23
|
+
])
|
24
|
+
|
25
|
+
partition.bulk(body: [
|
26
|
+
{index: {_type: 'test', _id: 3}},
|
27
|
+
{subject: 'test3'},
|
28
|
+
{create: {_type: 'test', _id: 4}},
|
29
|
+
{subject: 'test4'}
|
30
|
+
])
|
31
|
+
|
32
|
+
partition.bulk(body: [
|
33
|
+
{update: {_type: 'test', _id: 1, data: {doc: {body: 'Document 1'}}}},
|
34
|
+
{update: {_type: 'test', _id: 2, data: {doc: {body: 'Document 2'}}}}
|
35
|
+
])
|
36
|
+
|
37
|
+
partition.bulk(body: [
|
38
|
+
{update: {_type: 'test', _id: 3}},
|
39
|
+
{doc: {body: 'Document 3'}},
|
40
|
+
{update: {_type: 'test', _id: 4}},
|
41
|
+
{doc: {body: 'Document 4'}}
|
42
|
+
])
|
43
|
+
|
44
|
+
sleep 1.5
|
45
|
+
|
46
|
+
hits = partition.search['hits']['hits'].sort{|h1, h2| h1['_id'].to_i <=> h2['_id'].to_i}
|
47
|
+
raise "Expected 4 hits, got #{hits.size}" unless hits.size == 4
|
48
|
+
|
49
|
+
expected_hits = [
|
50
|
+
{'_index' => 'bulk_operations', '_type' => 'test', '_id' => '1', '_score' => 1.0, '_source' => {'subject' => 'test1', 'body' => 'Document 1', 'scalastic_partition_id' => 1}},
|
51
|
+
{'_index' => 'bulk_operations', '_type' => 'test', '_id' => '2', '_score' => 1.0, '_source' => {'subject' => 'test2', 'body' => 'Document 2', 'scalastic_partition_id' => 1}},
|
52
|
+
{'_index' => 'bulk_operations', '_type' => 'test', '_id' => '3', '_score' => 1.0, '_source' => {'subject' => 'test3', 'body' => 'Document 3', 'scalastic_partition_id' => 1}},
|
53
|
+
{'_index' => 'bulk_operations', '_type' => 'test', '_id' => '4', '_score' => 1.0, '_source' => {'subject' => 'test4', 'body' => 'Document 4', 'scalastic_partition_id' => 1}},
|
54
|
+
]
|
55
|
+
|
56
|
+
raise 'Unexpected results' unless hits == expected_hits
|
57
|
+
|
58
|
+
res = partition.bulk(body: [
|
59
|
+
{delete: {_type: 'test', _id: 1}},
|
60
|
+
{delete: {_type: 'test', _id: 2}},
|
61
|
+
{delete: {_type: 'test', _id: 3}},
|
62
|
+
{delete: {_type: 'test', _id: 4}},
|
63
|
+
])
|
64
|
+
|
65
|
+
sleep 1.5
|
66
|
+
|
67
|
+
count = partition.search(search_type: 'count')['hits']['total']
|
68
|
+
raise 'Some documents were not removed' unless count == 0
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module CreatePartition
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'create_partition' if client.indices.exists? index: 'create_partition'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
# Connect to Elasticsearch and get the partitions client
|
12
|
+
es_client = Elasticsearch::Client.new
|
13
|
+
partitions = es_client.partitions
|
14
|
+
|
15
|
+
# Create an index for the test.
|
16
|
+
es_client.indices.create index: 'create_partition'
|
17
|
+
partitions.prepare_index index: 'create_partition' # Needs to be called only once per index
|
18
|
+
|
19
|
+
# Create a partition
|
20
|
+
partition = partitions.create index: 'create_partition', id: 1
|
21
|
+
sleep 1.5
|
22
|
+
raise 'Partition was not created' unless partition.exists?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module CustomPartitionPrefix
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'custom_partition_prefix' if client.indices.exists? index: 'custom_partition_prefix'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
client = Elasticsearch::Client.new
|
12
|
+
partitions = client.partitions
|
13
|
+
|
14
|
+
partitions.config.partition_prefix = 'custom'
|
15
|
+
|
16
|
+
client.indices.create index: 'custom_partition_prefix'
|
17
|
+
partitions.prepare_index index: 'custom_partition_prefix' # Must be called only once per index
|
18
|
+
|
19
|
+
partition = partitions.create index: 'custom_partition_prefix', id: 1
|
20
|
+
sleep 1.5
|
21
|
+
|
22
|
+
# Are aliases there?
|
23
|
+
aliases = client.indices.get_aliases index: 'custom_partition_prefix'
|
24
|
+
expected_aliases = {"custom_partition_prefix"=>{"aliases"=>{"custom_1_index"=>{}, "custom_1_search"=>{"filter"=>{"term"=>{"scalastic_partition_id"=>1}}}}}}
|
25
|
+
raise "Expected: #{expected_aliases}; got: #{aliases}" unless expected_aliases == aliases
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module DeleteByQuery
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'delete_by_query' if client.indices.exists? index: 'delete_by_query'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
client = Elasticsearch::Client.new
|
12
|
+
partitions = client.partitions
|
13
|
+
|
14
|
+
client.indices.create(index: 'delete_by_query')
|
15
|
+
partitions.prepare_index(index: 'delete_by_query')
|
16
|
+
|
17
|
+
p = partitions.create(index: 'delete_by_query', id: 1)
|
18
|
+
p.index(id: 1, type: 'test')
|
19
|
+
p.index(id: 2, type: 'test')
|
20
|
+
p.index(id: 3, type: 'test')
|
21
|
+
sleep 1.5
|
22
|
+
|
23
|
+
p.delete_by_query(body:{query:{terms:{_id: [1,3]}}})
|
24
|
+
sleep 1.5
|
25
|
+
|
26
|
+
expected_hits = [{'_index' => 'delete_by_query', '_type' => 'test', '_id' => '2', '_score' => 1.0, '_source' => {'scalastic_partition_id' => 1}}]
|
27
|
+
actual_hits = p.search['hits']['hits']
|
28
|
+
raise "Unexpected results!: #{actual_hits}" unless actual_hits == expected_hits
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module DeletePartition
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'delete_partition' if client.indices.exists? index: 'delete_partition'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
# Connect to Elasticsearch and create an index
|
12
|
+
client = Elasticsearch::Client.new
|
13
|
+
partitions = client.partitions
|
14
|
+
client.indices.create index: 'delete_partition'
|
15
|
+
partitions.prepare_index index: 'delete_partition'
|
16
|
+
|
17
|
+
# Create partitions
|
18
|
+
partitions.create index: 'delete_partition', id: 1
|
19
|
+
partitions.create index: 'delete_partition', id: 2
|
20
|
+
partitions.create index: 'delete_partition', id: 3
|
21
|
+
sleep 1.5
|
22
|
+
|
23
|
+
# Delete one of the partitions
|
24
|
+
partitions.delete id: 2
|
25
|
+
sleep 1.5
|
26
|
+
|
27
|
+
raise 'Partition still exists' if partitions[2].exists?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module DocumentGet
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'document_get' if client.indices.exists? index: 'document_get'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
client = Elasticsearch::Client.new
|
12
|
+
client.indices.create(index: 'document_get')
|
13
|
+
client.partitions.prepare_index(index: 'document_get')
|
14
|
+
|
15
|
+
p = client.partitions.create(id: 1, index: 'document_get')
|
16
|
+
p.index(id: 1, type: 'test', body: {title: 'Test'})
|
17
|
+
|
18
|
+
sleep 1.5
|
19
|
+
|
20
|
+
res = p.get(id: 1, type: 'test')['_source']
|
21
|
+
expected = {'title' => 'Test', 'scalastic_partition_id' => 1}
|
22
|
+
raise "Expected: #{expected}, got: #{res}" unless res == expected
|
23
|
+
|
24
|
+
p = client.partitions.create(id: 2, index: 'document_get', routing: 12345)
|
25
|
+
p.index(id: 2, type: 'test', body: {title: 'Routing test'})
|
26
|
+
|
27
|
+
sleep 1.5
|
28
|
+
|
29
|
+
res = p.get(id: 2, type: 'test')['_source']
|
30
|
+
expected = {'title' => 'Routing test', 'scalastic_partition_id' => 2}
|
31
|
+
raise "Expected: #{expected}, got: #{res}" unless res == expected
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module ExtendPartition
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
%w(extend_partition_1 extend_partition_2).each do |i|
|
8
|
+
client.indices.delete index: i if client.indices.exists? index: i
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
# Connect to Elasticsearch and set up indices
|
14
|
+
client = Elasticsearch::Client.new
|
15
|
+
partitions = client.partitions
|
16
|
+
client.indices.create index: 'extend_partition_1'
|
17
|
+
partitions.prepare_index index: 'extend_partition_1'
|
18
|
+
client.indices.create index: 'extend_partition_2'
|
19
|
+
partitions.prepare_index index: 'extend_partition_2'
|
20
|
+
|
21
|
+
# Create a partition residing in extend_partition_1
|
22
|
+
partition = partitions.create(index: 'extend_partition_1', id: 1)
|
23
|
+
|
24
|
+
# Extend partition to index extend_partition_2. Now search will be performed in both indices, but
|
25
|
+
# all new documents will be indexex into extend_partition_2.
|
26
|
+
partition.extend_to(index: 'extend_partition_2')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module ListPartitions
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'list_partitions' if client.indices.exists? index: 'list_partitions'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
# Set everything up
|
12
|
+
client = Elasticsearch::Client.new
|
13
|
+
client.indices.create index: 'list_partitions'
|
14
|
+
partitions = client.partitions
|
15
|
+
partitions.prepare_index index: 'list_partitions' # Must be called once per each index
|
16
|
+
|
17
|
+
sleep 1.5
|
18
|
+
|
19
|
+
# Create a couple of partitions
|
20
|
+
partitions.create index: 'list_partitions', id: 1
|
21
|
+
partitions.create index: 'list_partitions', id: 2
|
22
|
+
partitions.create index: 'list_partitions', id: 3
|
23
|
+
|
24
|
+
partitions[1].index type: 'test', body: {title: 'In partition 1'}
|
25
|
+
partitions[2].index type: 'test', body: {title: 'In partition 2'}
|
26
|
+
partitions[3].index type: 'test', body: {title: 'In partition 3'}
|
27
|
+
|
28
|
+
sleep 1.5
|
29
|
+
|
30
|
+
# List all partitions
|
31
|
+
ids = partitions.to_a.map{|p| p.id}
|
32
|
+
raise "Unexpected partitions: #{ids}" unless ids.sort == [1,2,3]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module NestedSelector
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'nested_selector' if client.indices.exists? index: 'nested_selector'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
client = Elasticsearch::Client.new
|
12
|
+
partitions = client.partitions
|
13
|
+
|
14
|
+
# Create an index for testing
|
15
|
+
client.indices.create index: 'nested_selector'
|
16
|
+
|
17
|
+
# Prepare the environment
|
18
|
+
partitions.config.partition_selector = 'parent.child.partition_id'
|
19
|
+
partitions.prepare_index index: 'nested_selector' # Must be called only once for the index
|
20
|
+
|
21
|
+
# Indexing should work
|
22
|
+
partition = partitions.create index: 'nested_selector', id: 1
|
23
|
+
partition.index type: 'document', id: 1, body: {title: 'Test', content: 'This is a test'}
|
24
|
+
|
25
|
+
sleep 1.5
|
26
|
+
|
27
|
+
# Searching should work, too
|
28
|
+
results = partition.search(type: 'document', body: {query: {match_all: {}}})
|
29
|
+
count = results['hits']['total']
|
30
|
+
raise "Expected 1 result, got #{count}" unless count == 1
|
31
|
+
|
32
|
+
hit = results['hits']['hits'].first['_source']
|
33
|
+
expected = {'title'=>'Test', 'content'=>'This is a test', 'parent'=>{'child'=>{'partition_id'=>1}}}
|
34
|
+
raise "Expected: #{expected}, got: #{hit}" unless expected == hit
|
35
|
+
|
36
|
+
# Deleting should work
|
37
|
+
partition.delete type: 'document', id: 1
|
38
|
+
sleep 1.5
|
39
|
+
count = partition.search(search_type: 'count', body: {query: {match_all: {}}})['hits']['total']
|
40
|
+
raise "Expected 0 records, got #{count}" unless count == 0
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module NestedSelectorBulk
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'nested_selector_bulk' if client.indices.exists? index: 'nested_selector_bulk'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
client = Elasticsearch::Client.new
|
12
|
+
|
13
|
+
# Set up test environment
|
14
|
+
client.indices.create index: 'nested_selector_bulk'
|
15
|
+
partitions = client.partitions
|
16
|
+
partitions.config.partition_selector = 'parent.child.partition_id'
|
17
|
+
partitions.prepare_index index: 'nested_selector_bulk' # Set up field mapping
|
18
|
+
|
19
|
+
# Create partition
|
20
|
+
partition = partitions.create index: 'nested_selector_bulk', id: 1
|
21
|
+
|
22
|
+
# Index documents
|
23
|
+
partition.bulk body: [
|
24
|
+
{index: {_type: 'test', _id: 1, data: {subject: 'Test 1'}}},
|
25
|
+
{create: {_type: 'test', _id: 2, data: {subject: 'Test 2'}}},
|
26
|
+
]
|
27
|
+
|
28
|
+
partition.bulk body: [
|
29
|
+
{index: {_type: 'test', _id: 3}},
|
30
|
+
{subject: 'Test 3'},
|
31
|
+
{create: {_type: 'test', _id: 4}},
|
32
|
+
{subject: 'Test 4'}
|
33
|
+
]
|
34
|
+
|
35
|
+
sleep 1.5
|
36
|
+
|
37
|
+
hits = partition.search(body: {query: {match_all: {}}})['hits']['hits'].sort{|h1, h2| h1['_id'] <=> h2['_id']}
|
38
|
+
expected_hits = [
|
39
|
+
{'_index'=>'nested_selector_bulk', '_type' => 'test', '_id' => '1', '_score' => 1.0, '_source' => {'subject' => 'Test 1', 'parent' => {'child' => {'partition_id' => 1}}}},
|
40
|
+
{'_index'=>'nested_selector_bulk', '_type' => 'test', '_id' => '2', '_score' => 1.0, '_source' => {'subject' => 'Test 2', 'parent' => {'child' => {'partition_id' => 1}}}},
|
41
|
+
{'_index'=>'nested_selector_bulk', '_type' => 'test', '_id' => '3', '_score' => 1.0, '_source' => {'subject' => 'Test 3', 'parent' => {'child' => {'partition_id' => 1}}}},
|
42
|
+
{'_index'=>'nested_selector_bulk', '_type' => 'test', '_id' => '4', '_score' => 1.0, '_source' => {'subject' => 'Test 4', 'parent' => {'child' => {'partition_id' => 1}}}},
|
43
|
+
]
|
44
|
+
raise "Expected: #{expected_hits}, got: #{hits}" unless expected_hits == hits
|
45
|
+
|
46
|
+
# Update documents
|
47
|
+
partition.bulk body: [
|
48
|
+
{update: {_type: 'test', _id: 1, data: {doc: {subject: 'Test 1a'}}}},
|
49
|
+
{update: {_type: 'test', _id: 2, data: {doc: {subject: 'Test 2a'}}}},
|
50
|
+
{update: {_type: 'test', _id: 3}},
|
51
|
+
{doc: {subject: 'Test 3a'}}
|
52
|
+
]
|
53
|
+
|
54
|
+
partition.bulk body: [
|
55
|
+
{update: {_type: 'test', _id: 3}},
|
56
|
+
{doc: {subject: 'Test 3a'}},
|
57
|
+
{update: {_type: 'test', _id: 4}},
|
58
|
+
{doc: {subject: 'Test 4a'}}
|
59
|
+
]
|
60
|
+
|
61
|
+
sleep 1.5
|
62
|
+
|
63
|
+
hits = partition.search(body: {query: {match_all: {}}})['hits']['hits'].sort{|h1, h2| h1['_id'] <=> h2['_id']}
|
64
|
+
expected_hits = [
|
65
|
+
{'_index'=>'nested_selector_bulk', '_type' => 'test', '_id' => '1', '_score' => 1.0, '_source' => {'subject' => 'Test 1a', 'parent' => {'child' => {'partition_id' => 1}}}},
|
66
|
+
{'_index'=>'nested_selector_bulk', '_type' => 'test', '_id' => '2', '_score' => 1.0, '_source' => {'subject' => 'Test 2a', 'parent' => {'child' => {'partition_id' => 1}}}},
|
67
|
+
{'_index'=>'nested_selector_bulk', '_type' => 'test', '_id' => '3', '_score' => 1.0, '_source' => {'subject' => 'Test 3a', 'parent' => {'child' => {'partition_id' => 1}}}},
|
68
|
+
{'_index'=>'nested_selector_bulk', '_type' => 'test', '_id' => '4', '_score' => 1.0, '_source' => {'subject' => 'Test 4a', 'parent' => {'child' => {'partition_id' => 1}}}},
|
69
|
+
]
|
70
|
+
raise "Expected: #{expected_hits}, got: #{hits}" unless expected_hits == hits
|
71
|
+
|
72
|
+
partition.bulk body: [
|
73
|
+
{delete: {_type: 'test', _id: 1}},
|
74
|
+
{delete: {_type: 'test', _id: 2}},
|
75
|
+
{delete: {_type: 'test', _id: 3}},
|
76
|
+
{delete: {_type: 'test', _id: 4}},
|
77
|
+
]
|
78
|
+
|
79
|
+
sleep 1.5
|
80
|
+
|
81
|
+
hits = partition.search['hits']['hits']
|
82
|
+
raise "Expected no hits, got: #{hits}" unless hits.empty?
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module PartitionOperations
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'partition_operations' if client.indices.exists? index: 'partition_operations'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
client = Elasticsearch::Client.new
|
12
|
+
partitions = client.partitions
|
13
|
+
|
14
|
+
client.indices.create index: 'partition_operations'
|
15
|
+
partitions.prepare_index index: 'partition_operations'
|
16
|
+
partition1 = partitions.create(index: 'partition_operations', id: 1)
|
17
|
+
partition2 = partitions.create(index: 'partition_operations', id: 2)
|
18
|
+
|
19
|
+
partition1.index id: 1, type: 'document', body: {subject: 'Subject 1'}
|
20
|
+
partition1.index id: 2, type: 'document', body: {subject: 'Subject 2'}
|
21
|
+
|
22
|
+
sleep 1.5
|
23
|
+
|
24
|
+
# Partition 2 should have no documents
|
25
|
+
count = partition2.search(search_type: 'count', body: {query: {match_all: {}}})['hits']['total']
|
26
|
+
raise 'Partition 2 is not empty!' unless count == 0
|
27
|
+
|
28
|
+
# Partiton 1 should contain everything we just indexed
|
29
|
+
hits = partition1.search(type: 'document', body: {query:{match_all: {}}})['hits']['hits']
|
30
|
+
raise "Expected 2 documents, got #{hits.size}" unless hits.size == 2
|
31
|
+
h1 = hits.find{|h| h['_id'].to_i == 1}
|
32
|
+
raise 'Document 1 cannot be found' unless h1
|
33
|
+
partition_id = h1['_source']['scalastic_partition_id']
|
34
|
+
raise "Expected: 1; got: #{partition_id}" unless partition_id == 1
|
35
|
+
|
36
|
+
# Now delete something from partition 1
|
37
|
+
partition1.delete type: 'document', id: 1
|
38
|
+
|
39
|
+
sleep 1.5
|
40
|
+
|
41
|
+
count = partition1.search(search_type: 'count', body: {query: {match_all: {}}})['hits']['total']
|
42
|
+
raise "Expected 1 document, got #{count}" unless count == 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RegressionTests
|
2
|
+
module StringSelectorField
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def cleanup
|
6
|
+
client = Elasticsearch::Client.new
|
7
|
+
client.indices.delete index: 'string_selector_field' if client.indices.exists? index: 'string_selector_field'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
client = Elasticsearch::Client.new
|
12
|
+
partitions = client.partitions
|
13
|
+
|
14
|
+
client.indices.create index: 'string_selector_field'
|
15
|
+
|
16
|
+
partitions.config.partition_selector_type = 'string'
|
17
|
+
partitions.prepare_index index: 'string_selector_field'
|
18
|
+
|
19
|
+
partition = partitions.create index: 'string_selector_field', id: 'favorites'
|
20
|
+
partition.index type: 'license', body: {title: 'This is a test'}
|
21
|
+
|
22
|
+
sleep 1.5
|
23
|
+
|
24
|
+
hits = partition.search(type: 'license')['hits']['hits']
|
25
|
+
raise "Expected 1 result, got #{hits.size}" unless hits.size == 1
|
26
|
+
|
27
|
+
expected = {'title'=>'This is a test', 'scalastic_partition_id'=>'favorites'}
|
28
|
+
actual = hits.first['_source']
|
29
|
+
raise "Expected: #{expected}, got: #{actual}" unless expected == actual
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'scalastic'
|
2
|
+
require 'elasticsearch'
|
3
|
+
|
4
|
+
module RegressionTests
|
5
|
+
include Enumerable
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def each(&block)
|
9
|
+
Dir.glob('./regression/regression_tests/**.rb').each do |l|
|
10
|
+
load l
|
11
|
+
end
|
12
|
+
|
13
|
+
RegressionTests.constants.map{|c| RegressionTests.const_get(c)}.select{|c| c.is_a?(Module) && c.respond_to?(:run) && c.respond_to?(:cleanup)}.each(&block)
|
14
|
+
end
|
15
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scalastic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aliaksei Baturytski
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,8 +100,23 @@ files:
|
|
100
100
|
- lib/scalastic/config.rb
|
101
101
|
- lib/scalastic/es_actions_generator.rb
|
102
102
|
- lib/scalastic/partition.rb
|
103
|
+
- lib/scalastic/partition_selector.rb
|
103
104
|
- lib/scalastic/partitions_client.rb
|
104
105
|
- lib/scalastic/version.rb
|
106
|
+
- regression/regression.rake
|
107
|
+
- regression/regression_tests.rb
|
108
|
+
- regression/regression_tests/bulk_operations.rb
|
109
|
+
- regression/regression_tests/create_partition.rb
|
110
|
+
- regression/regression_tests/custom_partition_prefix.rb
|
111
|
+
- regression/regression_tests/delete_by_query.rb
|
112
|
+
- regression/regression_tests/delete_partition.rb
|
113
|
+
- regression/regression_tests/document_get.rb
|
114
|
+
- regression/regression_tests/extend_partition.rb
|
115
|
+
- regression/regression_tests/list_partitions.rb
|
116
|
+
- regression/regression_tests/nested_selector.rb
|
117
|
+
- regression/regression_tests/nested_selector_bulk.rb
|
118
|
+
- regression/regression_tests/partition_operations.rb
|
119
|
+
- regression/regression_tests/string_selector_field.rb
|
105
120
|
- scalastic.gemspec
|
106
121
|
homepage: https://github.com/aliakb/scalastic
|
107
122
|
licenses:
|