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 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: