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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5eaea5d5ea27529e7fd71ac835aa12a20fb4f6f7
4
- data.tar.gz: ab107d4bc5e24cd69d3fe535d540957e4c4e83e5
3
+ metadata.gz: 8c137698c420bc7867a4dbf8caf041372c7c0ba5
4
+ data.tar.gz: d1a06b6c6fbf723fe018ba57a450b6cf446d1455
5
5
  SHA512:
6
- metadata.gz: 2203b940e31523fd84ec2dee701ab3fc5ef46dc9007104592672b6e04dd0515c034ea0e1db249b21159dfc7d70d8c2c074a70fa94320a65ee3d8d02fbada7b2c
7
- data.tar.gz: 80604212d8a574a5b94d208eb6cc9e363e6ad95131acce8dd733dde60168e8e0d4269e0cadbda5ac8e7a899d4e49dc4ba35b7b3de54384aa2a2e611b48abb464
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
@@ -1,5 +1,7 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ import 'regression/regression.rake'
3
4
  RSpec::Core::RakeTask.new(:spec)
4
5
 
5
6
  task :default => :spec
7
+
@@ -2,7 +2,7 @@ module Elasticsearch
2
2
  module Transport
3
3
  class Client
4
4
  def partitions
5
- @partitions_client ||= Scalastic::PartitionsClient.new(self, Scalastic::Config.default)
5
+ @partitions_client ||= Scalastic::PartitionsClient.new(self)
6
6
  end
7
7
  end
8
8
  end
@@ -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 = "scalastic"
12
- @partition_selector = :scalastic_partition_id
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
@@ -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 = {body: {}}.merge(args)
43
- args[:body][config.partition_selector.to_sym] = id
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
- if (op_data[:data])
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, "ES client is nil") if es_client.nil?
11
- raise(ArgumentError, "Config is nil") if config.nil?
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, "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)
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["aliases"].keys}.flatten.map{|a| config.get_partition_id(a)}.compact.uniq
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, "Missing required argument :index")
48
- mapping = {properties: {config.partition_selector => {type: "long"}}}
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})
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
@@ -1,3 +1,3 @@
1
1
  module Scalastic
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/scalastic.rb CHANGED
@@ -5,5 +5,4 @@ require 'scalastic/partitions_client'
5
5
  require 'elasticsearch/transport_client'
6
6
 
7
7
  module Scalastic
8
- # Your code goes here...
9
8
  end
@@ -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.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-02-29 00:00:00.000000000 Z
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: