es-elasticity 0.12.0 → 0.13.3.pre1
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 +5 -5
- data/.circleci/config.yml +223 -0
- data/.gitignore +10 -7
- data/.rubocop.yml +23 -0
- data/CHANGELOG.md +143 -0
- data/Gemfile +4 -4
- data/Gemfile.lock +94 -0
- data/README.md +17 -3
- data/Rakefile +5 -1
- data/{elasticity.gemspec → es-elasticity.gemspec} +6 -0
- data/lib/elasticity.rb +1 -0
- data/lib/elasticity/index_config.rb +3 -2
- data/lib/elasticity/index_mapper.rb +8 -1
- data/lib/elasticity/instrumented_client.rb +1 -1
- data/lib/elasticity/search.rb +19 -4
- data/lib/elasticity/strategies/alias_index.rb +26 -8
- data/lib/elasticity/strategies/single_index.rb +12 -2
- data/lib/elasticity/version.rb +1 -1
- data/tasks/ci.rake +12 -0
- data/tasks/rdoc.rake +14 -0
- metadata +61 -35
- data/.travis.yml +0 -17
- data/CHANGELOG +0 -76
- data/bin/rake +0 -16
- data/bin/rspec +0 -16
- data/es-elasticity-0.12.0.pre.rc1.gem +0 -0
- data/spec/functional/persistence_spec.rb +0 -439
- data/spec/functional/search_spec.rb +0 -117
- data/spec/functional/segmented_spec.rb +0 -86
- data/spec/rspec_config.rb +0 -42
- data/spec/units/document_spec.rb +0 -63
- data/spec/units/index_config_spec.rb +0 -97
- data/spec/units/index_mapper_spec.rb +0 -4
- data/spec/units/multi_search_response_parser_spec.rb +0 -121
- data/spec/units/multi_search_spec.rb +0 -158
- data/spec/units/search_spec.rb +0 -230
- data/spec/units/strategies/single_index_spec.rb +0 -85
@@ -1,117 +0,0 @@
|
|
1
|
-
RSpec.describe "Search", elasticsearch: true do
|
2
|
-
class CatDoc < Elasticity::Document
|
3
|
-
configure do |c|
|
4
|
-
c.strategy = Elasticity::Strategies::SingleIndex
|
5
|
-
c.document_type = "cat"
|
6
|
-
c.mapping = { "properties" => {
|
7
|
-
name: { type: "text" },
|
8
|
-
description: { type: "text" },
|
9
|
-
age: { type: "integer" }
|
10
|
-
} }
|
11
|
-
end
|
12
|
-
|
13
|
-
attr_accessor :name, :age, :description
|
14
|
-
|
15
|
-
def to_document
|
16
|
-
{ name: name, age: age, description: description }
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class DogDoc < Elasticity::Document
|
21
|
-
configure do |c|
|
22
|
-
c.strategy = Elasticity::Strategies::SingleIndex
|
23
|
-
c.document_type = "dog"
|
24
|
-
c.mapping = { "properties" => {
|
25
|
-
name: { type: "keyword" },
|
26
|
-
description: { type: "text" },
|
27
|
-
age: { type: "integer" },
|
28
|
-
hungry: { type: "boolean" }
|
29
|
-
} }
|
30
|
-
end
|
31
|
-
attr_accessor :name, :age, :description, :hungry
|
32
|
-
|
33
|
-
def to_document
|
34
|
-
{ name: name, age: age, description: description, hungry: hungry }
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
describe "search_args" do
|
39
|
-
before do
|
40
|
-
CatDoc.recreate_index
|
41
|
-
DogDoc.recreate_index
|
42
|
-
|
43
|
-
@elastic_search_client.cluster.health wait_for_status: 'yellow'
|
44
|
-
|
45
|
-
cat = CatDoc.new(name: "felix the cat", age: 10, description: "I am an old cat")
|
46
|
-
dog = DogDoc.new(name: "fido", age: 4, hungry: true, description: "I am a hungry dog")
|
47
|
-
|
48
|
-
cat.update
|
49
|
-
dog.update
|
50
|
-
|
51
|
-
CatDoc.flush_index
|
52
|
-
end
|
53
|
-
|
54
|
-
describe "explain: true" do
|
55
|
-
def get_explanations(results)
|
56
|
-
results.map(&:_explanation)
|
57
|
-
end
|
58
|
-
|
59
|
-
it "supports on single index search results" do
|
60
|
-
results = CatDoc.search({}, { explain: true }).search_results
|
61
|
-
|
62
|
-
expect(get_explanations(results)).to all( be_truthy )
|
63
|
-
end
|
64
|
-
|
65
|
-
it "supports for multisearch" do
|
66
|
-
cat = CatDoc.search({}, { explain: true })
|
67
|
-
dog = DogDoc.search({})
|
68
|
-
|
69
|
-
subject = Elasticity::MultiSearch.new do |m|
|
70
|
-
m.add(:cats, cat, documents: CatDoc)
|
71
|
-
m.add(:dogs, dog, documents: DogDoc)
|
72
|
-
end
|
73
|
-
|
74
|
-
expect(get_explanations(subject[:cats])).to all( be_truthy )
|
75
|
-
expect(get_explanations(subject[:dogs])).to all( be_nil )
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
describe "highlight" do
|
80
|
-
it "is nil when the highlight does not return" do
|
81
|
-
results = CatDoc.search({}).search_results
|
82
|
-
|
83
|
-
expect(results.first.highlighted_attrs).to be_nil
|
84
|
-
expect(results.first.highlighted).to be_nil
|
85
|
-
end
|
86
|
-
|
87
|
-
describe "when specifying highlight" do
|
88
|
-
let(:cat_search_result) {
|
89
|
-
highlight_search = {
|
90
|
-
query: {
|
91
|
-
multi_match: {
|
92
|
-
query: "cat",
|
93
|
-
fields: ["name^1000", "description"]
|
94
|
-
}
|
95
|
-
},
|
96
|
-
highlight: {
|
97
|
-
fields: {
|
98
|
-
"*": {}
|
99
|
-
}
|
100
|
-
}
|
101
|
-
}
|
102
|
-
|
103
|
-
CatDoc.search(highlight_search).search_results.first
|
104
|
-
}
|
105
|
-
|
106
|
-
it "highlighted_attrs returns the highlighted" do
|
107
|
-
expect(cat_search_result.highlighted_attrs).to eq(["name", "description"])
|
108
|
-
end
|
109
|
-
|
110
|
-
it "highlighted returns a new object with the name transformed" do
|
111
|
-
expect(cat_search_result.highlighted.name.first).to include("felix")
|
112
|
-
expect(cat_search_result.highlighted.description.first).to include("old")
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
RSpec.describe "Segmented indexes", elasticsearch: true do
|
2
|
-
subject do
|
3
|
-
Class.new(Elasticity::SegmentedDocument) do
|
4
|
-
def self.name
|
5
|
-
"SomeClass"
|
6
|
-
end
|
7
|
-
|
8
|
-
configure do |c|
|
9
|
-
c.index_base_name = "people"
|
10
|
-
c.document_type = "person"
|
11
|
-
c.mapping = {
|
12
|
-
properties: {
|
13
|
-
name: { type: "text" },
|
14
|
-
},
|
15
|
-
}
|
16
|
-
end
|
17
|
-
|
18
|
-
attr_accessor :name
|
19
|
-
|
20
|
-
def self.by_name(name)
|
21
|
-
search(query: { match: { name: name } })
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_document
|
25
|
-
{ name: name }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def ensure_index(*segments)
|
31
|
-
@indexed ||= []
|
32
|
-
segments.each(&:recreate_index)
|
33
|
-
@indexed += segments
|
34
|
-
end
|
35
|
-
|
36
|
-
after do
|
37
|
-
Array(@indexed).each { |i| i.delete_index }
|
38
|
-
end
|
39
|
-
|
40
|
-
it "allows all operations on a segment" do
|
41
|
-
seg = subject.segment("A")
|
42
|
-
ensure_index(seg)
|
43
|
-
|
44
|
-
rodrigo = seg.new(name: "rodrigo")
|
45
|
-
|
46
|
-
id, success = rodrigo.update
|
47
|
-
expect(id).to be_kind_of(String)
|
48
|
-
expect(success).to be true
|
49
|
-
|
50
|
-
seg.flush_index
|
51
|
-
results = seg.by_name("rodrigo").to_a.first
|
52
|
-
expect(results.class).to eq rodrigo.class
|
53
|
-
expect(results.name).to eq rodrigo.name
|
54
|
-
|
55
|
-
rodrigo.delete
|
56
|
-
seg.flush_index
|
57
|
-
|
58
|
-
results = seg.by_name("rodrigo").to_a
|
59
|
-
expect(results).to be_empty
|
60
|
-
end
|
61
|
-
|
62
|
-
it "isolates segments from one another" do
|
63
|
-
seg_a = subject.segment("A")
|
64
|
-
seg_b = subject.segment("B")
|
65
|
-
ensure_index(seg_a, seg_b)
|
66
|
-
|
67
|
-
doc_a = seg_a.new(name: "doc a")
|
68
|
-
_, success = doc_a.update
|
69
|
-
expect(success).to be true
|
70
|
-
|
71
|
-
doc_b = seg_b.new(name: "doc b")
|
72
|
-
_, success = doc_b.update
|
73
|
-
expect(success).to be true
|
74
|
-
|
75
|
-
seg_a.flush_index
|
76
|
-
seg_b.flush_index
|
77
|
-
|
78
|
-
res_a = seg_a.by_name("doc").to_a.first
|
79
|
-
expect(res_a.class).to eq doc_a.class
|
80
|
-
expect(res_a.name).to eq doc_a.name
|
81
|
-
|
82
|
-
res_b = seg_b.by_name("doc").to_a.first
|
83
|
-
expect(res_b.class).to eq doc_b.class
|
84
|
-
expect(res_b.name).to eq doc_b.name
|
85
|
-
end
|
86
|
-
end
|
data/spec/rspec_config.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require "simplecov"
|
2
|
-
require "oj"
|
3
|
-
|
4
|
-
SimpleCov.start
|
5
|
-
|
6
|
-
require "elasticity"
|
7
|
-
require "pry"
|
8
|
-
require "byebug"
|
9
|
-
|
10
|
-
def elastic_search_client
|
11
|
-
return @elastic_search_client if defined?(@elastic_search_client)
|
12
|
-
@elastic_search_client = Elasticsearch::Client.new host: "http://0.0.0.0:9200"
|
13
|
-
end
|
14
|
-
|
15
|
-
logger = Logger.new("spec/spec.log")
|
16
|
-
logger.level = Logger::DEBUG
|
17
|
-
|
18
|
-
ActiveSupport::LogSubscriber.logger = logger
|
19
|
-
Elasticity::LogSubscriber.attach_to(:elasticity)
|
20
|
-
|
21
|
-
RSpec.configure do |c|
|
22
|
-
c.disable_monkey_patching!
|
23
|
-
|
24
|
-
c.before(:suite) do
|
25
|
-
logger.info "init.rspec Starting test suite execution"
|
26
|
-
end
|
27
|
-
|
28
|
-
c.before(:each) do |example|
|
29
|
-
logger.info "spec.rspec #{example.full_description}"
|
30
|
-
|
31
|
-
if example.metadata[:elasticsearch]
|
32
|
-
client = elastic_search_client
|
33
|
-
else
|
34
|
-
client = double(:elasticsearch_client)
|
35
|
-
end
|
36
|
-
|
37
|
-
Elasticity.configure do |e|
|
38
|
-
e.client = client
|
39
|
-
e.namespace = "elasticity_test"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/spec/units/document_spec.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
require "elasticity/search"
|
2
|
-
|
3
|
-
RSpec.describe Elasticity::Document do
|
4
|
-
mappings = {
|
5
|
-
properties: {
|
6
|
-
name: { type: "string" },
|
7
|
-
|
8
|
-
items: {
|
9
|
-
type: "nested",
|
10
|
-
properties: {
|
11
|
-
name: { type: "string" },
|
12
|
-
},
|
13
|
-
}
|
14
|
-
}
|
15
|
-
}
|
16
|
-
|
17
|
-
let :klass do
|
18
|
-
Class.new(described_class) do
|
19
|
-
def self.name
|
20
|
-
"SomeClass"
|
21
|
-
end
|
22
|
-
|
23
|
-
configure do |c|
|
24
|
-
c.index_base_name = "class_names"
|
25
|
-
c.document_type = "class_name"
|
26
|
-
c.mapping = mappings
|
27
|
-
c.settings = { number_of_shards: 2 }
|
28
|
-
end
|
29
|
-
|
30
|
-
attr_accessor :name, :items
|
31
|
-
|
32
|
-
def to_document
|
33
|
-
{ name: name, items: items }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
let :strategy do
|
39
|
-
double(:strategy)
|
40
|
-
end
|
41
|
-
|
42
|
-
before :each do
|
43
|
-
allow(Elasticity::Strategies::AliasIndex).to receive(:new).and_return(strategy)
|
44
|
-
end
|
45
|
-
|
46
|
-
it "requires subclasses to define to_document method" do
|
47
|
-
expect { Class.new(described_class).new.to_document }.to raise_error(NotImplementedError)
|
48
|
-
end
|
49
|
-
|
50
|
-
context "instance" do
|
51
|
-
subject { klass.new _id: 1, name: "Foo", items: [{ name: "Item1" }] }
|
52
|
-
|
53
|
-
it "stores the document in the strategy" do
|
54
|
-
expect(strategy).to receive(:index_document).with("class_name", 1, { name: "Foo", items: [{ name: "Item1" }] }).and_return("_id" => "1", "created" => true)
|
55
|
-
subject.update
|
56
|
-
end
|
57
|
-
|
58
|
-
it "assigns number_of_shards if present" do
|
59
|
-
expect(subject.config.settings[:number_of_shards]).to eq 2
|
60
|
-
expect(subject.config.definition[:settings][:number_of_shards]).to eq 2
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
require 'elasticity/index_config'
|
2
|
-
|
3
|
-
RSpec.describe Elasticity::IndexConfig do
|
4
|
-
let(:elasticity_config) { double("config", client: double("client", versions: ["5.3.4"])) }
|
5
|
-
subject { }
|
6
|
-
|
7
|
-
let(:defaults) do
|
8
|
-
{
|
9
|
-
index_base_name: 'users',
|
10
|
-
document_type: 'user'
|
11
|
-
}
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'accepts default configuration options' do
|
15
|
-
config = described_class.new(elasticity_config, defaults) {}
|
16
|
-
expect(config.index_base_name).to eql('users')
|
17
|
-
expect(config.document_type).to eql('user')
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'overrides defaults' do
|
21
|
-
config = described_class.new(elasticity_config, defaults) do |c|
|
22
|
-
c.index_base_name = 'user_documents'
|
23
|
-
c.document_type = 'users'
|
24
|
-
end
|
25
|
-
|
26
|
-
expect(config.index_base_name).to eql('user_documents')
|
27
|
-
expect(config.document_type).to eql('users')
|
28
|
-
end
|
29
|
-
|
30
|
-
context "subclass warnings and exceptions" do
|
31
|
-
class Multied < Elasticity::Document
|
32
|
-
end
|
33
|
-
describe "multi_mapping exceptions" do
|
34
|
-
it "raises an exception for version 7 and above if subclasses are configured" do
|
35
|
-
stub_version("7.0.0")
|
36
|
-
expect do
|
37
|
-
Multied.configure do |c|
|
38
|
-
c.index_base_name = "cats_and_dogs"
|
39
|
-
c.strategy = Elasticity::Strategies::SingleIndex
|
40
|
-
c.subclasses = { cat: "Cat", dog: "Dog" }
|
41
|
-
end
|
42
|
-
end.to raise_error(
|
43
|
-
Elasticity::IndexConfig::SubclassError,
|
44
|
-
Elasticity::IndexConfig::SUBCLASSES_ERROR
|
45
|
-
)
|
46
|
-
end
|
47
|
-
|
48
|
-
it "does not raise an exception for version 7 and above if no subclasses are configured" do
|
49
|
-
stub_version("7.0.0")
|
50
|
-
expect do
|
51
|
-
Multied.configure do |c|
|
52
|
-
c.index_base_name = "cats_and_dogs"
|
53
|
-
c.strategy = Elasticity::Strategies::SingleIndex
|
54
|
-
end
|
55
|
-
end.to_not raise_error
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe "multi_mapping warnings" do
|
60
|
-
it "warns if multi_mapping is not supported by the ES version" do
|
61
|
-
stub_version("6.0.2")
|
62
|
-
expect do
|
63
|
-
Multied.configure do |c|
|
64
|
-
c.index_base_name = "cats_and_dogs"
|
65
|
-
c.strategy = Elasticity::Strategies::SingleIndex
|
66
|
-
c.subclasses = { cat: "Cat", dog: "Dog" }
|
67
|
-
end
|
68
|
-
end.to output("#{Elasticity::IndexConfig::SUBCLASSES_WARNING}\n").to_stderr
|
69
|
-
end
|
70
|
-
|
71
|
-
it "does not warn if multi_mapping is supported by the ES version" do
|
72
|
-
stub_version("5.3.1")
|
73
|
-
expect do
|
74
|
-
Multied.configure do |c|
|
75
|
-
c.index_base_name = "cats_and_dogs"
|
76
|
-
c.strategy = Elasticity::Strategies::SingleIndex
|
77
|
-
c.subclasses = { cat: "Cat", dog: "Dog" }
|
78
|
-
end
|
79
|
-
end.to_not output.to_stderr
|
80
|
-
end
|
81
|
-
|
82
|
-
it "does not warn when no subclasses are configured" do
|
83
|
-
stub_version("6.0.2")
|
84
|
-
expect do
|
85
|
-
Multied.configure do |c|
|
86
|
-
c.index_base_name = "cats_and_dogs"
|
87
|
-
c.strategy = Elasticity::Strategies::SingleIndex
|
88
|
-
end
|
89
|
-
end.to_not output.to_stderr
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def stub_version(version)
|
94
|
-
allow_any_instance_of(Elasticity::InstrumentedClient).to receive(:versions).and_return([version])
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,121 +0,0 @@
|
|
1
|
-
require "elasticity/multi_search_response_parser"
|
2
|
-
|
3
|
-
RSpec.describe Elasticity::MultiSearchResponseParser do
|
4
|
-
describe ".parse" do
|
5
|
-
let :response do
|
6
|
-
{
|
7
|
-
"hits" => {
|
8
|
-
"total" => 2,
|
9
|
-
"hits" => [
|
10
|
-
{ "_id" => 1, "_source" => { "name" => "foo" }},
|
11
|
-
{ "_id" => 2, "_source" => { "name" => "bar" }}
|
12
|
-
]
|
13
|
-
}
|
14
|
-
}
|
15
|
-
end
|
16
|
-
|
17
|
-
let :klass do
|
18
|
-
Class.new do
|
19
|
-
include ActiveModel::Model
|
20
|
-
attr_accessor :_id, :name
|
21
|
-
|
22
|
-
def self.map_hit(hit)
|
23
|
-
new(_id: hit["_id"], name: hit["_source"]["name"])
|
24
|
-
end
|
25
|
-
|
26
|
-
def ==(other)
|
27
|
-
self._id == other._id && self.name == other.name
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
let :search do
|
33
|
-
body = {
|
34
|
-
index: "index_first",
|
35
|
-
type: "document_first",
|
36
|
-
search: { search: :first, size: 2 }
|
37
|
-
}
|
38
|
-
|
39
|
-
{
|
40
|
-
search_definition: OpenStruct.new(body: body),
|
41
|
-
documents: klass
|
42
|
-
}
|
43
|
-
end
|
44
|
-
|
45
|
-
it "parses a simple reponse" do
|
46
|
-
search_result = described_class.parse(response, search)
|
47
|
-
|
48
|
-
expect(search_result[0].name).to eq "foo"
|
49
|
-
expect(search_result[1].name).to eq "bar"
|
50
|
-
end
|
51
|
-
|
52
|
-
context "for a 400 error response" do
|
53
|
-
let(:response) do
|
54
|
-
{
|
55
|
-
"error" => {
|
56
|
-
"root_cause" => [
|
57
|
-
{
|
58
|
-
"type" => "too_many_clauses",
|
59
|
-
"reason" => "too_many_clauses: maxClauseCount is set to 1024"
|
60
|
-
}
|
61
|
-
],
|
62
|
-
},
|
63
|
-
"status" => 400
|
64
|
-
}
|
65
|
-
end
|
66
|
-
|
67
|
-
it "raises an error for the given status code" do
|
68
|
-
expect { described_class.parse response, search }.to(
|
69
|
-
raise_error Elasticsearch::Transport::Transport::Errors::BadRequest,
|
70
|
-
response.to_json
|
71
|
-
)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
context "for a 500 error response" do
|
76
|
-
let(:response) do
|
77
|
-
{
|
78
|
-
"error" => {
|
79
|
-
"root_cause" => [
|
80
|
-
{
|
81
|
-
"type" => "not_index_found",
|
82
|
-
"reason" => "not_index_found: index bla was not found"
|
83
|
-
}
|
84
|
-
],
|
85
|
-
},
|
86
|
-
"status" => 500
|
87
|
-
}
|
88
|
-
end
|
89
|
-
|
90
|
-
it "raises an error for the given status code" do
|
91
|
-
expect { described_class.parse response, search }.to(
|
92
|
-
raise_error Elasticsearch::Transport::Transport::Errors::InternalServerError,
|
93
|
-
response.to_json
|
94
|
-
)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
context "for an unknown error response" do
|
99
|
-
let(:response) do
|
100
|
-
{
|
101
|
-
"error" => {
|
102
|
-
"root_cause" => [
|
103
|
-
{
|
104
|
-
"type" => "known_error",
|
105
|
-
"reason" => "known_error: Something wrong happened"
|
106
|
-
}
|
107
|
-
],
|
108
|
-
},
|
109
|
-
"status" => 555
|
110
|
-
}
|
111
|
-
end
|
112
|
-
|
113
|
-
it "raises an unkown error for an known status code" do
|
114
|
-
expect { described_class.parse response, search }.to(
|
115
|
-
raise_error Elasticity::MultiSearchResponseParser::UnknownError,
|
116
|
-
response.to_json
|
117
|
-
)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|