scalastic 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8d4a081eb653e2378d6a39f4f77cd51f44b553b0
4
+ data.tar.gz: c38641e0fe62a596f2d631a7edbfc6c4276785b7
5
+ SHA512:
6
+ metadata.gz: a46a54dd509265a69b621b5596bd21c23b3d766f1d4c479faa28436e69b5650bb133dba236316860eb8ae48cca864fdf9b1f06b1d20f28bfb898162287cefb03
7
+ data.tar.gz: 252794ac6ea2ccb301636a90953e8a31ed2a3186ea079ba1c4894ffbc4ea86ddf41075e89f3e37d49a527ef7f5c05992c40bd46e86f4cc9e694d06a5920b330d
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
4
+ - 1.9.3
5
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in scalastic.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Alex Baturytski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # Scalastic
2
+
3
+ Scalastic is a library that organizes your Elasticsearch documents into partitions, as recommended [here](https://www.elastic.co/guide/en/elasticsearch/guide/current/faking-it.html). A partition provides methods for indexing documents (adding or updating), searching for them, and deleting them. Partitions are isolated: documents stored in one partition are not visible in other partitions.
4
+
5
+ Initially your partition lives in a single index; however, as it grows, you may extend it to another index, redirecting all new documents there and keeping all documents from the original index available for searches. Partitions use Elasticsearch's filtered aliases; a partition with id 1 will create alias "scalastic_1_index" for indexing, and "scalastic_1_search" for searching. The index alias always points to a single index; the search one may span across multiple indices, with the most recent ones at the top.
6
+
7
+ Scalastic relies on the field "scalastic_partition_id" of type "long" to determine partition the document belongs to. The search alias for a partition has a term filter that returns only document belonging to that partition. The index alias, however, does not have a filter; if you're inserting documents into that alias directly (and not using Scalastic API), it becomes your responsibility to set this field to the correct value (which is partition id) for your document to show up in the partition. Note that delete API uses search alias to locate and delete documents to allow deletion of documents created in older indices.
8
+
9
+ ## Configuring the environment for Scalastic
10
+ Every new index must be prepared before it can be used with scalastic:
11
+
12
+ ```ruby
13
+ es_client = Elasticsearch::Client.new
14
+ es_client.indices.create index: 'my_index'
15
+ es_client.partitions.prepare index: 'my_index'
16
+ ```
17
+
18
+ Preparing an index creates mappings for fields required by Scalastic. Note that preparing uses the _default_ mapping, which means that already existing document types will not be affected. Because of that **you should always prepare new indices before using them**.
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'scalastic'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ Or install it yourself as:
33
+
34
+ $ gem install scalastic
35
+
36
+ ## Usage
37
+
38
+ Scalastic extends functionality of the Elasticsearch client by adding a property called "partitions" to the Elasticsearch client. That property serves as a gateway to all partitions' functionality.
39
+
40
+ ### Creating a partition
41
+ ```ruby
42
+ # Connect to Elasticsearch and get the partitions client
43
+ es_client = Elasticsearch::Client.new
44
+ partitions = es_client.partitions
45
+
46
+ # Create an index for the test.
47
+ es_client.indices.create index: 'create_partition_test'
48
+ partitions.prepare_index index: 'create_partition_test' # Must be called once per each index
49
+
50
+ # Create a partition
51
+ partition = partitions.create index: 'create_partition_test', id: 1
52
+ raise 'Partition was not created' unless partition.exists?
53
+ ```
54
+
55
+ ### Listing all existing partitions
56
+ ```ruby
57
+ # Set everything up
58
+ client = Elasticsearch::Client.new
59
+ client.indices.create index: 'list_partitions_test'
60
+ partitions = client.partitions
61
+ partitions.prepare_index index: 'list_partitions_test' # Must be called once per each index
62
+
63
+ # Create a couple of partitions
64
+ partitions.create index: 'list_partitions_test', id: 1
65
+ partitions.create index: 'list_partitions_test', id: 2
66
+ partitions.create index: 'list_partitions_test', id: 3
67
+
68
+ # List all partitions
69
+ partitions.to_a.each{|p| puts "Found partition #{p.id}"}
70
+ ```
71
+
72
+ ### Deleting a partition
73
+ ```ruby
74
+ # Connect to Elasticsearch and create an index
75
+ client = Elasticsearch::Client.new
76
+ partitions = client.partitions
77
+ client.indices.create index: 'delete_partition_test'
78
+ partitions.prepare_index index: 'delete_partition_test'
79
+
80
+ # Create partitions
81
+ partitions.create index: 'delete_partition_test', id: 1
82
+ partitions.create index: 'delete_partition_test', id: 2
83
+ partitions.create index: 'delete_partition_test', id: 3
84
+
85
+ # Delete one of the partitions
86
+ partitions.delete id: 2
87
+ raise "Partition still exists" if partitions[2].exists?
88
+ ```
89
+
90
+ ### Extending partition to another index
91
+ ```ruby
92
+ # Connect to Elasticsearch and set up indices
93
+ client = Elasticsearch::Client.new
94
+ partitions = client.partitions
95
+ client.indices.create index: 'extend_partition_1'
96
+ partitions.prepare_index index: 'extend_partition_1'
97
+ client.indices.create index: 'extend_partition_2'
98
+ partitions.prepare_index index: 'extend_partition_2'
99
+
100
+ # Create a partition residing in extend_partition_1
101
+ partition = partitions.create(index: 'extend_partition_1', id: 1)
102
+
103
+ # Extend partition to index extend_partition_2. Now search will be performed in both indices, but
104
+ # all new documents will be indexex into extend_partition_2.
105
+ partition.extend_to(index: 'extend_partition_2')
106
+ ```
107
+
108
+ ### Operating documents inside partitions
109
+ ```ruby
110
+ client = Elasticsearch::Client.new
111
+ partitions = client.partitions
112
+
113
+ client.indices.create index: 'partition_index'
114
+ partitions.prepare_index index: 'partition_index'
115
+ partition1 = partitions.create(index: 'partition_index', id: 1)
116
+ partition2 = partitions.create(index: 'partition_index', id: 2)
117
+
118
+ partition1.index id: 1, type: 'document', body: {subject: 'Subject 1'}
119
+ partition1.index id: 2, type: 'document', body: {subject: 'Subject 2'}
120
+
121
+ # Partition 2 should have no documents
122
+ count = partition2.search(search_type: 'count', body: {query: {match_all: {}}})['hits']['total']
123
+ raise 'Partition 2 is not empty!' unless count == 0
124
+
125
+ # Partiton 1 should contain everything we just indexed
126
+ hits = partition1.search(type: 'document', body: {query:{match_all: {}}})['hits']['hits']
127
+ raise "Expected 2 documents, got #{hits.size}" unless hits.size == 2
128
+ h1 = hits.find{|h| h['_id'].to_i == 1}
129
+ raise 'Document 1 cannot be found' unless h1
130
+ raise 'Invalid scalastic_partition_id' unless h1['_source']['scalastic_partition_id'] == 1
131
+
132
+ # Now delete something from partition 1
133
+ partition1.delete type: 'document', id: 1
134
+ count = partition1.search(search_type: 'count', body: {query: {match_all: {}}})['hits']['total']
135
+ raise "Expected 1 document, got #{count}" unless count == 1
136
+ ```
137
+
138
+ ### Notes
139
+ * 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.
140
+ * All hash keys in arguments must be symbols; using anything else may result in unexpected behavior.
141
+
142
+
143
+ ## Development
144
+
145
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
146
+
147
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
148
+
149
+ ## Contributing
150
+
151
+ Bug reports and pull requests are welcome on GitHub at https://github.com/aliakb/scalastic.
152
+
153
+
154
+ ## License
155
+
156
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "scalastic"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,9 @@
1
+ module Elasticsearch
2
+ module Transport
3
+ class Client
4
+ def partitions
5
+ @partitions_client ||= Scalastic::PartitionsClient.new(self, Scalastic::Config.default)
6
+ end
7
+ end
8
+ end
9
+ end
data/lib/scalastic.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'scalastic/version'
2
+ require 'scalastic/config'
3
+ require 'scalastic/partitions_client'
4
+
5
+ require 'elasticsearch/transport_client'
6
+
7
+ module Scalastic
8
+ # Your code goes here...
9
+ end
@@ -0,0 +1,37 @@
1
+ module Scalastic
2
+ class Config
3
+ attr_reader(:partition_prefix)
4
+ attr_reader(:partition_selector)
5
+
6
+ def self.default
7
+ @default ||= new
8
+ end
9
+
10
+ def initialize
11
+ @partition_prefix = "scalastic"
12
+ @partition_selector = "scalastic_partition_id"
13
+ end
14
+
15
+ def index_endpoint(partition_id)
16
+ "#{partition_prefix}_#{partition_id}_index"
17
+ end
18
+
19
+ def search_endpoint(partition_id)
20
+ "#{partition_prefix}_#{partition_id}_search"
21
+ end
22
+
23
+ def get_partition_id(alias_name)
24
+ m = partition_regex.match(alias_name)
25
+ m && m[1].to_i
26
+ end
27
+
28
+ private
29
+
30
+ def partition_regex
31
+ @partition_regex ||= begin
32
+ escaped_prefix = Regexp.escape(partition_prefix)
33
+ Regexp.new("^#{escaped_prefix}_(\\w+)_(index|search)$")
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module Scalastic
2
+ module EsActionsGenerator
3
+ extend self
4
+
5
+ def new_search_alias(config, args)
6
+ index = args[:index] || raise(ArgumentError, "Missing required argument :index")
7
+ id = args[:id] || raise(ArgumentError, "Missing required argument :id")
8
+ routing = args[:routing]
9
+ {index: index, alias: config.search_endpoint(id), filter: {term: {config.partition_selector => id}}}.tap do |op|
10
+ op.merge!(routing: routing) if routing
11
+ end
12
+ end
13
+
14
+ def new_index_alias(config, args)
15
+ index = args[:index] || raise(ArgumentError, "Missing required argument :index")
16
+ id = args[:id] || raise(ArgumentError, "Missing required argument :id")
17
+ routing = args[:routing]
18
+ {index: index, alias: config.index_endpoint(id)}.tap do |op|
19
+ op.merge!(routing: routing) if routing
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,60 @@
1
+ require 'scalastic/es_actions_generator'
2
+
3
+ module Scalastic
4
+ class Partition
5
+ attr_reader(:es_client)
6
+ attr_reader(:config)
7
+ attr_reader(:id)
8
+
9
+ def initialize(es_client, config, id)
10
+ raise(ArgumentError, 'ES client is nil!') if es_client.nil?
11
+ raise(ArgumentError, 'config is nil!') if config.nil?
12
+ raise(ArgumentError, 'id is empty!') if id.nil? || id.to_s.empty?
13
+
14
+ @es_client = es_client
15
+ @config = config
16
+ @id = id
17
+ end
18
+
19
+ def extend_to(args)
20
+ index = args[:index]
21
+ raise(ArgumentError, 'Missing required argument :index') if index.nil? || index.to_s.empty?
22
+
23
+ index_alias = config.index_endpoint(id)
24
+ indices = es_client.indices.get_aliases(name: index_alias).select{|i, d| d['aliases'].any?}.keys
25
+ actions = indices.map{|i| {remove: {index: i, alias: index_alias}}}
26
+ actions << {add: EsActionsGenerator.new_index_alias(config, args.merge(id: id))}
27
+ actions << {add: EsActionsGenerator.new_search_alias(config, args.merge(id: id))}
28
+ es_client.indices.update_aliases(body: {actions: actions})
29
+ end
30
+
31
+ def search(args = {})
32
+ args = args.merge(index: config.search_endpoint(id))
33
+ es_client.search(args)
34
+ end
35
+
36
+ def index(args)
37
+ args = {body: {}}.merge(args)
38
+ args[:body][config.partition_selector.to_sym] = id
39
+ args = args.merge(index: config.index_endpoint(id))
40
+ es_client.index(args)
41
+ end
42
+
43
+ def delete(args = {})
44
+ args = args.merge(index: config.search_endpoint(id))
45
+ es_client.delete(args)
46
+ end
47
+
48
+ def exists?
49
+ names = [config.search_endpoint(id), config.index_endpoint(id)]
50
+ all_aliases = es_client.indices.get_aliases name: names.join(',')
51
+ all_aliases.any?{|_index, data| data['aliases'].any?}
52
+ end
53
+
54
+ #TODO: add bulk
55
+
56
+ def inspect
57
+ "ES partition #{id}"
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,53 @@
1
+ require "scalastic/partition"
2
+ require "scalastic/es_actions_generator"
3
+
4
+ module Scalastic
5
+ class PartitionsClient
6
+ attr_reader(:es_client)
7
+ attr_reader(:config)
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?
12
+ @es_client = es_client
13
+ @config = config
14
+ end
15
+
16
+ def create(args = {})
17
+ actions = [
18
+ {add: EsActionsGenerator.new_search_alias(config, args)},
19
+ {add: EsActionsGenerator.new_index_alias(config, args)},
20
+ ]
21
+ es_client.indices.update_aliases(body: {actions: actions})
22
+ self[args[:id]]
23
+ end
24
+
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)
28
+ unless pairs.any?
29
+ #TODO: log a warning
30
+ return
31
+ end
32
+ actions = pairs.map{|i, a| {remove: {index: i, alias: a}}}
33
+ es_client.indices.update_aliases(body: {actions: actions})
34
+ end
35
+
36
+ def [](id)
37
+ Partition.new(es_client, config, id)
38
+ end
39
+
40
+ def to_a
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
43
+ partition_ids.map{|id| Partition.new(es_client, config, id)}
44
+ end
45
+
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})
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Scalastic
2
+ VERSION = "0.1.0"
3
+ end
data/scalastic.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'scalastic/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "scalastic"
8
+ spec.version = Scalastic::VERSION
9
+ spec.authors = ["Aliaksei Baturytski"]
10
+ spec.email = ["abaturytski@gmail.com"]
11
+
12
+ spec.summary = "Elasticsearch document partitions"
13
+ spec.description = "Elasticsearch alias-based partitions for scalable indexing and searching"
14
+ spec.homepage = "https://github.com/aliakb/scalastic"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.10"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.4"
25
+ spec.add_development_dependency "simplecov", "~> 0.11"
26
+
27
+ spec.add_dependency "elasticsearch", "~> 1.0"
28
+ spec.add_dependency "multi_json", "~> 1.0"
29
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scalastic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Aliaksei Baturytski
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-01-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.11'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: elasticsearch
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: multi_json
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.0'
97
+ description: Elasticsearch alias-based partitions for scalable indexing and searching
98
+ email:
99
+ - abaturytski@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".travis.yml"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bin/console
111
+ - bin/setup
112
+ - lib/elasticsearch/transport_client.rb
113
+ - lib/scalastic.rb
114
+ - lib/scalastic/config.rb
115
+ - lib/scalastic/es_actions_generator.rb
116
+ - lib/scalastic/partition.rb
117
+ - lib/scalastic/partitions_client.rb
118
+ - lib/scalastic/version.rb
119
+ - scalastic.gemspec
120
+ homepage: https://github.com/aliakb/scalastic
121
+ licenses:
122
+ - MIT
123
+ metadata: {}
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubyforge_project:
140
+ rubygems_version: 2.4.5
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: Elasticsearch document partitions
144
+ test_files: []