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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0551f44df8bc5a94b2f3e115d1649bb0c64bb0be
4
- data.tar.gz: fe326d30a64848f1cdf15d0e590165757dbfcb9e
3
+ metadata.gz: 8a71d6115d947753187c7da5136c2ca67fbb1131
4
+ data.tar.gz: 0ec7e8030e636f8305b09d496185e948c950d4bd
5
5
  SHA512:
6
- metadata.gz: a2f2c6b60704baeab1ea36aa6372743d73a10ac5197d0ee5b134ccdb426806cb1b71264671fe0e248597248a4e631117607b800c63202a2c0f7c8053bc92bb80
7
- data.tar.gz: 0123ab5f1ba1b23abffe2e795c9d5501c2b380b761123ccc52297b0df3786a330f2d189d9a9d7622941d5c5284d111c9130a574ec45c53e9068ff8e34d1aa00c
6
+ metadata.gz: 0535323e08ea36d5897b162b1de6d69e53f08e9bac68c794e70559b175b963ced7d584680cb9153e61e33ec1ad436ae69b823ab619a4f0dba26388a818737d42
7
+ data.tar.gz: e6eb59a631d55a8aceaca0ab13ded8e092e9ea359dc946a65db69299e6bf7657780ad8915349424485a01a43547f06a1f73d50fa564c46f7e502937d0a6ad190
data/.dockerignore ADDED
@@ -0,0 +1 @@
1
+ tmp
data/.gitignore CHANGED
@@ -20,3 +20,5 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ .ruby-version
24
+ .ruby-gemset
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
@@ -0,0 +1,10 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Documentation:
4
+ Enabled: false
5
+
6
+ AllCops:
7
+ TargetRubyVersion: 2.4
8
+
9
+ Metrics/LineLength:
10
+ Max: 150
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in mindex.gemspec
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 Michael Voigt
1
+ Copyright (c) 2017 Michael Voigt
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,29 +1,82 @@
1
1
  # Mindex
2
2
 
3
- TODO: Write a gem description
3
+ This gem provides functionality to build elasticsearch indices.
4
4
 
5
5
  ## Installation
6
6
 
7
- Add this line to your application's Gemfile:
8
-
9
- gem 'mindex'
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
- $ bundle
17
+ ## Example
14
18
 
15
- Or install it yourself as:
19
+ ### Index declaration
20
+ ```ruby
21
+ module Index
22
+ class Event
23
+ include Mindex::Index
16
24
 
17
- $ gem install mindex
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
- ## Usage
33
+ def self.fetch(ids)
34
+ Event.where(id: ids).map do |event|
35
+ index_data(event)
36
+ end
37
+ end
20
38
 
21
- TODO: Write usage instructions here
39
+ private
22
40
 
23
- ## Contributing
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
- 1. Fork it ( https://github.com/[my-github-username]/mindex/fork )
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create a new Pull Request
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
@@ -1,2 +1,9 @@
1
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
2
 
3
+ require 'bundler/gem_tasks'
4
+
5
+ require 'rspec/core/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task default: :spec
@@ -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
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mindex
2
- VERSION = "0.0.1"
4
+ VERSION = '0.1.0'
3
5
  end
data/lib/mindex.rb CHANGED
@@ -1,5 +1,20 @@
1
- require "mindex/version"
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
- # Your code goes here...
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
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
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 = "mindex"
7
+ spec.name = 'mindex'
8
8
  spec.version = Mindex::VERSION
9
- spec.authors = ["Michael Voigt"]
10
- spec.email = ["michael.voigt@voigt-mail.de"]
11
- spec.summary = %q{Write a short summary. Required.}
12
- spec.description = %q{Write a longer description. Optional.}
13
- spec.homepage = ""
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{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
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 "bundler", "~> 1.6"
22
- spec.add_development_dependency "rake"
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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Mindex do
4
+ it 'defines a version number' do
5
+ expect(described_class::VERSION).to eq '0.0.0'
6
+ end
7
+ end
@@ -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.1
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: 2014-08-11 00:00:00.000000000 Z
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.6'
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.6'
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: Write a longer description. Optional.
153
+ description:
42
154
  email:
43
- - michael.voigt@voigt-mail.de
155
+ - michael.voigt@spider-network.com
44
156
  executables: []
45
157
  extensions: []
46
158
  extra_rdoc_files: []
47
159
  files:
48
- - .gitignore
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
- homepage: ''
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.4.1
202
+ rubygems_version: 2.6.14
77
203
  signing_key:
78
204
  specification_version: 4
79
- summary: Write a short summary. Required.
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