mongoid_search 0.3.2 → 0.4.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 +7 -0
- data/LICENSE +1 -1
- data/README.md +179 -116
- data/Rakefile +5 -2
- data/lib/mongoid_search.rb +14 -7
- data/lib/mongoid_search/log.rb +2 -1
- data/lib/mongoid_search/mongoid_search.rb +61 -42
- data/lib/mongoid_search/railtie.rb +2 -5
- data/lib/mongoid_search/tasks/mongoid_search.rake +16 -0
- data/lib/mongoid_search/util.rb +19 -18
- data/spec/models/category.rb +2 -0
- data/spec/models/product.rb +15 -6
- data/spec/models/subproduct.rb +2 -1
- data/spec/models/tag.rb +3 -2
- data/spec/models/variant.rb +3 -1
- data/spec/mongoid_search_spec.rb +231 -133
- data/spec/spec_helper.rb +4 -1
- data/spec/util_spec.rb +28 -29
- metadata +56 -54
- data/VERSION +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4f3da97e4dd6f65400548a1e6f12e957fe0c2d5c8a390e015cd8d20572474c73
|
4
|
+
data.tar.gz: 71423ee292ac1f9e53cb4ef4171d4309c25ca56895203bd53c6bc2d8641fb385
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 59b2511bea3017819e52c2633e8ac8cc19158e7da43042584b9fccc4f815b2a2e57fa4b1407e2ee5726d36710473f0cc9143b7b750586c802658869a0c6f7c23
|
7
|
+
data.tar.gz: 65bfb18a5361a333d815b92bafc34bbe5133c04b28c9e4540397605f98eb76123338119b2b5ee911b5074e85d68d84cc6547ab059845bd4698eb0b5beaaf2f02
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,168 +1,231 @@
|
|
1
|
-
Mongoid Search
|
2
|
-
============
|
1
|
+
# Mongoid Search
|
3
2
|
|
4
|
-
Mongoid Search is a simple full text search implementation for Mongoid ORM. It performs well for small data sets. If your searchable model is big (i.e. 1.000.000+ records),
|
3
|
+
Mongoid Search is a simple full text search implementation for Mongoid ORM. It supports Mongoid 3, 4, 5 and 6 and performs well for small data sets. If your searchable model is big (i.e. 1.000.000+ records), [mongoid_fulltext](https://github.com/mongoid/mongoid_fulltext), ElasticSearch, Solr or Sphinx may suit you better.
|
5
4
|
|
6
|
-
|
7
|
-
--------
|
5
|
+
[](https://travis-ci.org/mongoid/mongoid_search)
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
gem 'mongoid_search'
|
7
|
+
## Installation
|
12
8
|
|
13
|
-
|
9
|
+
In your Gemfile:
|
14
10
|
|
15
|
-
|
11
|
+
```ruby
|
12
|
+
gem 'mongoid_search'
|
13
|
+
```
|
16
14
|
|
17
15
|
Then:
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
17
|
+
```
|
18
|
+
bundle install
|
19
|
+
```
|
20
|
+
|
21
|
+
## Examples
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class Product
|
25
|
+
include Mongoid::Document
|
26
|
+
include Mongoid::Search
|
27
|
+
field :brand
|
28
|
+
field :name
|
29
|
+
field :unit
|
30
|
+
field :info, type: Hash
|
31
|
+
|
32
|
+
has_many :tags
|
33
|
+
belongs_to :category
|
34
|
+
|
35
|
+
search_in :brand, :name, tags: :name, category: :name, info: %i[summary description]
|
36
|
+
search_in :unit, index: :_unit_keywords
|
37
|
+
end
|
38
|
+
|
39
|
+
class Tag
|
40
|
+
include Mongoid::Document
|
41
|
+
field :name
|
42
|
+
|
43
|
+
belongs_to :product
|
44
|
+
end
|
45
|
+
|
46
|
+
class Category
|
47
|
+
include Mongoid::Document
|
48
|
+
field :name
|
49
|
+
|
50
|
+
has_many :products
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Now when you save a product, you get a `_keywords` field automatically:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
p = Product.new brand: 'Apple', name: 'iPhone', unit: 'kilogram', info: { summary: 'Info-summary', description: 'Info-description' }
|
58
|
+
p.tags << Tag.new(name: 'Amazing')
|
59
|
+
p.tags << Tag.new(name: 'Awesome')
|
60
|
+
p.tags << Tag.new(name: 'Superb')
|
61
|
+
p.save
|
62
|
+
# => true
|
63
|
+
p._keywords
|
64
|
+
# => ["amazing", "apple", "awesome", "iphone", "superb", "Info-summary", "Info-description"]
|
65
|
+
p._unit_keywords
|
66
|
+
# => ["kilogram"]
|
67
|
+
```
|
68
|
+
|
69
|
+
Now you can run search, which will look in the `_keywords` field and return all matching results:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
Product.full_text_search("apple iphone").size
|
73
|
+
# => 1
|
74
|
+
```
|
75
|
+
|
76
|
+
You can also search in "virtual" fields by defining them as methods. This can be useful when you have a method with dynamic fields (i.e. variable schema).
|
77
|
+
```ruby
|
78
|
+
class ModelWithDynamicFields
|
79
|
+
|
80
|
+
...
|
81
|
+
|
82
|
+
search_in :search_data
|
83
|
+
|
84
|
+
def search_data
|
85
|
+
# concatenate all String fields' values
|
86
|
+
self.attributes.select{|k,v| v.is_a?(String) }.values.join(' ')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
Mongoid_search will run the method before save and use it's output to populate the `_keywords` field.
|
91
|
+
|
92
|
+
Of course, some models could have more than one index. For instance, two different searches with different fields, so you could even specify from which index should be searched:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
Product.full_text_search("kilogram", index: :_unit_keywords).size
|
96
|
+
# => 1
|
97
|
+
```
|
39
98
|
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
class Category
|
44
|
-
include Mongoid::Document
|
45
|
-
field :name
|
46
|
-
|
47
|
-
has_many :products
|
48
|
-
end
|
49
|
-
|
50
|
-
Now when you save a product, you get a _keywords field automatically:
|
51
|
-
|
52
|
-
p = Product.new :brand => "Apple", :name => "iPhone"
|
53
|
-
p.tags << Tag.new(:name => "Amazing")
|
54
|
-
p.tags << Tag.new(:name => "Awesome")
|
55
|
-
p.tags << Tag.new(:name => "Superb")
|
56
|
-
p.save
|
57
|
-
=> true
|
58
|
-
p._keywords
|
59
|
-
=> ["amazing", "apple", "awesome", "iphone", "superb"]
|
99
|
+
Note that the search is case insensitive, and accept partial searching too:
|
60
100
|
|
61
|
-
|
101
|
+
```ruby
|
102
|
+
Product.full_text_search('ipho').size
|
103
|
+
# => 1
|
104
|
+
```
|
62
105
|
|
63
|
-
|
64
|
-
=> 1
|
106
|
+
Assuming you have a category with multiple products you can use the following code to search for 'iphone' in products cheaper than $499.
|
65
107
|
|
66
|
-
|
108
|
+
```ruby
|
109
|
+
category.products.where(:price.lt => 499).full_text_search('iphone').asc(:price)
|
110
|
+
```
|
67
111
|
|
68
|
-
|
69
|
-
=> 1
|
112
|
+
To index or reindex all existing records, run this rake task
|
70
113
|
|
71
|
-
|
72
|
-
|
114
|
+
```
|
115
|
+
$ rake mongoid_search:index
|
116
|
+
```
|
73
117
|
|
74
|
-
|
118
|
+
## Options
|
75
119
|
|
120
|
+
### match
|
76
121
|
|
77
|
-
|
78
|
-
|
122
|
+
* `:any` - match any occurrence
|
123
|
+
* `:all` - match all occurrences
|
79
124
|
|
80
|
-
|
125
|
+
Default is `:any`.
|
81
126
|
|
82
|
-
|
127
|
+
```ruby
|
128
|
+
Product.full_text_search('apple motorola', match: :any).size
|
129
|
+
# => 1
|
83
130
|
|
84
|
-
|
131
|
+
Product.full_text_search('apple motorola', match: :all).size
|
132
|
+
# => 0
|
133
|
+
```
|
85
134
|
|
86
|
-
|
135
|
+
### allow\_empty\_search
|
87
136
|
|
88
|
-
|
89
|
-
|
137
|
+
* `true` - will return `Model.all`
|
138
|
+
* `false` - will return `[]`
|
90
139
|
|
91
|
-
|
92
|
-
=> 0
|
140
|
+
Default is `false`.
|
93
141
|
|
94
|
-
|
142
|
+
```ruby
|
143
|
+
Product.full_text_search('', allow_empty_search: true).size
|
144
|
+
# => 1
|
145
|
+
```
|
95
146
|
|
96
|
-
|
147
|
+
### relevant_search
|
97
148
|
|
98
|
-
|
149
|
+
* `true` - adds relevance information to the results
|
150
|
+
* `false` - no relevance information
|
99
151
|
|
100
|
-
|
152
|
+
Default is `false`.
|
101
153
|
|
102
|
-
|
103
|
-
|
154
|
+
```ruby
|
155
|
+
Product.full_text_search('amazing apple', relevant_search: true)
|
156
|
+
# => [#<Product _id: 5016e7d16af54efe1c000001, _type: nil, brand: "Apple", name: "iPhone", attrs: nil, info: nil, category_id: nil, _keywords: ["amazing", "apple", "awesome", "iphone", "superb"], relevance: 2.0>]
|
157
|
+
```
|
104
158
|
|
105
|
-
relevant_search
|
159
|
+
Please note that relevant_search will return an Array and not a Criteria object. The search method should always be called in the end of the method chain.
|
106
160
|
|
107
|
-
|
161
|
+
### index
|
108
162
|
|
109
|
-
|
163
|
+
Default is `_keywords`.
|
110
164
|
|
111
|
-
|
165
|
+
```ruby
|
166
|
+
Product.full_text_search('amazing apple', index: :_keywords)
|
167
|
+
# => [#<Product _id: 5016e7d16af54efe1c000001, _type: nil, brand: "Apple", name: "iPhone", unit: "l", attrs: nil, info: nil, category_id: nil, _keywords: ["amazing", "apple", "awesome", "iphone", "superb"], _unit_keywords: ["l"], relevance: 2.0>]
|
112
168
|
|
113
|
-
|
114
|
-
|
169
|
+
Product.full_text_search('kg', index: :_unit_keywords)
|
170
|
+
# => [#<Product _id: 5016e7d16af54efe1c000001, _type: nil, brand: "Apple", name: "iPhone", unit: "kg", attrs: nil, info: nil, category_id: nil, _keywords: ["amazing", "apple", "awesome", "iphone", "superb"], _unit_keywords: ["kg"], relevance: 2.0>]
|
171
|
+
```
|
115
172
|
|
116
|
-
|
173
|
+
index enables to have two or more different searches, with different or same fields. It should be noted that indexes are exclusive per each one.
|
117
174
|
|
118
|
-
Initializer
|
119
|
-
-----------
|
175
|
+
## Initializer
|
120
176
|
|
121
177
|
Alternatively, you can create an initializer to setup those options:
|
122
178
|
|
123
|
-
|
124
|
-
|
125
|
-
|
179
|
+
```ruby
|
180
|
+
Mongoid::Search.setup do |config|
|
181
|
+
## Default matching type. Match :any or :all searched keywords
|
182
|
+
config.match = :any
|
183
|
+
|
184
|
+
## If true, an empty search will return all objects
|
185
|
+
config.allow_empty_search = false
|
126
186
|
|
127
|
-
|
128
|
-
|
187
|
+
## If true, will search with relevance information
|
188
|
+
config.relevant_search = false
|
129
189
|
|
130
|
-
|
131
|
-
|
190
|
+
## Stem keywords
|
191
|
+
config.stem_keywords = false
|
132
192
|
|
133
|
-
|
134
|
-
|
193
|
+
## Add a custom proc returning strings to replace the default stemmer
|
194
|
+
# For example using ruby-stemmer:
|
195
|
+
# config.stem_proc = Proc.new { |word| Lingua.stemmer(word, :language => 'nl') }
|
135
196
|
|
136
|
-
|
137
|
-
|
138
|
-
# config.stem_proc = Proc.new { |word| Lingua.stemmer(word, :language => 'nl') }
|
197
|
+
## Words to ignore
|
198
|
+
config.ignore_list = []
|
139
199
|
|
140
|
-
|
141
|
-
|
200
|
+
## An array of words
|
201
|
+
# config.ignore_list = %w{ a an to from as }
|
142
202
|
|
143
|
-
|
144
|
-
|
203
|
+
## Or from a file
|
204
|
+
# config.ignore_list = YAML.load(File.open(File.dirname(__FILE__) + '/config/ignorelist.yml'))["ignorelist"]
|
145
205
|
|
146
|
-
|
147
|
-
|
206
|
+
## Search using regex (slower)
|
207
|
+
config.regex_search = true
|
148
208
|
|
149
|
-
|
150
|
-
config.regex_search = true
|
209
|
+
## Regex to search
|
151
210
|
|
152
|
-
|
211
|
+
## Match partial words on both sides (slower)
|
212
|
+
config.regex = Proc.new { |query| /#{query}/ }
|
153
213
|
|
154
|
-
|
155
|
-
|
214
|
+
## Match partial words on the beginning or in the end (slightly faster)
|
215
|
+
# config.regex = Proc.new { |query| /^#{query}/ }
|
216
|
+
# config.regex = Proc.new { |query| /#{query}$/ }
|
156
217
|
|
157
|
-
|
158
|
-
|
159
|
-
|
218
|
+
# Ligatures to be replaced
|
219
|
+
# http://en.wikipedia.org/wiki/Typographic_ligature
|
220
|
+
config.ligatures = { "œ"=>"oe", "æ"=>"ae" }
|
160
221
|
|
161
|
-
|
162
|
-
|
163
|
-
config.ligatures = { "œ"=>"oe", "æ"=>"ae" }
|
222
|
+
# Strip symbols regex to be replaced. These symbols will be replaced by space
|
223
|
+
config.strip_symbols = /[._:;'\"`,?|+={}()!@#%^&*<>~\$\-\\\/\[\]]/
|
164
224
|
|
165
|
-
|
166
|
-
|
167
|
-
end
|
225
|
+
# Strip accents regex to be replaced. These sybols will be removed after strip_symbols replacing
|
226
|
+
config.strip_accents = /[^\s\p{Alnum}]/
|
168
227
|
|
228
|
+
# Minimum word size. Words smaller than it won't be indexed
|
229
|
+
config.minimum_word_size = 2
|
230
|
+
end
|
231
|
+
```
|
data/Rakefile
CHANGED
@@ -3,8 +3,11 @@ require 'rake'
|
|
3
3
|
|
4
4
|
require 'rspec/core/rake_task'
|
5
5
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
6
|
-
spec.rspec_opts = [
|
6
|
+
spec.rspec_opts = ['-c', '-f progress']
|
7
7
|
spec.pattern = 'spec/**/*_spec.rb'
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
require 'rubocop/rake_task'
|
11
|
+
RuboCop::RakeTask.new(:rubocop)
|
12
|
+
|
13
|
+
task default: %i[rubocop spec]
|
data/lib/mongoid_search.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
|
1
|
+
require 'mongoid_search/mongoid_search'
|
2
2
|
|
3
3
|
require 'mongoid_search/railtie' if defined?(Rails)
|
4
|
-
require 'mongoid_search/mongoid_search'
|
5
4
|
|
6
5
|
module Mongoid::Search
|
7
6
|
## Default matching type. Match :any or :all searched keywords
|
@@ -22,7 +21,7 @@ module Mongoid::Search
|
|
22
21
|
|
23
22
|
## Stem procedure
|
24
23
|
mattr_accessor :stem_proc
|
25
|
-
@@stem_proc =
|
24
|
+
@@stem_proc = proc { |word| word.stem }
|
26
25
|
|
27
26
|
## Words to ignore
|
28
27
|
mattr_accessor :ignore_list
|
@@ -42,25 +41,33 @@ module Mongoid::Search
|
|
42
41
|
mattr_accessor :regex
|
43
42
|
|
44
43
|
## Match partial words on both sides (slower)
|
45
|
-
@@regex =
|
44
|
+
@@regex = proc { |query| /#{query}/ }
|
46
45
|
|
47
46
|
## Match partial words on the beginning or in the end (slightly faster)
|
48
|
-
# @@regex = Proc.new { |query|
|
47
|
+
# @@regex = Proc.new { |query| /^#{query}/ }
|
49
48
|
# @@regex = Proc.new { |query| /#{query}$/ }
|
50
49
|
|
51
50
|
# Ligatures to be replaced
|
52
51
|
# http://en.wikipedia.org/wiki/Typographic_ligature
|
53
52
|
mattr_accessor :ligatures
|
54
|
-
@@ligatures = {
|
53
|
+
@@ligatures = { 'œ' => 'oe', 'æ' => 'ae', 'ꜵ' => 'ao' }
|
55
54
|
|
56
55
|
# Minimum word size. Words smaller than it won't be indexed
|
57
56
|
mattr_accessor :minimum_word_size
|
58
57
|
@@minimum_word_size = 2
|
59
58
|
|
59
|
+
# Strip special symbols
|
60
|
+
mattr_accessor :strip_symbols
|
61
|
+
@@strip_symbols = /[._:;'\"`,?|+={}()!@#%^&*<>~\$\-\\\/\[\]]/
|
62
|
+
|
63
|
+
# Strip accents
|
64
|
+
mattr_accessor :strip_accents
|
65
|
+
@@strip_accents = /[^\s\p{Alnum}]/
|
66
|
+
|
60
67
|
def self.setup
|
61
68
|
yield self
|
62
69
|
end
|
63
70
|
end
|
64
71
|
|
65
72
|
require 'mongoid_search/util'
|
66
|
-
require 'mongoid_search/log'
|
73
|
+
require 'mongoid_search/log'
|
data/lib/mongoid_search/log.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
module Mongoid::Search
|
2
|
-
|
3
|
-
base.send(:cattr_accessor, :search_fields)
|
4
|
-
|
5
|
-
base.extend ClassMethods
|
2
|
+
extend ActiveSupport::Concern
|
6
3
|
|
4
|
+
included do
|
5
|
+
cattr_accessor :search_fields
|
7
6
|
@@classes ||= []
|
8
|
-
@@classes <<
|
7
|
+
@@classes << self
|
9
8
|
end
|
10
9
|
|
11
10
|
def self.classes
|
@@ -16,17 +15,19 @@ module Mongoid::Search
|
|
16
15
|
# Set a field or a number of fields as sources for search
|
17
16
|
def search_in(*args)
|
18
17
|
args, options = args_and_options(args)
|
19
|
-
|
18
|
+
set_search_fields(options[:index], args)
|
20
19
|
|
21
|
-
field :
|
20
|
+
field options[:index], type: Array
|
22
21
|
|
23
|
-
index({ :
|
22
|
+
index({ options[:index] => 1 }, background: true)
|
24
23
|
|
25
24
|
before_save :set_keywords
|
26
25
|
end
|
27
26
|
|
28
|
-
def full_text_search(query, options={})
|
27
|
+
def full_text_search(query, options = {})
|
29
28
|
options = extract_options(options)
|
29
|
+
attr_accessor :relevance if options[:relevant_search].eql? true
|
30
|
+
|
30
31
|
return (options[:allow_empty_search] ? criteria.all : []) if query.blank?
|
31
32
|
|
32
33
|
if options[:relevant_search]
|
@@ -43,33 +44,46 @@ module Mongoid::Search
|
|
43
44
|
# Goes through all documents in the class that includes Mongoid::Search
|
44
45
|
# and indexes the keywords.
|
45
46
|
def index_keywords!
|
46
|
-
all.each { |d| d.index_keywords! ? Log.green(
|
47
|
+
all.each { |d| d.index_keywords! ? Log.green('.') : Log.red('F') }
|
47
48
|
end
|
48
49
|
|
49
50
|
private
|
51
|
+
|
52
|
+
def set_search_fields(index, fields)
|
53
|
+
self.search_fields ||= {}
|
54
|
+
|
55
|
+
(self.search_fields[index] ||= []).concat fields
|
56
|
+
end
|
57
|
+
|
50
58
|
def query(keywords, options)
|
51
59
|
keywords_hash = keywords.map do |kw|
|
52
|
-
|
53
|
-
|
60
|
+
if Mongoid::Search.regex_search
|
61
|
+
escaped_kw = Regexp.escape(kw)
|
62
|
+
kw = Mongoid::Search.regex.call(escaped_kw)
|
63
|
+
end
|
64
|
+
|
65
|
+
{ options[:index] => kw }
|
54
66
|
end
|
55
67
|
|
56
|
-
criteria.send("#{(options[:match])
|
68
|
+
criteria.send("#{(options[:match])}_of", *keywords_hash)
|
57
69
|
end
|
58
70
|
|
59
71
|
def args_and_options(args)
|
60
72
|
options = args.last.is_a?(Hash) &&
|
61
|
-
|
62
|
-
|
63
|
-
|
73
|
+
%i[match
|
74
|
+
allow_empty_search
|
75
|
+
index
|
76
|
+
relevant_search].include?(args.last.keys.first) ? args.pop : {}
|
64
77
|
|
65
78
|
[args, extract_options(options)]
|
66
79
|
end
|
67
80
|
|
68
81
|
def extract_options(options)
|
69
82
|
{
|
70
|
-
:
|
71
|
-
:
|
72
|
-
:
|
83
|
+
match: options[:match] || Mongoid::Search.match,
|
84
|
+
allow_empty_search: options[:allow_empty_search] || Mongoid::Search.allow_empty_search,
|
85
|
+
relevant_search: options[:relevant_search] || Mongoid::Search.relevant_search,
|
86
|
+
index: options[:index] || :_keywords
|
73
87
|
}
|
74
88
|
end
|
75
89
|
|
@@ -79,52 +93,57 @@ module Mongoid::Search
|
|
79
93
|
|
80
94
|
def search_relevant(query, options)
|
81
95
|
results_with_relevance(query, options).sort { |o| o['value'] }.map do |r|
|
82
|
-
|
83
|
-
new(r['_id'].merge(:relevance => r['value'])) do |o|
|
96
|
+
new(r['_id'].merge(relevance: r['value'])) do |o|
|
84
97
|
# Need to match the actual object
|
85
98
|
o.instance_variable_set('@new_record', false)
|
86
99
|
o._id = r['_id']['_id']
|
87
100
|
end
|
88
|
-
|
89
101
|
end
|
90
102
|
end
|
91
103
|
|
92
104
|
def results_with_relevance(query, options)
|
93
105
|
keywords = Mongoid::Search::Util.normalize_keywords(query)
|
94
106
|
|
95
|
-
map = %
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
107
|
+
map = %{
|
108
|
+
function() {
|
109
|
+
var entries = 0;
|
110
|
+
for(i in keywords) {
|
111
|
+
for(j in this.#{options[:index]}) {
|
112
|
+
if(this.#{options[:index]}[j] == keywords[i]) {
|
113
|
+
entries++;
|
114
|
+
}
|
102
115
|
}
|
103
116
|
}
|
117
|
+
if(entries > 0) {
|
118
|
+
emit(this, entries);
|
119
|
+
}
|
104
120
|
}
|
105
|
-
if(entries > 0) {
|
106
|
-
emit(this, entries);
|
107
|
-
}
|
108
|
-
}
|
109
121
|
}
|
110
122
|
|
111
|
-
reduce = %
|
112
|
-
|
113
|
-
|
114
|
-
|
123
|
+
reduce = %{
|
124
|
+
function(key, values) {
|
125
|
+
return(values);
|
126
|
+
}
|
115
127
|
}
|
116
128
|
|
117
|
-
query(keywords, options).map_reduce(map, reduce).scope(:
|
129
|
+
query(keywords, options).map_reduce(map, reduce).scope(keywords: keywords).out(inline: 1)
|
118
130
|
end
|
119
131
|
end
|
120
132
|
|
121
133
|
def index_keywords!
|
122
|
-
|
134
|
+
search_fields.map do |index, fields|
|
135
|
+
update_attribute(index, get_keywords(fields))
|
136
|
+
end
|
123
137
|
end
|
124
138
|
|
125
|
-
private
|
126
139
|
def set_keywords
|
127
|
-
|
128
|
-
|
140
|
+
search_fields.each do |index, fields|
|
141
|
+
send("#{index}=", get_keywords(fields))
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_keywords(fields)
|
146
|
+
Mongoid::Search::Util.keywords(self, fields)
|
147
|
+
.flatten.reject { |k| k.nil? || k.empty? }.uniq.sort
|
129
148
|
end
|
130
149
|
end
|