scalastic 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|