elastics 0.5.0 → 0.6.1
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/README.md +10 -1
- data/elastics.gemspec +1 -1
- data/lib/elastics.rb +2 -0
- data/lib/elastics/active_record.rb +4 -1
- data/lib/elastics/active_record/helper_methods.rb +4 -2
- data/lib/elastics/active_record/search_result.rb +9 -2
- data/lib/elastics/client.rb +2 -2
- data/lib/elastics/model/schema.rb +6 -5
- data/lib/elastics/query_helper.rb +1 -1
- data/lib/elastics/rspec.rb +38 -0
- data/lib/elastics/search_query.rb +99 -0
- data/lib/elastics/tasks.rb +17 -1
- data/lib/elastics/tasks/indices.rb +52 -6
- data/lib/elastics/tasks/mappings.rb +33 -2
- data/lib/elastics/version.rb +1 -1
- data/lib/tasks/elastics.rake +1 -0
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4aec32b4215bd40d80c3d367f2166a0b91a278b9
|
4
|
+
data.tar.gz: ccae5ce2bf5904bd2605f152f4b02e1556449035
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4327171fbed3358386552e0a516b55e10c13aa47efea873e80fdc122a409862a73564a744ec49e2f7889c5be6e71e4c1d8a937aba1a48e2b915d0fe5f07cedba
|
7
|
+
data.tar.gz: cf810f168e30abb9167ff719c653f9e604764eb845734a0c16fa15146db3e4c558d6206258d83d394c2fe999297c7a029c0d2515a5de0c5a7e3338141510634c
|
data/README.md
CHANGED
@@ -101,8 +101,15 @@ end
|
|
101
101
|
User.elastics # Elastics::Client instance
|
102
102
|
User.elastics_params # hash with index & type values for the model
|
103
103
|
User.request_elastics(params) # performs request merging params with elastics_params
|
104
|
-
|
104
|
+
|
105
|
+
search = User.search_elastics(data)
|
105
106
|
# Returns Elastics::ActiveRecord::SearchResult object with some useful methods
|
107
|
+
search.relation # User.where(id: found_ids)
|
108
|
+
search.collection # Array of users sorted as in elastics response
|
109
|
+
|
110
|
+
# Set scope applied to relation:
|
111
|
+
search = Project.search_elastics(data, scope: ->(s) { s.includes(:user) })
|
112
|
+
search.collection.first.user # eagger-loaded
|
106
113
|
|
107
114
|
# Indexing on create/update can be skipped with skip_elastics
|
108
115
|
User.skip_elastics { users.each { |x| x.update_attributes(smth: 'not indexed') } }
|
@@ -136,6 +143,8 @@ production:
|
|
136
143
|
index_prefix: app_
|
137
144
|
```
|
138
145
|
|
146
|
+
YAML is passed througn ERB before parsing.
|
147
|
+
|
139
148
|
#### Create mappings & import data
|
140
149
|
```
|
141
150
|
$ rake elastics:migrate elastics:reindex
|
data/elastics.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_runtime_dependency 'httpclient', '~> 2.4
|
21
|
+
spec.add_runtime_dependency 'httpclient', '~> 2.4'
|
22
22
|
|
23
23
|
spec.add_development_dependency 'bundler', '~> 1.5'
|
24
24
|
spec.add_development_dependency 'rake', '~> 10.0'
|
data/lib/elastics.rb
CHANGED
@@ -8,9 +8,11 @@ module Elastics
|
|
8
8
|
autoload :Instrumentation, 'elastics/instrumentation'
|
9
9
|
autoload :Model, 'elastics/model'
|
10
10
|
autoload :QueryHelper, 'elastics/query_helper'
|
11
|
+
autoload :SearchQuery, 'elastics/search_query'
|
11
12
|
autoload :Result, 'elastics/result'
|
12
13
|
autoload :Tasks, 'elastics/tasks'
|
13
14
|
autoload :VersionManager, 'elastics/version_manager'
|
15
|
+
autoload :RSpec, 'elastics/rspec'
|
14
16
|
end
|
15
17
|
|
16
18
|
require 'elastics/railtie' if defined?(Rails)
|
@@ -38,7 +38,10 @@ module Elastics
|
|
38
38
|
private
|
39
39
|
def install_elastics_hooks(hooks)
|
40
40
|
if hooks.include?(:update)
|
41
|
-
after_commit :index_elastics,
|
41
|
+
after_commit :index_elastics,
|
42
|
+
on: [:create, :update],
|
43
|
+
unless: :skip_elastics?,
|
44
|
+
if: -> { previous_changes.any? }
|
42
45
|
end
|
43
46
|
if hooks.include?(:destroy)
|
44
47
|
after_commit :delete_elastics, on: [:destroy], unless: :skip_elastics?
|
@@ -21,8 +21,10 @@ module Elastics
|
|
21
21
|
|
22
22
|
# Finds items by ids and returns array in the order in which ids were given.
|
23
23
|
# Every missing record is replaced with `nil` in the result.
|
24
|
-
|
25
|
-
|
24
|
+
# If `conditions_present` is `true` it doesn't add where clause.
|
25
|
+
def find_all_ordered(ids, conditions_present = false)
|
26
|
+
relation = conditions_present ? where(id: ids) : self
|
27
|
+
items_by_id = relation.index_by(&:id)
|
26
28
|
ids.map { |i| items_by_id[i] }
|
27
29
|
end
|
28
30
|
|
@@ -1,8 +1,12 @@
|
|
1
1
|
module Elastics
|
2
2
|
module ActiveRecord
|
3
3
|
class SearchResult < Result::Search
|
4
|
+
# It expects `:model` option with a model-class.
|
5
|
+
# Optionally pass `scope` option with a lambda which takes and modifies
|
6
|
+
# relation.
|
4
7
|
def initialize(response, options = {})
|
5
8
|
@model = options[:model]
|
9
|
+
@scope = options[:scope]
|
6
10
|
super response, options
|
7
11
|
end
|
8
12
|
|
@@ -12,11 +16,14 @@ module Elastics
|
|
12
16
|
end
|
13
17
|
|
14
18
|
def collection
|
15
|
-
@collection ||=
|
19
|
+
@collection ||= relation.find_all_ordered(ids_to_find, true)
|
16
20
|
end
|
17
21
|
|
18
22
|
def relation
|
19
|
-
@
|
23
|
+
@relation ||= begin
|
24
|
+
result = @model.where(id: ids_to_find)
|
25
|
+
@scope ? @scope.call(result) : result
|
26
|
+
end
|
20
27
|
end
|
21
28
|
end
|
22
29
|
end
|
data/lib/elastics/client.rb
CHANGED
@@ -13,11 +13,12 @@ module Elastics
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def reset_elastics_index_name
|
16
|
-
@elastics_index_name =
|
17
|
-
superclass.elastics_index_name
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
@elastics_index_name =
|
17
|
+
if respond_to?(:superclass) && superclass.respond_to?(:elastics_index_name)
|
18
|
+
superclass.elastics_index_name
|
19
|
+
else
|
20
|
+
compute_elastics_index_name
|
21
|
+
end
|
21
22
|
end
|
22
23
|
|
23
24
|
def compute_elastics_index_name
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Elastics
|
2
|
+
module RSpec
|
3
|
+
module_function
|
4
|
+
|
5
|
+
# Adds around filter to perform elastics specific helper actions.
|
6
|
+
#
|
7
|
+
# - Enables autorefresh for each example,
|
8
|
+
# - Executes `clear_elastics` before each example,
|
9
|
+
# - Performs migration (once, for first occured example).
|
10
|
+
#
|
11
|
+
# Filter is applied only to tagged examples (`:elastics` by default).
|
12
|
+
#
|
13
|
+
# RSpec.configure do |config|
|
14
|
+
# Elastics::RSpec.configure(config)
|
15
|
+
# end
|
16
|
+
def configure(config, tag = :elastics)
|
17
|
+
migrated = false
|
18
|
+
error = nil
|
19
|
+
config.around tag => true do |ex|
|
20
|
+
if migrated
|
21
|
+
raise error if error
|
22
|
+
Model.list.each(&:clear_elastics)
|
23
|
+
else
|
24
|
+
begin
|
25
|
+
Tasks.drop_indices
|
26
|
+
Tasks.migrate
|
27
|
+
rescue Error => e
|
28
|
+
error = e
|
29
|
+
raise e
|
30
|
+
ensure
|
31
|
+
migrated = true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
AutoRefresh.enable! { ex.run }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Elastics
|
2
|
+
# Helpers to build search query.
|
3
|
+
#
|
4
|
+
# class Query
|
5
|
+
# include Elastics::SearchQuery
|
6
|
+
#
|
7
|
+
# # implement any of:
|
8
|
+
# # query, phrase_query, query_filters, post_filter, aggregations
|
9
|
+
#
|
10
|
+
# def phrase_query
|
11
|
+
# {bool: {should: [
|
12
|
+
# {multi_match: {
|
13
|
+
# # ...
|
14
|
+
# }}
|
15
|
+
# ]}}
|
16
|
+
# # or just
|
17
|
+
# {math: {message: params[:query_string]}}
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def query_filters
|
21
|
+
# [
|
22
|
+
# {term: {published: true}},
|
23
|
+
# terms_array_query(:tag, params[:tags], execution: :and),
|
24
|
+
# some_complex_filter,
|
25
|
+
# ]
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# def aggregations
|
29
|
+
# {
|
30
|
+
# tag: {terms: {
|
31
|
+
# field: :tag,
|
32
|
+
# size: 10,
|
33
|
+
# shard_size: 10,
|
34
|
+
# }},
|
35
|
+
# }
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# result = Model.search_elastics Query.new(params).as_json
|
40
|
+
module SearchQuery
|
41
|
+
include Elastics::QueryHelper
|
42
|
+
|
43
|
+
attr_reader :params
|
44
|
+
|
45
|
+
def initialize(params)
|
46
|
+
@params = params
|
47
|
+
end
|
48
|
+
|
49
|
+
def as_json
|
50
|
+
page = params[:page] || 1
|
51
|
+
per_page = params[:per_page] || 10
|
52
|
+
result = {
|
53
|
+
from: (page - 1) * per_page,
|
54
|
+
size: per_page,
|
55
|
+
fields: [],
|
56
|
+
query: query,
|
57
|
+
sort: sort,
|
58
|
+
}
|
59
|
+
post_filter = self.post_filter
|
60
|
+
result[:post_filter] = post_filter if post_filter
|
61
|
+
aggregations = self.aggregations
|
62
|
+
result[:aggregations] = aggregations if aggregations
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
66
|
+
# Builds query from phrase_query & query_filters.
|
67
|
+
def query
|
68
|
+
normalize_query(phrase_query, query_filters.compact)
|
69
|
+
end
|
70
|
+
|
71
|
+
def phrase_query
|
72
|
+
end
|
73
|
+
|
74
|
+
def query_filters
|
75
|
+
[]
|
76
|
+
end
|
77
|
+
|
78
|
+
def post_filter
|
79
|
+
end
|
80
|
+
|
81
|
+
def aggregations
|
82
|
+
end
|
83
|
+
|
84
|
+
# Takes `params[:sort]` and returns it compatible with elastics.
|
85
|
+
# Wraps scalars into array, hashes are converted into arrays,
|
86
|
+
# array are passed as is.
|
87
|
+
#
|
88
|
+
# {name: :asc, _score: :desc} => [{name: :asc}, {_score: :desc}]
|
89
|
+
# :created_at => [:created_at]
|
90
|
+
def sort
|
91
|
+
val = params[:sort]
|
92
|
+
case val
|
93
|
+
when Hash then val.map { |x| Hash[[x]] }
|
94
|
+
when Array then val
|
95
|
+
else val ? [val] : []
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/elastics/tasks.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
1
3
|
require 'active_support'
|
2
4
|
require 'active_support/core_ext'
|
3
5
|
|
@@ -23,7 +25,21 @@ module Elastics
|
|
23
25
|
extend self
|
24
26
|
|
25
27
|
def log(*args)
|
26
|
-
puts(*args)
|
28
|
+
puts(*args) if verbose
|
29
|
+
Rails.logger.info { "Elastics: #{args.join ' '}" } if defined?(Rails)
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_accessor :verbose
|
33
|
+
|
34
|
+
def suppress_messages
|
35
|
+
verbose_was, self.verbose = verbose, false
|
36
|
+
yield
|
37
|
+
ensure
|
38
|
+
self.verbose = verbose_was
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_yaml(file)
|
42
|
+
YAML.load(ERB.new(File.read(file)).result)
|
27
43
|
end
|
28
44
|
|
29
45
|
private
|
@@ -11,12 +11,54 @@ module Elastics
|
|
11
11
|
@indices_paths ||= base_paths.map { |x| File.join x, 'indices' }
|
12
12
|
end
|
13
13
|
|
14
|
+
# Merges settings from single files and dirs.
|
14
15
|
def indices_settings
|
15
|
-
@indices_settings ||=
|
16
|
+
@indices_settings ||= indices_from_files.merge!(indices_from_dirs)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Reads indices settings from single yml file.
|
20
|
+
# Setting can be given specific for env or one for all envs.
|
21
|
+
#
|
22
|
+
# tweet:
|
23
|
+
# development:
|
24
|
+
# settings:
|
25
|
+
# number_of_shards: 5
|
26
|
+
# production:
|
27
|
+
# settings:
|
28
|
+
# number_of_shards: 10
|
29
|
+
#
|
30
|
+
# user:
|
31
|
+
# settings:
|
32
|
+
# number_of_shards: 5
|
33
|
+
def indices_from_files
|
34
|
+
indices_paths.each_with_object({}) do |path, hash|
|
35
|
+
file = "#{path}.yml"
|
36
|
+
next unless File.exists?(file)
|
37
|
+
load_yaml(file).each do |name, data|
|
38
|
+
hash[name] = data[Rails.env] || data
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Reads indices settings from separate files. Index name is taken from
|
44
|
+
# file name, setting can be given specific for env or one for all envs.
|
45
|
+
#
|
46
|
+
# development:
|
47
|
+
# settings:
|
48
|
+
# number_of_shards: 5
|
49
|
+
# production:
|
50
|
+
# settings:
|
51
|
+
# number_of_shards: 10
|
52
|
+
#
|
53
|
+
# # or
|
54
|
+
# settings:
|
55
|
+
# number_of_shards: 1
|
56
|
+
def indices_from_dirs
|
57
|
+
indices_paths.map { |path| Dir["#{path}/*.yml"] }.
|
16
58
|
flatten.sort.
|
17
59
|
each_with_object({}) do |file, hash|
|
18
60
|
name = File.basename file, '.yml'
|
19
|
-
data =
|
61
|
+
data = load_yaml(file)
|
20
62
|
hash[name] = data[Rails.env] || data
|
21
63
|
end
|
22
64
|
end
|
@@ -76,10 +118,14 @@ module Elastics
|
|
76
118
|
new_versions = {}
|
77
119
|
post_aliases options do |index|
|
78
120
|
new_versions[index] = version_manager.next_version index
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
121
|
+
if client.index_exists?(versioned_index_name(index, :current))
|
122
|
+
[
|
123
|
+
alias_action(:remove, index, :current),
|
124
|
+
alias_action(:add, index, :next),
|
125
|
+
]
|
126
|
+
else
|
127
|
+
alias_action(:add, index, :next)
|
128
|
+
end
|
83
129
|
end
|
84
130
|
drop_indices(options.merge version: :current) if options.fetch(:drop, true)
|
85
131
|
new_versions.each do |index, version|
|
@@ -21,12 +21,43 @@ module Elastics
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
# Merges mappings from single files and dirs.
|
24
25
|
def mappings
|
25
|
-
@mappings ||=
|
26
|
+
@mappings ||= mappings_from_files.merge!(mappings_from_dirs)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Reads mappings from single yml file.
|
30
|
+
#
|
31
|
+
# user:
|
32
|
+
# dynamic: false
|
33
|
+
# properties:
|
34
|
+
# name: string
|
35
|
+
# tweet:
|
36
|
+
# properties:
|
37
|
+
# content: string
|
38
|
+
# user_id: integer
|
39
|
+
def mappings_from_files
|
40
|
+
mappings_paths.each_with_object({}) do |path, hash|
|
41
|
+
file = "#{path}.yml"
|
42
|
+
next unless File.exists?(file)
|
43
|
+
load_yaml(file).each do |name, data|
|
44
|
+
hash[name] = fix_mapping(name, data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Reads mappings from separate files. Type name is taken from file name.
|
50
|
+
#
|
51
|
+
# # user.yml
|
52
|
+
# dynamic: false
|
53
|
+
# properties:
|
54
|
+
# name: string
|
55
|
+
def mappings_from_dirs
|
56
|
+
mappings_paths.map { |path| Dir["#{path}/*.yml"] }.
|
26
57
|
flatten.sort.
|
27
58
|
each_with_object({}) do |file, hash|
|
28
59
|
name = File.basename file, '.yml'
|
29
|
-
hash[name] = fix_mapping(name,
|
60
|
+
hash[name] = fix_mapping(name, load_yaml(file))
|
30
61
|
end
|
31
62
|
end
|
32
63
|
|
data/lib/elastics/version.rb
CHANGED
data/lib/tasks/elastics.rake
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elastics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Max Melentiev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httpclient
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.4
|
19
|
+
version: '2.4'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.4
|
26
|
+
version: '2.4'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -131,6 +131,8 @@ files:
|
|
131
131
|
- lib/elastics/railtie.rb
|
132
132
|
- lib/elastics/result.rb
|
133
133
|
- lib/elastics/result/search.rb
|
134
|
+
- lib/elastics/rspec.rb
|
135
|
+
- lib/elastics/search_query.rb
|
134
136
|
- lib/elastics/tasks.rb
|
135
137
|
- lib/elastics/tasks/config.rb
|
136
138
|
- lib/elastics/tasks/indices.rb
|
@@ -166,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
168
|
version: '0'
|
167
169
|
requirements: []
|
168
170
|
rubyforge_project:
|
169
|
-
rubygems_version: 2.
|
171
|
+
rubygems_version: 2.6.8
|
170
172
|
signing_key:
|
171
173
|
specification_version: 4
|
172
174
|
summary: ElasticSearch client with ActiveRecord integration
|
@@ -178,4 +180,3 @@ test_files:
|
|
178
180
|
- spec/lib/elastics/instrumentation_spec.rb
|
179
181
|
- spec/lib/elastics/model/schema_spec.rb
|
180
182
|
- spec/spec_helper.rb
|
181
|
-
has_rdoc:
|