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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ef34e64a60623b87c60e8477416604d19138a10
4
- data.tar.gz: b55837db1a514c41e356618b1e2681115e08a8fa
3
+ metadata.gz: 543ec27590a91159027e57f2ebd0f688608982eb
4
+ data.tar.gz: 6eed8ee0decba2a9bbc29dd013871c1e0065891e
5
5
  SHA512:
6
- metadata.gz: 1c9bdd0df778f890e22fe167a7eb5c4239ab7cf55e4910bd3661da841c96cb346bef64b633409b178b9b6eea961c70e035af937b905b4bff4c133160df73abf7
7
- data.tar.gz: 91ecda07f660e90de9f2f2ab9819154a1f9189b78a8ab25a81e76d14d84ab70d765017d32fe0cbb90bb5493f4cc6ff8c15de0d6383c7c57e5f5a31103e633fbe
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.2.1)
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 (2.1.1)
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
 
@@ -4,6 +4,7 @@ require "elastic_mapper/version"
4
4
  require "elastic_mapper/mapping"
5
5
  require "elastic_mapper/index"
6
6
  require "elastic_mapper/search"
7
+ require "elastic_mapper/multi_search"
7
8
 
8
9
  module ElasticMapper
9
10
 
@@ -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: ordered_results(res.results.map(&:id)),
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
  #
@@ -1,3 +1,3 @@
1
1
  module ElasticMapper
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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
- IndexModel.put_mapping
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.2.1
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-05-26 00:00:00.000000000 Z
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