mongoid_search 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +22 -1
- data/lib/mongoid_search/mongoid_search.rb +23 -13
- data/lib/mongoid_search/util.rb +2 -2
- data/spec/models/product.rb +2 -1
- data/spec/models/variant.rb +4 -0
- data/spec/mongoid_search_spec.rb +33 -3
- data/spec/util_spec.rb +4 -0
- metadata +5 -3
data/README.md
CHANGED
@@ -62,6 +62,15 @@ Note that the search is case insensitive, and accept partial searching too:
|
|
62
62
|
|
63
63
|
Product.search("ipho").size
|
64
64
|
=> 1
|
65
|
+
|
66
|
+
Assuming you have a category with multiple products you can now use the following
|
67
|
+
code to search for 'iphone' in products cheaper than $499
|
68
|
+
|
69
|
+
@category.products.where(:price.lt => 499).asc(:price).csearch('iphone')
|
70
|
+
|
71
|
+
In this case we have to use csearch, an alias for search, because since v2.0.0
|
72
|
+
Mongoid defines it's own Criteria.search method.
|
73
|
+
|
65
74
|
|
66
75
|
Options
|
67
76
|
-------
|
@@ -90,11 +99,23 @@ allow_empty_search:
|
|
90
99
|
|
91
100
|
Product.search("").size
|
92
101
|
=> 1
|
102
|
+
|
103
|
+
ignore_list:
|
104
|
+
Pass in an ignore list location. Keywords in that list will be ignored.
|
105
|
+
|
106
|
+
search_in :brand, :name, { :tags => :name }, { :ignore_list => Rails.root.join("config", "ignorelist.yml") }
|
107
|
+
|
108
|
+
The list should look like:
|
109
|
+
|
110
|
+
ignorelist:
|
111
|
+
a, an, to, from, as
|
112
|
+
|
113
|
+
You can include how many keywords you like.
|
93
114
|
|
94
115
|
TODO
|
95
116
|
----
|
96
117
|
|
97
118
|
* Strip html with sanitize (https://github.com/rgrove/sanitize)
|
98
|
-
*
|
119
|
+
* Rewrite and test relevant search
|
99
120
|
* Move all configurations to a configuration file. Maybe /config/mongoid_search.yml.
|
100
121
|
|
@@ -14,7 +14,7 @@ module Mongoid::Search
|
|
14
14
|
self.relevant_search = [true, false].include?(options[:relevant_search]) ? options[:allow_empty_search] : false
|
15
15
|
self.stem_keywords = [true, false].include?(options[:stem_keywords]) ? options[:allow_empty_search] : false
|
16
16
|
self.ignore_list = YAML.load(File.open(options[:ignore_list]))["ignorelist"] if options[:ignore_list].present?
|
17
|
-
self.search_fields = args
|
17
|
+
self.search_fields = (self.search_fields || []).concat args
|
18
18
|
|
19
19
|
field :_keywords, :type => Array
|
20
20
|
index :_keywords
|
@@ -29,14 +29,20 @@ module Mongoid::Search
|
|
29
29
|
search_without_relevance(query, options)
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
# Mongoid 2.0.0 introduces Criteria.seach so we need to provide
|
34
|
+
# alternate method
|
35
|
+
alias csearch search
|
32
36
|
|
33
37
|
def search_without_relevance(query, options={})
|
34
|
-
return
|
35
|
-
|
38
|
+
return criteria.all if query.blank? && allow_empty_search
|
39
|
+
criteria.send("#{(options[:match]||self.match).to_s}_in", :_keywords => Util.keywords(query, stem_keywords, ignore_list).map { |q| /#{q}/ })
|
36
40
|
end
|
37
|
-
|
41
|
+
|
42
|
+
# I know what this method should do, but I don't really know what it does.
|
43
|
+
# It was a pull from another fork, with no tests on it. Proably should be rewrited (and tested).
|
38
44
|
def search_relevant(query, options={})
|
39
|
-
return
|
45
|
+
return criteria.all if query.blank? && allow_empty_search
|
40
46
|
|
41
47
|
keywords = Util.keywords(query, stem_keywords, ignore_list)
|
42
48
|
|
@@ -64,7 +70,7 @@ module Mongoid::Search
|
|
64
70
|
{:_keywords => kw}
|
65
71
|
end
|
66
72
|
|
67
|
-
criteria = self.any_of(*kw_conditions)
|
73
|
+
criteria = (criteria || self).any_of(*kw_conditions)
|
68
74
|
|
69
75
|
query = criteria.selector
|
70
76
|
|
@@ -72,15 +78,15 @@ module Mongoid::Search
|
|
72
78
|
options.delete(:skip)
|
73
79
|
options.merge! :scope => {:keywords => keywords}, :query => query
|
74
80
|
|
75
|
-
res = collection.map_reduce(map, reduce, options)
|
76
|
-
|
77
|
-
|
81
|
+
# res = collection.map_reduce(map, reduce, options)
|
82
|
+
# res.find.sort(['value', -1]) # Cursor
|
83
|
+
collection.map_reduce(map, reduce, options)
|
78
84
|
end
|
79
85
|
end
|
80
86
|
|
81
87
|
private
|
82
88
|
|
83
|
-
# TODO: This need some
|
89
|
+
# TODO: This need some refactoring..
|
84
90
|
def set_keywords
|
85
91
|
self._keywords = self.search_fields.map do |field|
|
86
92
|
if field.is_a?(Hash)
|
@@ -98,9 +104,13 @@ module Mongoid::Search
|
|
98
104
|
end
|
99
105
|
end
|
100
106
|
else
|
101
|
-
|
102
|
-
|
107
|
+
value = self[field]
|
108
|
+
if value.is_a?(Array)
|
109
|
+
value.each {|v| Util.keywords(v, stem_keywords, ignore_list) if v}
|
110
|
+
else
|
111
|
+
Util.keywords(value, stem_keywords, ignore_list) if value
|
112
|
+
end
|
103
113
|
end
|
104
|
-
end.flatten.
|
114
|
+
end.flatten.map(&:to_s).select{|f| not f.empty? }.uniq.sort
|
105
115
|
end
|
106
116
|
end
|
data/lib/mongoid_search/util.rb
CHANGED
@@ -2,12 +2,12 @@ module Util
|
|
2
2
|
|
3
3
|
def self.keywords(text, stem_keywords, ignore_list)
|
4
4
|
return [] if text.blank?
|
5
|
-
text = text.
|
5
|
+
text = text.to_s.
|
6
6
|
mb_chars.
|
7
7
|
normalize(:kd).
|
8
8
|
to_s.
|
9
9
|
gsub(/[._:;'"`,?|+={}()!@#%^&*<>~\$\-\\\/\[\]]/, ' '). # strip punctuation
|
10
|
-
gsub(/[^[:
|
10
|
+
gsub(/[^[:alnum:]\s]/,''). # strip accents
|
11
11
|
downcase.
|
12
12
|
split(' ').
|
13
13
|
reject { |word| word.size < 2 }
|
data/spec/models/product.rb
CHANGED
@@ -3,10 +3,11 @@ class Product
|
|
3
3
|
include Mongoid::Search
|
4
4
|
field :brand
|
5
5
|
field :name
|
6
|
+
field :attrs, :type => Array
|
6
7
|
|
7
8
|
references_many :tags
|
8
9
|
referenced_in :category
|
9
10
|
embeds_many :subproducts
|
10
11
|
|
11
|
-
search_in :brand, :name, :outlet, :tags => :name, :category => :name, :subproducts => [:brand, :name]
|
12
|
+
search_in :brand, :name, :outlet, :attrs, :tags => :name, :category => :name, :subproducts => [:brand, :name]
|
12
13
|
end
|
data/spec/mongoid_search_spec.rb
CHANGED
@@ -31,21 +31,45 @@ describe Mongoid::Search do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
it "should set the _keywords field" do
|
34
|
-
@product.
|
34
|
+
@product.attrs = ['lightweight', 'rugged', :red]
|
35
|
+
@product.save!
|
36
|
+
@product._keywords.should include "amazing", "apple", "awesome", "craddle", "iphone", "mobile", "ole", "lightweight", "rugged", "red"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should inherit _keywords field and build upon" do
|
40
|
+
variant = Variant.create :brand => "Apple",
|
41
|
+
:name => "iPhone",
|
42
|
+
:tags => ["Amazing", "Awesome", "Olé"].map { |tag| Tag.new(:name => tag) },
|
43
|
+
:category => Category.new(:name => "Mobile"),
|
44
|
+
:subproducts => [Subproduct.new(:brand => "Apple", :name => "Craddle")],
|
45
|
+
:color => :white
|
46
|
+
variant._keywords.should include "amazing", "apple", "awesome", "craddle", "iphone", "mobile", "ole", "white"
|
47
|
+
Variant.search(:name => 'Apple', :color => :white).size.should == 1
|
35
48
|
end
|
36
49
|
|
37
50
|
it "should set the _keywords field with stemmed words if stem is enabled" do
|
38
51
|
Product.stem_keywords = true
|
39
52
|
@product.save!
|
40
|
-
@product._keywords.should == ["amaz", "appl", "
|
53
|
+
@product._keywords.should == ["amaz", "appl", "awesom", "craddl", "iphon", "mobil", "ol"]
|
41
54
|
end
|
42
55
|
|
43
56
|
it "should ignore keywords in an ignore list" do
|
44
57
|
Product.ignore_list = YAML.load(File.open(File.dirname(__FILE__) + '/config/ignorelist.yml'))["ignorelist"]
|
45
58
|
@product.save!
|
46
|
-
@product._keywords.should == ["apple", "
|
59
|
+
@product._keywords.should == ["apple", "craddle", "iphone", "mobile", "ole"]
|
47
60
|
end
|
48
61
|
|
62
|
+
it "should incorporate numbers as keywords" do
|
63
|
+
@product = Product.create :brand => "Ford",
|
64
|
+
:name => "T 1908",
|
65
|
+
:tags => ["Amazing", "First", "Car"].map { |tag| Tag.new(:name => tag) },
|
66
|
+
:category => Category.new(:name => "Vehicle")
|
67
|
+
|
68
|
+
@product.save!
|
69
|
+
@product._keywords.should == ["1908","amazing", "car", "first", "ford", "vehicle"]
|
70
|
+
end
|
71
|
+
|
72
|
+
|
49
73
|
it "should return results in search" do
|
50
74
|
Product.search("apple").size.should == 1
|
51
75
|
end
|
@@ -99,4 +123,10 @@ describe Mongoid::Search do
|
|
99
123
|
it "should search for embedded documents" do
|
100
124
|
Product.search("craddle").size.should == 1
|
101
125
|
end
|
126
|
+
|
127
|
+
it 'should work in a chainable fashion' do
|
128
|
+
@product.category.products.where(:brand => 'Apple').csearch('apple').size.should == 1
|
129
|
+
@product.category.products.csearch('craddle').where(:brand => 'Apple').size.should == 1
|
130
|
+
end
|
131
|
+
|
102
132
|
end
|
data/spec/util_spec.rb
CHANGED
@@ -37,4 +37,8 @@ describe Util do
|
|
37
37
|
it "should ignore keywords with less than two words" do
|
38
38
|
Util.keywords("A runner running", false, "").should_not include "a"
|
39
39
|
end
|
40
|
+
|
41
|
+
it "should not ignore numbers" do
|
42
|
+
Util.keywords("Ford T 1908", false, "").should include "1908"
|
43
|
+
end
|
40
44
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 4
|
9
|
+
version: 0.2.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Mauricio Zaffari
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-04-
|
17
|
+
date: 2011-04-14 00:00:00 -03:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -127,6 +127,7 @@ files:
|
|
127
127
|
- spec/models/product.rb
|
128
128
|
- spec/models/subproduct.rb
|
129
129
|
- spec/models/tag.rb
|
130
|
+
- spec/models/variant.rb
|
130
131
|
- spec/mongoid_search_spec.rb
|
131
132
|
- spec/spec_helper.rb
|
132
133
|
- spec/util_spec.rb
|
@@ -170,6 +171,7 @@ test_files:
|
|
170
171
|
- spec/models/product.rb
|
171
172
|
- spec/models/subproduct.rb
|
172
173
|
- spec/models/tag.rb
|
174
|
+
- spec/models/variant.rb
|
173
175
|
- spec/mongoid_search_spec.rb
|
174
176
|
- spec/spec_helper.rb
|
175
177
|
- spec/util_spec.rb
|