mindex 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.dockerignore +1 -0
- data/.gitignore +2 -0
- data/.gitlab-ci.yml +44 -0
- data/.rspec +1 -0
- data/.rubocop.yml +10 -0
- data/.rubocop_todo.yml +28 -0
- data/Dockerfile +17 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +68 -15
- data/Rakefile +8 -1
- data/docker-compose.yml +20 -0
- data/lib/mindex/elasticsearch.rb +35 -0
- data/lib/mindex/index.rb +154 -0
- data/lib/mindex/version.rb +3 -1
- data/lib/mindex.rb +17 -2
- data/mindex.gemspec +22 -14
- data/spec/dummy/index/event.rb +26 -0
- data/spec/lib/mindex/elasticsearch_spec.rb +82 -0
- data/spec/lib/mindex/index_spec.rb +205 -0
- data/spec/lib/mindex_spec.rb +7 -0
- data/spec/spec_helper.rb +36 -0
- metadata +148 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a71d6115d947753187c7da5136c2ca67fbb1131
|
4
|
+
data.tar.gz: 0ec7e8030e636f8305b09d496185e948c950d4bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0535323e08ea36d5897b162b1de6d69e53f08e9bac68c794e70559b175b963ced7d584680cb9153e61e33ec1ad436ae69b823ab619a4f0dba26388a818737d42
|
7
|
+
data.tar.gz: e6eb59a631d55a8aceaca0ab13ded8e092e9ea359dc946a65db69299e6bf7657780ad8915349424485a01a43547f06a1f73d50fa564c46f7e502937d0a6ad190
|
data/.dockerignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
tmp
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
image: "ruby:2.4"
|
2
|
+
|
3
|
+
stages:
|
4
|
+
- test
|
5
|
+
- release
|
6
|
+
|
7
|
+
cache:
|
8
|
+
paths:
|
9
|
+
- vendor/ruby
|
10
|
+
|
11
|
+
before_script:
|
12
|
+
- gem install bundler --no-ri --no-rdoc
|
13
|
+
- bundle install --jobs $(nproc) --path vendor
|
14
|
+
- mkdir -p tmp
|
15
|
+
|
16
|
+
rubocop:
|
17
|
+
stage: test
|
18
|
+
script:
|
19
|
+
- rubocop
|
20
|
+
|
21
|
+
rspec:
|
22
|
+
stage: test
|
23
|
+
services:
|
24
|
+
- name: docker.elastic.co/elasticsearch/elasticsearch:5.6.3
|
25
|
+
alias: elasticsearch
|
26
|
+
variables:
|
27
|
+
ELASTICSEARCH_URL: elasticsearch:9200
|
28
|
+
ELASTICSEARCH_USERNAME: elastic
|
29
|
+
ELASTICSEARCH_PASSWORD: changeme
|
30
|
+
script:
|
31
|
+
- rspec
|
32
|
+
|
33
|
+
release-gem:
|
34
|
+
stage: release
|
35
|
+
only:
|
36
|
+
- tags
|
37
|
+
script:
|
38
|
+
- mkdir -p ~/.gem
|
39
|
+
- 'echo -e "---\n:rubygems_api_key: $RUBYGEMS_API_KEY" > ~/.gem/credentials'
|
40
|
+
- chmod 0600 ~/.gem/credentials
|
41
|
+
- GEM_VERSION=$(echo $CI_COMMIT_TAG|sed 's/[^.0-9]//g')
|
42
|
+
- sed -i -e "s/0.0.0/$GEM_VERSION/g" lib/mindex/version.rb
|
43
|
+
- gem build mindex.gemspec
|
44
|
+
- gem push mindex-$GEM_VERSION.gem -k rubygems
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --format doc --require spec_helper
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2017-10-23 21:48:29 +0000 using RuboCop version 0.51.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 2
|
10
|
+
Metrics/AbcSize:
|
11
|
+
Max: 24
|
12
|
+
|
13
|
+
# Offense count: 3
|
14
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Max: 117
|
17
|
+
|
18
|
+
# Offense count: 1
|
19
|
+
# Configuration parameters: CountComments.
|
20
|
+
Metrics/MethodLength:
|
21
|
+
Max: 21
|
22
|
+
|
23
|
+
# Offense count: 1
|
24
|
+
# Cop supports --auto-correct.
|
25
|
+
# Configuration parameters: AutoCorrect.
|
26
|
+
Performance/TimesMap:
|
27
|
+
Exclude:
|
28
|
+
- 'lib/mindex/index.rb'
|
data/Dockerfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
FROM ruby:latest
|
2
|
+
|
3
|
+
RUN gem update --system && gem install bundler
|
4
|
+
|
5
|
+
WORKDIR /code
|
6
|
+
|
7
|
+
RUN mkdir -p /code/lib/mindex
|
8
|
+
|
9
|
+
COPY Gemfile .
|
10
|
+
COPY mindex.gemspec .
|
11
|
+
COPY lib/mindex/version.rb /code/lib/mindex
|
12
|
+
|
13
|
+
RUN bundle install
|
14
|
+
|
15
|
+
ADD . /code
|
16
|
+
|
17
|
+
CMD ["bundle", "exec", "rspec"]
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,29 +1,82 @@
|
|
1
1
|
# Mindex
|
2
2
|
|
3
|
-
|
3
|
+
This gem provides functionality to build elasticsearch indices.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
Add
|
8
|
-
|
9
|
-
|
7
|
+
Add the following line to your Gemfile:
|
8
|
+
```ruby
|
9
|
+
gem 'mindex'
|
10
|
+
```
|
10
11
|
|
11
12
|
And then execute:
|
13
|
+
```
|
14
|
+
$ bundle
|
15
|
+
```
|
12
16
|
|
13
|
-
|
17
|
+
## Example
|
14
18
|
|
15
|
-
|
19
|
+
### Index declaration
|
20
|
+
```ruby
|
21
|
+
module Index
|
22
|
+
class Event
|
23
|
+
include Mindex::Index
|
16
24
|
|
17
|
-
|
25
|
+
def self.scroll(options = {})
|
26
|
+
query = Event
|
27
|
+
query = query.where('"updated_at" >= ?', options[:started_at]) if options[:started_at]
|
28
|
+
query.find_in_batches do |events|
|
29
|
+
yield index_data(events)
|
30
|
+
end
|
31
|
+
end
|
18
32
|
|
19
|
-
|
33
|
+
def self.fetch(ids)
|
34
|
+
Event.where(id: ids).map do |event|
|
35
|
+
index_data(event)
|
36
|
+
end
|
37
|
+
end
|
20
38
|
|
21
|
-
|
39
|
+
private
|
22
40
|
|
23
|
-
|
41
|
+
def index_data(events)
|
42
|
+
[events].flatten.map do |event|
|
43
|
+
{
|
44
|
+
id: event.id,
|
45
|
+
name: event.name,
|
46
|
+
updated_at: event.updated_at,
|
47
|
+
created_at: event.created_at
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
### Usage
|
56
|
+
recreate index
|
57
|
+
```ruby
|
58
|
+
Event.recreate_index
|
59
|
+
```
|
60
|
+
|
61
|
+
reindex all items
|
62
|
+
```ruby
|
63
|
+
Event.reindex
|
64
|
+
```
|
24
65
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
66
|
+
index specific items
|
67
|
+
```ruby
|
68
|
+
Event.reindex_items([2903, 2016])
|
69
|
+
```
|
70
|
+
|
71
|
+
drop items
|
72
|
+
```ruby
|
73
|
+
Event.drop_items([29, 3])
|
74
|
+
```
|
75
|
+
|
76
|
+
get item from elasticsearch
|
77
|
+
```ruby
|
78
|
+
Event.es.get(index: Event.index_alias, id: 2903)
|
79
|
+
```
|
80
|
+
|
81
|
+
## Contributing
|
82
|
+
Please create an issue or merge request
|
data/Rakefile
CHANGED
data/docker-compose.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
version: '2'
|
2
|
+
|
3
|
+
services:
|
4
|
+
mindex:
|
5
|
+
build: .
|
6
|
+
volumes:
|
7
|
+
- .:/code
|
8
|
+
environment:
|
9
|
+
ELASTICSEARCH_URL: elasticsearch:9200
|
10
|
+
ELASTICSEARCH_USERNAME: elastic
|
11
|
+
ELASTICSEARCH_PASSWORD: changeme
|
12
|
+
depends_on:
|
13
|
+
- elasticsearch
|
14
|
+
- kibana
|
15
|
+
elasticsearch:
|
16
|
+
image: docker.elastic.co/elasticsearch/elasticsearch:5.6.3
|
17
|
+
kibana: # https://www.elastic.co/guide/en/kibana/current/_configuring_kibana_on_docker.html
|
18
|
+
image: docker.elastic.co/kibana/kibana:5.6.3
|
19
|
+
ports:
|
20
|
+
- "5601:5601"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elasticsearch'
|
4
|
+
|
5
|
+
module Mindex
|
6
|
+
class Elasticsearch < Delegator
|
7
|
+
def self.connect(options = {})
|
8
|
+
new({
|
9
|
+
url: Mindex.config.elasticsearch_url,
|
10
|
+
user: Mindex.config.elasticsearch_user,
|
11
|
+
password: Mindex.config.elasticsearch_pass
|
12
|
+
}.merge(Mindex.config.elasticsearch_options || {}).merge(options || {}))
|
13
|
+
end
|
14
|
+
|
15
|
+
def version
|
16
|
+
info['version']['number']
|
17
|
+
end
|
18
|
+
|
19
|
+
def version_gte?(expected_version)
|
20
|
+
Gem::Version.new(version) >= Gem::Version.new(expected_version)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(options)
|
24
|
+
@delegate_sd_obj ||= ::Elasticsearch::Client.new(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def __getobj__
|
28
|
+
@delegate_sd_obj
|
29
|
+
end
|
30
|
+
|
31
|
+
def __setobj__(obj)
|
32
|
+
@delegate_sd_obj = obj
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/mindex/index.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elasticsearch'
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
|
6
|
+
module Mindex
|
7
|
+
module Index
|
8
|
+
module ClassMethods # rubocop:disable Metrics/ModuleLength
|
9
|
+
def connection_settings(options = {})
|
10
|
+
@connection_settings = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def index_config(settings:, mappings:)
|
14
|
+
@index_settings = settings
|
15
|
+
@index_mappings = mappings
|
16
|
+
end
|
17
|
+
|
18
|
+
def index_prefix(prefix)
|
19
|
+
@index_prefix = prefix
|
20
|
+
end
|
21
|
+
|
22
|
+
def index_label(label)
|
23
|
+
@index_label = label
|
24
|
+
end
|
25
|
+
|
26
|
+
def index_num_threads(count)
|
27
|
+
@index_num_threads = count
|
28
|
+
end
|
29
|
+
|
30
|
+
def index_alias
|
31
|
+
[@index_prefix, (@index_label || name.demodulize.tableize)].compact.join('_').underscore
|
32
|
+
end
|
33
|
+
|
34
|
+
def index_name
|
35
|
+
elasticsearch.indices.get_alias(name: index_alias).keys.first
|
36
|
+
rescue ::Elasticsearch::Transport::Transport::Errors::NotFound
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def doc_type
|
41
|
+
@index_label || name.demodulize.tableize
|
42
|
+
end
|
43
|
+
|
44
|
+
def index_refresh
|
45
|
+
elasticsearch.indices.refresh(index: index_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def index_exist?(name = nil)
|
49
|
+
return false if name.nil? && index_name.nil?
|
50
|
+
elasticsearch.indices.exists(index: name || index_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def index_create(move_or_create_index_alias: true)
|
54
|
+
index_name = new_index_name
|
55
|
+
elasticsearch.indices.create(index: index_name, body: { settings: @index_settings || {}, mappings: @index_mappings || {} })
|
56
|
+
add_index_alias(target: index_name) if move_or_create_index_alias
|
57
|
+
index_name
|
58
|
+
end
|
59
|
+
|
60
|
+
def drop_items(ids)
|
61
|
+
bulk_data = [ids].flatten.map { |id| { delete: { _index: index_alias, _type: doc_type, _id: id } } }
|
62
|
+
elasticsearch.indices.client.bulk(body: bulk_data) if bulk_data.any?
|
63
|
+
end
|
64
|
+
|
65
|
+
def reindex_items(ids)
|
66
|
+
index_queue(threads: 1) do |queue|
|
67
|
+
queue << fetch(ids)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def reindex(options = {})
|
72
|
+
index_create unless index_exist?
|
73
|
+
index_queue do |queue|
|
74
|
+
scroll(options) do |items|
|
75
|
+
queue << items
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def recreate_index(options = {})
|
81
|
+
started_at = Time.now
|
82
|
+
index_name = index_create(move_or_create_index_alias: false)
|
83
|
+
index_queue(index: index_name) do |queue|
|
84
|
+
scroll(options) do |items|
|
85
|
+
queue << items
|
86
|
+
end
|
87
|
+
end
|
88
|
+
add_index_alias(target: index_name)
|
89
|
+
reindex(options.merge(started_at: started_at))
|
90
|
+
end
|
91
|
+
|
92
|
+
def elasticsearch
|
93
|
+
Elasticsearch.connect(@connection_settings)
|
94
|
+
end
|
95
|
+
alias es elasticsearch
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def index_queue(index: nil, threads: nil) # rubocop:disable Metrics/AbcSize
|
100
|
+
index ||= index_name
|
101
|
+
num_threads = threads || @index_num_threads || 4
|
102
|
+
queue = SizedQueue.new(num_threads * 2)
|
103
|
+
threads = num_threads.times.map do
|
104
|
+
thread = Thread.new do
|
105
|
+
until (items = queue.pop) == :stop
|
106
|
+
bulk_data = []
|
107
|
+
[items].flatten.each do |item|
|
108
|
+
bulk_data << { index: { _index: index, _type: doc_type, _id: (item[:id] || item['id']), data: item } }
|
109
|
+
end
|
110
|
+
elasticsearch.indices.client.bulk(body: bulk_data) if bulk_data.any?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
thread.abort_on_exception = true
|
114
|
+
thread
|
115
|
+
end
|
116
|
+
|
117
|
+
yield queue
|
118
|
+
|
119
|
+
index
|
120
|
+
ensure
|
121
|
+
num_threads.times { queue << :stop }
|
122
|
+
threads.each(&:join)
|
123
|
+
end
|
124
|
+
|
125
|
+
def new_index_name
|
126
|
+
"#{index_alias}-v#{DateTime.now.strftime('%Q')}" # rubocop:disable Style/DateTime
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_index_alias(target:)
|
130
|
+
actions = [{ add: { index: target, alias: index_alias } }]
|
131
|
+
|
132
|
+
if elasticsearch.indices.exists_alias(name: index_alias)
|
133
|
+
actions += elasticsearch.indices.get_alias(name: index_alias).keys.map do |index_name|
|
134
|
+
{ remove: { index: index_name, alias: index_alias } }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
elasticsearch.indices.update_aliases(body: { actions: actions })
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
module InstanceMethods
|
143
|
+
def elasticsearch
|
144
|
+
@elasticsearch ||= self.class.elasticsearch
|
145
|
+
end
|
146
|
+
alias es elasticsearch
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.included(receiver)
|
150
|
+
receiver.extend ClassMethods
|
151
|
+
receiver.send :include, InstanceMethods
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/mindex/version.rb
CHANGED
data/lib/mindex.rb
CHANGED
@@ -1,5 +1,20 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mindex/version'
|
4
|
+
require 'mindex/elasticsearch'
|
5
|
+
require 'mindex/index'
|
6
|
+
require 'ostruct'
|
2
7
|
|
3
8
|
module Mindex
|
4
|
-
|
9
|
+
class << self
|
10
|
+
attr_writer :config
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.config
|
14
|
+
@config ||= OpenStruct.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.configure
|
18
|
+
yield(config)
|
19
|
+
end
|
5
20
|
end
|
data/mindex.gemspec
CHANGED
@@ -1,23 +1,31 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
$LOAD_PATH.
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
4
4
|
require 'mindex/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'mindex'
|
8
8
|
spec.version = Mindex::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.
|
13
|
-
spec.
|
14
|
-
spec.license = "MIT"
|
9
|
+
spec.authors = ['Michael Voigt']
|
10
|
+
spec.email = ['michael.voigt@spider-network.com']
|
11
|
+
spec.summary = 'Mindex provides functionality to build elasticsearch indices'
|
12
|
+
spec.homepage = 'http://www.spider-network.com'
|
13
|
+
spec.license = 'MIT'
|
15
14
|
|
16
15
|
spec.files = `git ls-files -z`.split("\x0")
|
17
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(
|
19
|
-
spec.require_paths = [
|
17
|
+
spec.test_files = spec.files.grep(%r{^(spec)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_dependency 'activesupport'
|
21
|
+
spec.add_dependency 'elasticsearch'
|
20
22
|
|
21
|
-
spec.add_development_dependency
|
22
|
-
spec.add_development_dependency
|
23
|
+
spec.add_development_dependency 'bundler', '> 1'
|
24
|
+
spec.add_development_dependency 'pry'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
|
+
spec.add_development_dependency 'rspec'
|
27
|
+
spec.add_development_dependency 'rubocop'
|
28
|
+
spec.add_development_dependency 'rubocop-rspec'
|
29
|
+
spec.add_development_dependency 'sequel'
|
30
|
+
spec.add_development_dependency 'sqlite3'
|
23
31
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Index
|
4
|
+
class Event
|
5
|
+
include Mindex::Index
|
6
|
+
|
7
|
+
def self.reset_settings!
|
8
|
+
@connection_settings = nil
|
9
|
+
@index_settings = nil
|
10
|
+
@index_mappings = nil
|
11
|
+
@index_prefix = nil
|
12
|
+
@index_label = nil
|
13
|
+
@index_num_threads = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.scroll(_options = {})
|
17
|
+
DB[:events].select.each_slice(500) do |events|
|
18
|
+
yield events
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.fetch(ids)
|
23
|
+
DB[:events].where(id: ids).all
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Mindex::Elasticsearch do
|
4
|
+
subject(:connect) { described_class.connect }
|
5
|
+
|
6
|
+
describe '.connect' do
|
7
|
+
before do
|
8
|
+
allow(::Elasticsearch::Client).to receive(:new)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'creates a Elasticsearch::Client instance' do
|
12
|
+
connect
|
13
|
+
expect(::Elasticsearch::Client).to have_received(:new)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when elasticsearch_url is set to "foo"' do
|
17
|
+
before { Mindex.config.elasticsearch_url = 'foo' }
|
18
|
+
|
19
|
+
it do
|
20
|
+
connect
|
21
|
+
expect(::Elasticsearch::Client).to have_received(:new).with(hash_including(url: 'foo'))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when elasticsearch_user is set' do
|
26
|
+
before { Mindex.config.elasticsearch_user = 'username' }
|
27
|
+
|
28
|
+
it do
|
29
|
+
connect
|
30
|
+
expect(::Elasticsearch::Client).to have_received(:new).with(hash_including(user: 'username'))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when elasticsearch_pass is set' do
|
35
|
+
before { Mindex.config.elasticsearch_pass = 'password' }
|
36
|
+
|
37
|
+
it do
|
38
|
+
connect
|
39
|
+
expect(::Elasticsearch::Client).to have_received(:new).with(hash_including(password: 'password'))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when elasticsearch_options is set' do
|
44
|
+
before { Mindex.config.elasticsearch_options = { url: 'local', foo: 'bar' } }
|
45
|
+
|
46
|
+
it do
|
47
|
+
connect
|
48
|
+
expect(::Elasticsearch::Client).to have_received(:new).with(hash_including(url: 'local', foo: 'bar'))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '.version' do
|
54
|
+
before do
|
55
|
+
client = instance_double(Elasticsearch::Transport::Client, info: { 'version' => { 'number' => '6.6.6' } })
|
56
|
+
allow(::Elasticsearch::Client).to receive(:new).and_return(client)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns the elasticsearch cluster version' do
|
60
|
+
expect(connect.version).to eq '6.6.6'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '.version_gte' do
|
65
|
+
before do
|
66
|
+
client = instance_double(Elasticsearch::Transport::Client, info: { 'version' => { 'number' => '6.6.6' } })
|
67
|
+
allow(::Elasticsearch::Client).to receive(:new).and_return(client)
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when expected version is smaller' do
|
71
|
+
it { expect(connect.version_gte?('0.90.0')).to be true }
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when expected version is equal' do
|
75
|
+
it { expect(connect.version_gte?('6.6.6')).to be true }
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when expected version is greater' do
|
79
|
+
it { expect(connect.version_gte?('7.0.0')).to be false }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dummy/index/event'
|
4
|
+
|
5
|
+
describe Mindex::Index do # rubocop:disable Metrics/BlockLength
|
6
|
+
let(:index_klass) { Index::Event }
|
7
|
+
|
8
|
+
before do
|
9
|
+
index_klass.reset_settings!
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.index_alias' do
|
13
|
+
it 'returns the alias name of the index' do
|
14
|
+
expect(index_klass.index_alias).to eq 'events'
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when prefix is definde' do
|
18
|
+
let(:prefix) { 'foobar' }
|
19
|
+
before { index_klass.index_prefix(prefix) }
|
20
|
+
|
21
|
+
it 'prefixes the alias name' do
|
22
|
+
expect(index_klass.index_alias).to eq "#{prefix}_events"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when label is definde' do
|
27
|
+
let(:label) { 'competition' }
|
28
|
+
before { index_klass.index_label(label) }
|
29
|
+
|
30
|
+
it 'uses the label instead of the tableize class name' do
|
31
|
+
expect(index_klass.index_alias).to eq label
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '.doc_type' do
|
37
|
+
it 'uses the tableize class name' do
|
38
|
+
expect(index_klass.doc_type).to eq index_klass.name.demodulize.tableize
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when label is definde' do
|
42
|
+
let(:label) { 'competition' }
|
43
|
+
before { index_klass.index_label(label) }
|
44
|
+
|
45
|
+
it 'uses the label instead of the tableize class name' do
|
46
|
+
expect(index_klass.doc_type).to eq label
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '.index_name' do
|
52
|
+
context 'when index does not exists' do
|
53
|
+
it do
|
54
|
+
expect(index_klass.index_name).to be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when index exists' do
|
59
|
+
before do
|
60
|
+
@index_name = index_klass.index_create
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'returns the index name' do
|
64
|
+
expect(index_klass.index_name).to eq @index_name
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '.index_refresh' do
|
70
|
+
context 'when index does not exists' do
|
71
|
+
it do
|
72
|
+
expect { index_klass.index_refresh }.not_to raise_error
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when index exists' do
|
77
|
+
before { index_klass.index_create }
|
78
|
+
|
79
|
+
it do
|
80
|
+
expect { index_klass.index_refresh }.not_to raise_error
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '.index_exist?' do
|
86
|
+
context 'when the index does not exists' do
|
87
|
+
it do
|
88
|
+
expect(index_klass.index_exist?).to be false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when the index exists' do
|
93
|
+
before { index_klass.index_create }
|
94
|
+
|
95
|
+
it do
|
96
|
+
expect(index_klass.index_exist?).to be true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '.connection_settings' do
|
102
|
+
let(:connection_settings) { { foo: :bar, foobar: :baz, qux: :quux } }
|
103
|
+
|
104
|
+
before do
|
105
|
+
allow(Mindex::Elasticsearch).to receive(:connect)
|
106
|
+
index_klass.connection_settings(connection_settings)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'overwrites the connection settings' do
|
110
|
+
index_klass.elasticsearch
|
111
|
+
expect(Mindex::Elasticsearch).to have_received(:connect).with(connection_settings)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '.index_config' do
|
116
|
+
let(:settings) { { foo: :bar } }
|
117
|
+
let(:mapping) { { bar: :foo } }
|
118
|
+
|
119
|
+
before do
|
120
|
+
@indices = instance_double(Elasticsearch::API::Indices::IndicesClient, create: nil)
|
121
|
+
allow(::Elasticsearch::Client).to receive(:new).and_return(instance_double(Elasticsearch::Transport::Client, indices: @indices))
|
122
|
+
|
123
|
+
index_klass.index_config(settings: settings, mappings: mapping)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'creates the index with the given settings and mappings' do
|
127
|
+
index_klass.index_create(move_or_create_index_alias: false)
|
128
|
+
expect(@indices).to have_received(:create).with(hash_including(index: anything, body: { settings: settings, mappings: mapping }))
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '.reindex' do
|
133
|
+
let(:event_1) { { 'id' => 1, 'name' => 'BerlinMan' } }
|
134
|
+
let(:event_2) { { 'id' => 2, 'name' => 'Leipziger Triathlon' } }
|
135
|
+
|
136
|
+
before do
|
137
|
+
DB.run 'CREATE TABLE events (id integer primary key autoincrement, name varchar(255))'
|
138
|
+
DB[:events].insert(event_1)
|
139
|
+
DB[:events].insert(event_2)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'updates the entities' do
|
143
|
+
index_klass.reindex
|
144
|
+
expect(index_klass.es.get(index: index_klass.index_alias, id: event_1['id'])['_source']).to eq event_1
|
145
|
+
expect(index_klass.es.get(index: index_klass.index_alias, id: event_2['id'])['_source']).to eq event_2
|
146
|
+
DB[:events].where(id: event_1['id']).update(name: 'BerlinMan 2018')
|
147
|
+
DB[:events].where(id: event_2['id']).update(name: 'Leipziger Triathlon 2018')
|
148
|
+
index_klass.reindex
|
149
|
+
expect(index_klass.es.get(index: index_klass.index_alias, id: event_1['id'])['_source']['name']).to eq 'BerlinMan 2018'
|
150
|
+
expect(index_klass.es.get(index: index_klass.index_alias, id: event_2['id'])['_source']['name']).to eq 'Leipziger Triathlon 2018'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '.reindex_items' do
|
155
|
+
let(:event_1) { { 'id' => 1, 'name' => 'BerlinMan' } }
|
156
|
+
let(:event_2) { { 'id' => 2, 'name' => 'Paderborner Triathlon' } }
|
157
|
+
|
158
|
+
before do
|
159
|
+
index_klass.index_create
|
160
|
+
DB.run 'CREATE TABLE events (id integer primary key autoincrement, name varchar(255))'
|
161
|
+
DB[:events].insert(event_1)
|
162
|
+
DB[:events].insert(event_2)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'updates the entities' do
|
166
|
+
index_klass.reindex_items([event_1['id'], event_2['id']])
|
167
|
+
expect(index_klass.es.get(index: index_klass.index_alias, id: event_1['id'])['_source']['name']).to eq 'BerlinMan'
|
168
|
+
expect(index_klass.es.get(index: index_klass.index_alias, id: event_2['id'])['_source']['name']).to eq 'Paderborner Triathlon'
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '.drop_items' do
|
173
|
+
let(:event) { { 'id' => 1, 'name' => 'BerlinMan' } }
|
174
|
+
|
175
|
+
before do
|
176
|
+
DB.run 'CREATE TABLE events (id integer primary key autoincrement, name varchar(255))'
|
177
|
+
DB[:events].insert(event)
|
178
|
+
index_klass.reindex
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'drops the given entities' do
|
182
|
+
expect { index_klass.es.get(index: index_klass.index_alias, id: event['id']) }.not_to raise_error
|
183
|
+
|
184
|
+
index_klass.drop_items(event['id'])
|
185
|
+
|
186
|
+
expect do
|
187
|
+
index_klass.es.get(index: index_klass.index_alias, id: event['id'])
|
188
|
+
end.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe '.recreate_index' do
|
193
|
+
let(:event) { { 'id' => 1, 'name' => 'BerlinMan' } }
|
194
|
+
|
195
|
+
before do
|
196
|
+
DB.run 'CREATE TABLE events (id integer primary key autoincrement, name varchar(255))'
|
197
|
+
DB[:events].insert(event)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'inserts the entities in a new index' do
|
201
|
+
index_name = index_klass.recreate_index
|
202
|
+
expect(index_klass.es.get(index: index_name, id: event['id'])['_source']).to eq event
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
4
|
+
require 'mindex'
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
require 'sequel'
|
8
|
+
require 'sqlite3'
|
9
|
+
|
10
|
+
DB = Sequel.sqlite('tmp/database.sqlite3')
|
11
|
+
|
12
|
+
def reset_configuration!
|
13
|
+
Mindex.configure do |config|
|
14
|
+
config.elasticsearch_url = ENV.fetch('ELASTICSEARCH_URL', 'localhost:9200')
|
15
|
+
config.elasticsearch_user = ENV.fetch('ELASTICSEARCH_USERNAME', 'elastic')
|
16
|
+
config.elasticsearch_pass = ENV.fetch('ELASTICSEARCH_PASSWORD', 'changeme')
|
17
|
+
config.elasticsearch_options = nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def drop_indices!
|
22
|
+
Mindex::Elasticsearch.connect.indices.delete(index: '_all')
|
23
|
+
end
|
24
|
+
|
25
|
+
def drop_sqlite_tables!
|
26
|
+
tables = DB[:sqlite_master].where(type: 'table').exclude(name: %w[sqlite_sequence sqlite_master]).select.map { |row| row[:name] }
|
27
|
+
tables.each { |table| DB.run("DROP TABLE #{table}") }
|
28
|
+
end
|
29
|
+
|
30
|
+
RSpec.configure do |config|
|
31
|
+
config.before(:each) do
|
32
|
+
reset_configuration!
|
33
|
+
drop_indices!
|
34
|
+
drop_sqlite_tables!
|
35
|
+
end
|
36
|
+
end
|
metadata
CHANGED
@@ -1,59 +1,185 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mindex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Voigt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: elasticsearch
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
16
44
|
requirements:
|
17
|
-
- -
|
45
|
+
- - ">"
|
18
46
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1
|
47
|
+
version: '1'
|
20
48
|
type: :development
|
21
49
|
prerelease: false
|
22
50
|
version_requirements: !ruby/object:Gem::Requirement
|
23
51
|
requirements:
|
24
|
-
- -
|
52
|
+
- - ">"
|
25
53
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1
|
54
|
+
version: '1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
27
69
|
- !ruby/object:Gem::Dependency
|
28
70
|
name: rake
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
30
72
|
requirements:
|
31
|
-
- -
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: sequel
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sqlite3
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
32
144
|
- !ruby/object:Gem::Version
|
33
145
|
version: '0'
|
34
146
|
type: :development
|
35
147
|
prerelease: false
|
36
148
|
version_requirements: !ruby/object:Gem::Requirement
|
37
149
|
requirements:
|
38
|
-
- -
|
150
|
+
- - ">="
|
39
151
|
- !ruby/object:Gem::Version
|
40
152
|
version: '0'
|
41
|
-
description:
|
153
|
+
description:
|
42
154
|
email:
|
43
|
-
- michael.voigt@
|
155
|
+
- michael.voigt@spider-network.com
|
44
156
|
executables: []
|
45
157
|
extensions: []
|
46
158
|
extra_rdoc_files: []
|
47
159
|
files:
|
48
|
-
- .
|
160
|
+
- ".dockerignore"
|
161
|
+
- ".gitignore"
|
162
|
+
- ".gitlab-ci.yml"
|
163
|
+
- ".rspec"
|
164
|
+
- ".rubocop.yml"
|
165
|
+
- ".rubocop_todo.yml"
|
166
|
+
- Dockerfile
|
49
167
|
- Gemfile
|
50
168
|
- LICENSE.txt
|
51
169
|
- README.md
|
52
170
|
- Rakefile
|
171
|
+
- docker-compose.yml
|
53
172
|
- lib/mindex.rb
|
173
|
+
- lib/mindex/elasticsearch.rb
|
174
|
+
- lib/mindex/index.rb
|
54
175
|
- lib/mindex/version.rb
|
55
176
|
- mindex.gemspec
|
56
|
-
|
177
|
+
- spec/dummy/index/event.rb
|
178
|
+
- spec/lib/mindex/elasticsearch_spec.rb
|
179
|
+
- spec/lib/mindex/index_spec.rb
|
180
|
+
- spec/lib/mindex_spec.rb
|
181
|
+
- spec/spec_helper.rb
|
182
|
+
homepage: http://www.spider-network.com
|
57
183
|
licenses:
|
58
184
|
- MIT
|
59
185
|
metadata: {}
|
@@ -63,18 +189,23 @@ require_paths:
|
|
63
189
|
- lib
|
64
190
|
required_ruby_version: !ruby/object:Gem::Requirement
|
65
191
|
requirements:
|
66
|
-
- -
|
192
|
+
- - ">="
|
67
193
|
- !ruby/object:Gem::Version
|
68
194
|
version: '0'
|
69
195
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
196
|
requirements:
|
71
|
-
- -
|
197
|
+
- - ">="
|
72
198
|
- !ruby/object:Gem::Version
|
73
199
|
version: '0'
|
74
200
|
requirements: []
|
75
201
|
rubyforge_project:
|
76
|
-
rubygems_version: 2.
|
202
|
+
rubygems_version: 2.6.14
|
77
203
|
signing_key:
|
78
204
|
specification_version: 4
|
79
|
-
summary:
|
80
|
-
test_files:
|
205
|
+
summary: Mindex provides functionality to build elasticsearch indices
|
206
|
+
test_files:
|
207
|
+
- spec/dummy/index/event.rb
|
208
|
+
- spec/lib/mindex/elasticsearch_spec.rb
|
209
|
+
- spec/lib/mindex/index_spec.rb
|
210
|
+
- spec/lib/mindex_spec.rb
|
211
|
+
- spec/spec_helper.rb
|