search_magic 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock
CHANGED
@@ -9,20 +9,11 @@ module SearchMagic
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def search_on(field_name, options = {})
|
12
|
-
|
13
|
-
send(:searchable_fields)[field_name] = Metadata.new(:field_name => field_name, :field => fields[field_name.to_s], :association => metadata, :options => options)
|
12
|
+
searchable_fields[field_name] = options
|
14
13
|
end
|
15
14
|
|
16
15
|
def searchables
|
17
|
-
@searchables ||=
|
18
|
-
hash.keys.each do |name|
|
19
|
-
if self.method_defined?(name) && reflect_on_association(name).nil?
|
20
|
-
alias_method(:"_#{name}", name)
|
21
|
-
else
|
22
|
-
define_method(:"_#{name}") {find_searchable_value(name)}
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
16
|
+
@searchables ||= create_searchables
|
26
17
|
end
|
27
18
|
|
28
19
|
def search(pattern)
|
@@ -39,13 +30,36 @@ module SearchMagic
|
|
39
30
|
criteria
|
40
31
|
end
|
41
32
|
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def create_searchables
|
37
|
+
fields = searchable_fields.map do |field_name, options|
|
38
|
+
if association = reflect_on_association(field_name)
|
39
|
+
options[:as] ||= nil
|
40
|
+
only = [options[:only]].flatten.compact
|
41
|
+
except = [options[:except]].flatten.compact
|
42
|
+
associated = association.class_name.constantize.searchables.reject {|key, value| except.include?(key) }.select {|key, value| only.blank? ? true : only.include?(key) }
|
43
|
+
associated.map do |name, metadata|
|
44
|
+
Metadata.new(:type => self, :through => lambda do |obj|
|
45
|
+
value = obj.send(field_name)
|
46
|
+
value.is_a?(Array) ? value.map {|item| metadata.through.call(item)} : metadata.through.call(value)
|
47
|
+
end, :prefix => field_name.to_s.singularize.to_sym, :field_name => name, :options => metadata.options.merge(options))
|
48
|
+
end
|
49
|
+
else
|
50
|
+
Metadata.new(:type => self, :through => lambda {|obj| obj.send(field_name) }, :field_name => field_name.to_s.pluralize.singularize.to_sym, :options => options)
|
51
|
+
end
|
52
|
+
end.flatten
|
53
|
+
|
54
|
+
Hash[*fields.map {|metadata| [metadata.name, metadata]}.flatten]
|
55
|
+
end
|
42
56
|
end
|
43
57
|
|
44
58
|
module InstanceMethods
|
45
59
|
private
|
46
60
|
|
47
61
|
def update_searchable_values
|
48
|
-
|
62
|
+
self.searchable_values = self.class.searchables.values.map {|metadata| metadata.searchable_value_for(self)}.flatten
|
49
63
|
end
|
50
64
|
|
51
65
|
def find_searchable_value(name)
|
@@ -55,44 +69,28 @@ module SearchMagic
|
|
55
69
|
end
|
56
70
|
|
57
71
|
class Metadata
|
58
|
-
attr_accessor :
|
72
|
+
attr_accessor :type, :through, :prefix, :field_name, :options
|
59
73
|
|
60
74
|
def initialize(attributes = {})
|
61
75
|
attributes.each do |key, value|
|
62
76
|
send(:"#{key}=", value)
|
63
77
|
end
|
64
|
-
options[:only] = [options[:only]].flatten.compact
|
65
|
-
options[:except] = [options[:except]].flatten.compact
|
66
78
|
end
|
67
79
|
|
68
|
-
def
|
69
|
-
|
80
|
+
def name
|
81
|
+
@name ||= [options[:skip_prefix].presence ? nil : (prefix.present? ? options[:as] || prefix : nil),
|
82
|
+
prefix.present? ? field_name : (options[:as] || field_name)].compact.join("_").to_sym
|
70
83
|
end
|
71
84
|
|
72
|
-
def
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
case self.association.try(:macro)
|
78
|
-
when nil
|
79
|
-
[[self.field.type == Array ? name.to_s.singularize.to_sym : name, value, nil, self.options]]
|
80
|
-
when :embedded_in, :embeds_one, :referenced_in, :references_one
|
81
|
-
fields.map {|sub_name| [create_nested_name(name, sub_name), value, sub_name, self.options.merge(sub_fields[sub_name])]}
|
82
|
-
else
|
83
|
-
fields.map {|sub_name| [create_nested_name(name.to_s.singularize, sub_name.to_s.pluralize), value, sub_name, self.options.merge(sub_fields[sub_name])]}
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def create_nested_name(owning_name, sub_name)
|
88
|
-
[owning_name, sub_name].compact.join("_").to_sym
|
85
|
+
def value_for(obj)
|
86
|
+
v = self.through.call(obj)
|
87
|
+
v = v.is_a?(Array) ? v.join(" ") : v.to_s
|
88
|
+
v = v.gsub(/[[:punct:]]/, ' ') unless options[:keep_punctuation]
|
89
|
+
v
|
89
90
|
end
|
90
91
|
|
91
|
-
def
|
92
|
-
|
93
|
-
v = v.is_a?(Array) ? v.join(" ") : v.to_s
|
94
|
-
v = v.gsub(/[[:punct:]]/, '') unless options[:keep_punctuation]
|
95
|
-
v.downcase.split.map {|word| [searchable_name, word].join(":")}
|
92
|
+
def searchable_value_for(obj)
|
93
|
+
value_for(obj).downcase.split.map {|word| [name, word].join(":")}
|
96
94
|
end
|
97
95
|
end
|
98
96
|
|
data/lib/search_magic/version.rb
CHANGED
@@ -6,13 +6,14 @@ describe SearchMagic::FullTextSearch do
|
|
6
6
|
it { subject.searchable_fields.keys.should include(:address) }
|
7
7
|
it { subject.searchable_fields.keys.should include(:phones) }
|
8
8
|
it { should respond_to(:searchables)}
|
9
|
-
its(
|
9
|
+
its("searchables.keys") { should include(:address_street, :address_city, :address_state, :address_post_code, :mobile_number)}
|
10
|
+
its("searchables.keys") { should_not include(:mobile_country_code)}
|
10
11
|
end
|
11
12
|
|
12
13
|
describe "model includes referenced document fields in :searchable_fields" do
|
13
14
|
subject { Part }
|
14
15
|
it { subject.searchable_fields.keys.should include(:part_number) }
|
15
|
-
its(
|
16
|
+
its("searchables.keys") { should include(:part_number, :category_name) }
|
16
17
|
end
|
17
18
|
|
18
19
|
context "when a model embeds one other document" do
|
@@ -26,12 +27,6 @@ describe SearchMagic::FullTextSearch do
|
|
26
27
|
its(:address) { should_not be_nil }
|
27
28
|
its(:phones) { should_not be_empty }
|
28
29
|
its(:searchable_values) { should include("address_street:123", "address_street:example", "address_street:st") }
|
29
|
-
it { should respond_to(:_address_street, :_address_city, :_address_state, :_address_post_code, :_mobile_numbers)}
|
30
|
-
its(:_address_street) { should include("123", "example", "st") }
|
31
|
-
its(:_address_state) { should == "ca" }
|
32
|
-
its(:_address_city) { should == "nowhereland" }
|
33
|
-
its(:_address_post_code) { should == "12345" }
|
34
|
-
its(:_mobile_numbers) { should include("555-1234", "333-7890") }
|
35
30
|
end
|
36
31
|
|
37
32
|
context "when searching for 'address_city:nowhereland'" do
|
@@ -46,6 +41,23 @@ describe SearchMagic::FullTextSearch do
|
|
46
41
|
its(:count) { should == 1 }
|
47
42
|
its("first.name") { should == "Samuel" }
|
48
43
|
end
|
44
|
+
|
45
|
+
context "when searching for 'mobile_number:555-'" do
|
46
|
+
subject { Person.search("mobile_number:555-") }
|
47
|
+
its(:count) { should == 2 }
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when searching for 'mobile_number:333-'" do
|
51
|
+
subject { Person.search("mobile_number:333-") }
|
52
|
+
its(:count) { should == 1 }
|
53
|
+
its("first.name") { should == "Joshua" }
|
54
|
+
end
|
55
|
+
|
56
|
+
context "when searching for 'mobile_number:-0987'" do
|
57
|
+
subject { Person.search("mobile_number:-0987") }
|
58
|
+
its(:count) { should == 1 }
|
59
|
+
its("first.name") { should == "Samuel" }
|
60
|
+
end
|
49
61
|
end
|
50
62
|
|
51
63
|
context "when a model references other documents" do
|
@@ -100,10 +112,6 @@ describe SearchMagic::FullTextSearch do
|
|
100
112
|
it { should be }
|
101
113
|
its(:part_number) { should_not be_nil }
|
102
114
|
its(:searchable_values) { should include("part_number:t11001", "category_name:table", "status:available", "serial:t0411001") }
|
103
|
-
its(:_part_number) { should == "t11001" }
|
104
|
-
its(:_category_name) { should == "table" }
|
105
|
-
its(:_status) { should == "available" }
|
106
|
-
its(:_serial) { should == "T0411001" }
|
107
115
|
end
|
108
116
|
|
109
117
|
context "when searching for 'category_name:table'" do
|
@@ -25,8 +25,8 @@ describe SearchMagic::FullTextSearch do
|
|
25
25
|
its("searchable_fields.keys") { should include(:title, :description, :tags) }
|
26
26
|
its("searchable_fields.keys") { should_not include(:uuid) }
|
27
27
|
|
28
|
-
its(
|
29
|
-
its(
|
28
|
+
its("searchables.keys") { should include(:title, :description, :tag) }
|
29
|
+
its("searchables.keys") { should_not include(:uuid) }
|
30
30
|
end
|
31
31
|
|
32
32
|
describe "saving a model should run the :update_searchable_values callback" do
|