elastic_mapper 0.2.1 → 0.3.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/Gemfile.lock +2 -2
- data/README.md +21 -0
- data/lib/elastic_mapper.rb +1 -0
- data/lib/elastic_mapper/multi_search.rb +35 -0
- data/lib/elastic_mapper/search.rb +27 -2
- data/lib/elastic_mapper/version.rb +1 -1
- data/spec/elastic_mapper/multi_search_spec.rb +124 -0
- data/spec/elastic_mapper/search_spec.rb +2 -2
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 543ec27590a91159027e57f2ebd0f688608982eb
|
4
|
+
data.tar.gz: 6eed8ee0decba2a9bbc29dd013871c1e0065891e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a97cdb65e03a481197863a51bb3e471a9fb1af8c63349e0b808e2f73f106ca59e9913c7f9d48eafd7218a2bf68aec8781a32c15eb82561fbcf2e339f7d1e4796
|
7
|
+
data.tar.gz: 3909b4389855d275a7db70aa0ff18232f1e48d6a64dab918c6e9db52f707804ae2bd66821b4b5551ef80566be346ac2a17cfbdaa20ecd55d2f51a789baddc8c1
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
elastic_mapper (0.
|
4
|
+
elastic_mapper (0.3.0)
|
5
5
|
faraday (~> 0.8.9)
|
6
6
|
stretcher
|
7
7
|
|
@@ -26,7 +26,7 @@ GEM
|
|
26
26
|
faraday_middleware-multi_json (0.0.6)
|
27
27
|
faraday_middleware
|
28
28
|
multi_json
|
29
|
-
hashie (
|
29
|
+
hashie (3.3.1)
|
30
30
|
i18n (0.6.9)
|
31
31
|
json (1.8.1)
|
32
32
|
minitest (5.3.4)
|
data/README.md
CHANGED
@@ -118,6 +118,27 @@ articles = Article.search({ "query_string" => { "query" => 'alpha' } })
|
|
118
118
|
results = SearchModel.search('* OR alpha', size: 10, from: 10)
|
119
119
|
```
|
120
120
|
|
121
|
+
Searching Across Multiple Models
|
122
|
+
--------------------------------
|
123
|
+
|
124
|
+
Suppose you have two mappings in ElasticSearch, for instance an `article` and
|
125
|
+
a `user`. There are times when you might want to search across both models
|
126
|
+
at the same time.
|
127
|
+
|
128
|
+
This can be facilitated with ElasticMapper's `MultiSearch` class:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
multi = ElasticMapper::MultiSearch.new({
|
132
|
+
index_models: SearchModel,
|
133
|
+
index_models_two: SearchModelTwo
|
134
|
+
})
|
135
|
+
|
136
|
+
results = multi.search('* OR alpha', size: 1)
|
137
|
+
```
|
138
|
+
|
139
|
+
* a multi-search instance accepts a hash into the constructor, which maps ElasticSearch mappings to model classes.
|
140
|
+
* once you have a `MultiSearch` instance, search works the same as it does when using the ActiveModel mixin.
|
141
|
+
|
121
142
|
That's It
|
122
143
|
---------
|
123
144
|
|
data/lib/elastic_mapper.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
# search across multiple models at the same
|
2
|
+
# time. NO WAY!
|
3
|
+
class ElasticMapper::MultiSearch
|
4
|
+
|
5
|
+
# takes a hash lookup which maps from
|
6
|
+
# a mapping name on to an ActiveRecord model.
|
7
|
+
def initialize(obj_map)
|
8
|
+
@obj_map = obj_map # used to create classes from mappings.
|
9
|
+
@_mapping_name = @obj_map.keys.join(',')
|
10
|
+
self.extend(ElasticMapper::Search::ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
# receives queries in the form:
|
14
|
+
# {
|
15
|
+
# id: "#{obj._type}_#{obj.id}",
|
16
|
+
# obj_id: obj.id,
|
17
|
+
# type: obj._type
|
18
|
+
# }
|
19
|
+
def find(ids_hash)
|
20
|
+
results = {}
|
21
|
+
|
22
|
+
# 1. iterate over each active model we care about.
|
23
|
+
# 2. perform a bulk lookup based on id.
|
24
|
+
# 3. map everything onto results hash, so that we can maintain sort order.
|
25
|
+
@obj_map.keys.each do |key|
|
26
|
+
ids = ids_hash.select {|id_hash| id_hash[:type] == key.to_s}.map {|obj| obj[:obj_id]}
|
27
|
+
next unless ids.count > 0
|
28
|
+
@obj_map[key].find(ids).each do |record|
|
29
|
+
results["#{key}_#{record.id}"] = record
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
results
|
34
|
+
end
|
35
|
+
end
|
@@ -55,8 +55,24 @@ module ElasticMapper::Search
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
# if a search is being performed across multiple
|
59
|
+
# models, we must include the type along with id:
|
60
|
+
documents = if self.class.name =~ /MultiSearch/
|
61
|
+
ids = res.results.map do |obj|
|
62
|
+
{
|
63
|
+
id: "#{obj._type}_#{obj.id}",
|
64
|
+
obj_id: obj.id,
|
65
|
+
type: obj._type
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
ordered_results_multi(ids)
|
70
|
+
else
|
71
|
+
ordered_results(res.results.map(&:id))
|
72
|
+
end
|
73
|
+
|
58
74
|
Hashie::Mash.new({
|
59
|
-
documents:
|
75
|
+
documents: documents,
|
60
76
|
from: opts[:from],
|
61
77
|
total: res.total
|
62
78
|
})
|
@@ -91,11 +107,20 @@ module ElasticMapper::Search
|
|
91
107
|
h[m.id] = m
|
92
108
|
h
|
93
109
|
end
|
94
|
-
|
110
|
+
|
95
111
|
ids.map { |id| model_lookup[id] }
|
96
112
|
end
|
97
113
|
private :ordered_results
|
98
114
|
|
115
|
+
# Note: the multi-search functionality kicks along a
|
116
|
+
# type value which is used to perform lookups on the
|
117
|
+
# appropriate underlying model.
|
118
|
+
def ordered_results_multi(ids_hash)
|
119
|
+
model_lookup = self.find(ids_hash)
|
120
|
+
ids_hash.map { |id_hash| model_lookup[id_hash[:id]] }
|
121
|
+
end
|
122
|
+
private :ordered_results_multi
|
123
|
+
|
99
124
|
# sanitize a search query for Lucene. Useful if the original
|
100
125
|
# query raises an exception due to invalid DSL parse.
|
101
126
|
#
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ElasticMapper::MultiSearch do
|
6
|
+
|
7
|
+
class SearchModel < ActiveHash::Base
|
8
|
+
include ElasticMapper
|
9
|
+
attr_accessor :foo, :bar
|
10
|
+
|
11
|
+
mapping :foo, :bar
|
12
|
+
mapping_name :index_models
|
13
|
+
end
|
14
|
+
|
15
|
+
class SearchModelTwo < ActiveHash::Base
|
16
|
+
include ElasticMapper
|
17
|
+
attr_accessor :foo, :bar
|
18
|
+
|
19
|
+
mapping :foo, :bar
|
20
|
+
mapping_name :index_models_two
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "search" do
|
24
|
+
before(:each) do
|
25
|
+
reset_index
|
26
|
+
SearchModel.put_mapping
|
27
|
+
SearchModelTwo.put_mapping
|
28
|
+
end
|
29
|
+
let(:d1) { SearchModel.create(foo: 'hello world', bar: 'goodnight moon') }
|
30
|
+
let(:d2) { SearchModelTwo.create(foo: 'alpha century', bar: 'mars') }
|
31
|
+
let(:d3) { SearchModel.create(foo: 'cat lover') }
|
32
|
+
before(:each) do
|
33
|
+
index(d1)
|
34
|
+
index(d2)
|
35
|
+
index(d3)
|
36
|
+
end
|
37
|
+
|
38
|
+
context "search by query string" do
|
39
|
+
it "returns documents matching a query string" do
|
40
|
+
multi = ElasticMapper::MultiSearch.new({
|
41
|
+
index_models: SearchModel,
|
42
|
+
index_models_two: SearchModelTwo
|
43
|
+
})
|
44
|
+
|
45
|
+
results = multi.search('alpha')
|
46
|
+
results.documents.count.should == 1
|
47
|
+
results.documents.first.foo.should == 'alpha century'
|
48
|
+
results.documents.first.should be_a(SearchModelTwo)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "supports elasticsearch query DSL" do
|
52
|
+
multi = ElasticMapper::MultiSearch.new({
|
53
|
+
index_models: SearchModel,
|
54
|
+
index_models_two: SearchModelTwo
|
55
|
+
})
|
56
|
+
|
57
|
+
results = multi.search('*')
|
58
|
+
results.documents.count.should == 3
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "sort" do
|
63
|
+
it "can sort in descending order" do
|
64
|
+
multi = ElasticMapper::MultiSearch.new({
|
65
|
+
index_models: SearchModel,
|
66
|
+
index_models_two: SearchModelTwo
|
67
|
+
})
|
68
|
+
|
69
|
+
results = multi.search('*', sort: { :foo => :desc })
|
70
|
+
results.documents.first.foo.should == 'hello world'
|
71
|
+
results.documents.first.should be_a(SearchModel)
|
72
|
+
|
73
|
+
results.documents.second.foo.should == 'cat lover'
|
74
|
+
results.documents.second.should be_a(SearchModel)
|
75
|
+
|
76
|
+
results.documents.third.foo.should == 'alpha century'
|
77
|
+
results.documents.third.should be_a(SearchModelTwo)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "can sort in ascending order" do
|
81
|
+
multi = ElasticMapper::MultiSearch.new({
|
82
|
+
index_models: SearchModel,
|
83
|
+
index_models_two: SearchModelTwo
|
84
|
+
})
|
85
|
+
|
86
|
+
results = multi.search('*', sort: { :foo => :asc })
|
87
|
+
results.documents.first.foo.should == 'alpha century'
|
88
|
+
results.documents.first.should be_a(SearchModelTwo)
|
89
|
+
|
90
|
+
results.documents.second.foo.should == 'cat lover'
|
91
|
+
results.documents.second.should be_a(SearchModel)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "pagination" do
|
96
|
+
it "allows result size to be set with size" do
|
97
|
+
multi = ElasticMapper::MultiSearch.new({
|
98
|
+
index_models: SearchModel,
|
99
|
+
index_models_two: SearchModelTwo
|
100
|
+
})
|
101
|
+
|
102
|
+
results = multi.search('* OR alpha', size: 1)
|
103
|
+
results.documents.count.should == 1
|
104
|
+
results.documents.first.foo.should == 'alpha century'
|
105
|
+
results.documents.first.should be_a(SearchModelTwo)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "allows documents to be skipped with from" do
|
109
|
+
multi = ElasticMapper::MultiSearch.new({
|
110
|
+
index_models: SearchModel,
|
111
|
+
index_models_two: SearchModelTwo
|
112
|
+
})
|
113
|
+
|
114
|
+
results = multi.search({ "query_string" => { "query" => '* OR alpha' } }, size: 1, from: 1)
|
115
|
+
results.total.should == 3
|
116
|
+
results.from.should == 1
|
117
|
+
results.documents.count.should == 1
|
118
|
+
results.documents.first.foo.should == 'hello world'
|
119
|
+
results.documents.first.should be_a(SearchModel)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -15,7 +15,7 @@ describe ElasticMapper::Search do
|
|
15
15
|
describe "search" do
|
16
16
|
before(:each) do
|
17
17
|
reset_index
|
18
|
-
|
18
|
+
SearchModel.put_mapping
|
19
19
|
end
|
20
20
|
let(:d1) { SearchModel.create(foo: 'hello world', bar: 'goodnight moon') }
|
21
21
|
let(:d2) { SearchModel.create(foo: 'alpha century', bar: 'mars') }
|
@@ -50,7 +50,7 @@ describe ElasticMapper::Search do
|
|
50
50
|
it "returns documents matching the hash query" do
|
51
51
|
results = SearchModel.search({ "query_string" => { "query" => 'alpha' } })
|
52
52
|
results.documents.count.should == 1
|
53
|
-
results.documents.first.foo.should == 'alpha century'
|
53
|
+
results.documents.first.foo.should == 'alpha century'
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elastic_mapper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin Coe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: stretcher
|
@@ -140,10 +140,12 @@ files:
|
|
140
140
|
- lib/elastic_mapper.rb
|
141
141
|
- lib/elastic_mapper/index.rb
|
142
142
|
- lib/elastic_mapper/mapping.rb
|
143
|
+
- lib/elastic_mapper/multi_search.rb
|
143
144
|
- lib/elastic_mapper/search.rb
|
144
145
|
- lib/elastic_mapper/version.rb
|
145
146
|
- spec/elastic_mapper/index_spec.rb
|
146
147
|
- spec/elastic_mapper/mapping_spec.rb
|
148
|
+
- spec/elastic_mapper/multi_search_spec.rb
|
147
149
|
- spec/elastic_mapper/search_spec.rb
|
148
150
|
- spec/spec_helper.rb
|
149
151
|
homepage: ''
|
@@ -173,5 +175,6 @@ summary: A dead simple DSL for integrating ActiveModel with ElasticSearch.
|
|
173
175
|
test_files:
|
174
176
|
- spec/elastic_mapper/index_spec.rb
|
175
177
|
- spec/elastic_mapper/mapping_spec.rb
|
178
|
+
- spec/elastic_mapper/multi_search_spec.rb
|
176
179
|
- spec/elastic_mapper/search_spec.rb
|
177
180
|
- spec/spec_helper.rb
|