search_magic 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.textile +22 -2
- data/lib/search_magic/breadcrumb.rb +20 -0
- data/lib/search_magic/full_text_search.rb +15 -16
- data/lib/search_magic/metadata.rb +11 -6
- data/lib/search_magic/stack_frame.rb +28 -0
- data/lib/search_magic/version.rb +1 -1
- data/lib/search_magic.rb +2 -0
- data/spec/models/graph_1_model_a.rb +8 -0
- data/spec/models/graph_1_model_b.rb +8 -0
- data/spec/unit/search_magic/graph_searches_spec.rb +19 -0
- metadata +10 -2
data/Gemfile.lock
CHANGED
data/README.textile
CHANGED
@@ -86,7 +86,27 @@ end
|
|
86
86
|
|
87
87
|
p. Now all entries within *:searchable_values* for *:tags* will retain meaningful punctuation. The previous example is interesting for another reason: embedded arrays are handled specially. Specifically, the selector for an embedded array will be singularized. In the case of the previous example, this would result in a selector of "tag".
|
88
88
|
|
89
|
-
|
89
|
+
Two documents may search on each other's fields; doing so will cause each document to only search upon those fields stemming from itself once. Given the following example, _Foo_ would be able to search on @[:name, :bar_value]@, while _Bar_ would be able to search on @[:value, :foo_name]@.
|
90
|
+
|
91
|
+
bc.. class Foo
|
92
|
+
include Mongoid::Document
|
93
|
+
include SearchMagic::FullTextSearch
|
94
|
+
field :name
|
95
|
+
references_many :bars
|
96
|
+
search_on :name
|
97
|
+
search_on :bars
|
98
|
+
end
|
99
|
+
|
100
|
+
class Bar
|
101
|
+
include Mongoid::Document
|
102
|
+
include SearchMagic::FullTextSearch
|
103
|
+
field :value
|
104
|
+
referenced_in :foo
|
105
|
+
search_on :value
|
106
|
+
search_on :foo
|
107
|
+
end
|
108
|
+
|
109
|
+
Finally, it should be noted that nesting of searchable documents is possible. If a given document searches on an association with another document which, in and of itself, searches on a third document, the first automatically has access to the third document's searchable fields.
|
90
110
|
|
91
111
|
bc.. class Part
|
92
112
|
include Mongoid::Document
|
@@ -119,7 +139,7 @@ class PartCategory
|
|
119
139
|
search_on :name
|
120
140
|
end
|
121
141
|
|
122
|
-
p. *PartNumber* will be able to search on both _:number_ and _:category_name_. *Part*, on the other hand, will absorb all of the searchable fields of PartNumber, including its associations. So, it can be searched on _:serial_, _:number_, and _:category_name_
|
142
|
+
p. *PartNumber* will be able to search on both _:number_ and _:category_name_. *Part*, on the other hand, will absorb all of the searchable fields of PartNumber, including its associations. So, it can be searched on _:serial_, _:number_, and _:category_name_.
|
123
143
|
|
124
144
|
h3. :search_for
|
125
145
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SearchMagic
|
2
|
+
class Breadcrumb
|
3
|
+
attr_accessor :field_name, :options
|
4
|
+
|
5
|
+
def initialize(field_name, options)
|
6
|
+
self.field_name = field_name
|
7
|
+
self.options = options
|
8
|
+
self.options[:except] = Array.wrap(self.options[:except]).compact
|
9
|
+
self.options[:only] = Array.wrap(self.options[:only]).compact
|
10
|
+
end
|
11
|
+
|
12
|
+
def term
|
13
|
+
@term ||= options[:skip_prefix].presence ? nil : (options[:as] || field_name.to_s.pluralize.singularize)
|
14
|
+
end
|
15
|
+
|
16
|
+
def clone
|
17
|
+
Breadcrumb.new(field_name, options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -51,25 +51,24 @@ module SearchMagic
|
|
51
51
|
private
|
52
52
|
|
53
53
|
def create_searchables
|
54
|
-
fields =
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
54
|
+
stack, visited, fields = [StackFrame.new(self)], {}, []
|
55
|
+
until stack.empty?
|
56
|
+
current = stack.shift
|
57
|
+
unless visited.has_key?(current.type)
|
58
|
+
visited[current.type] = true
|
59
|
+
current.type.searchable_fields.each do |field_name, options|
|
60
|
+
next unless current.wants_field?(field_name)
|
61
|
+
path = current.path.clone + [Breadcrumb.new(field_name, options)]
|
62
|
+
if association = current.type.reflect_on_association(field_name)
|
63
|
+
stack << StackFrame.new(association.class_name.constantize, path)
|
64
|
+
else
|
65
|
+
fields << Metadata.new(:origin_type => current.type, :through => path, :options => options)
|
66
|
+
end
|
66
67
|
end
|
67
|
-
else
|
68
|
-
Metadata.new(:type => self, :through => lambda {|obj| obj.send(field_name) }, :field_name => field_name.to_s.pluralize.singularize.to_sym, :options => options)
|
69
68
|
end
|
70
|
-
end
|
69
|
+
end
|
71
70
|
|
72
|
-
|
71
|
+
fields.index_by(&:name)
|
73
72
|
end
|
74
73
|
end
|
75
74
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module SearchMagic
|
2
2
|
class Metadata
|
3
|
-
attr_accessor :
|
4
|
-
|
3
|
+
attr_accessor :origin_type, :through, :options
|
4
|
+
|
5
5
|
def initialize(attributes = {})
|
6
6
|
attributes.each do |key, value|
|
7
7
|
send(:"#{key}=", value)
|
@@ -9,23 +9,28 @@ module SearchMagic
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def name
|
12
|
-
@name ||=
|
13
|
-
prefix.present? ? field_name : (options[:as] || field_name)].compact.join("_").to_sym
|
12
|
+
@name ||= through.map(&:term).compact.join("_").to_sym
|
14
13
|
end
|
15
14
|
|
16
15
|
def value_for(obj, keep_punctuation)
|
17
|
-
v =
|
16
|
+
v = get_value(obj)
|
18
17
|
v = v.is_a?(Array) ? v.join(" ") : v.to_s
|
19
18
|
v = v.gsub(/[[:punct:]]/, ' ') unless keep_punctuation
|
20
19
|
v
|
21
20
|
end
|
22
21
|
|
23
22
|
def arrangeable_value_for(obj)
|
24
|
-
|
23
|
+
get_value(obj)
|
25
24
|
end
|
26
25
|
|
27
26
|
def searchable_value_for(obj)
|
28
27
|
value_for(obj, options[:keep_punctuation]).downcase.split.map {|word| [name, word].join(":")}
|
29
28
|
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def get_value(obj)
|
33
|
+
self.through.map(&:field_name).inject(obj) {|memo, method| memo.is_a?(Array) ? memo.map{|o| o.send(method)} : memo.send(method)}
|
34
|
+
end
|
30
35
|
end
|
31
36
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SearchMagic
|
2
|
+
class StackFrame
|
3
|
+
attr_accessor :type, :path
|
4
|
+
|
5
|
+
def initialize(type, path = [])
|
6
|
+
self.type = type
|
7
|
+
self.path = path
|
8
|
+
end
|
9
|
+
|
10
|
+
def wants_field?(field_name)
|
11
|
+
!field_excluded?(field_name) && field_included?(field_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def field_included?(field_name)
|
17
|
+
options[:only].blank? || options[:only].include?(field_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def field_excluded?(field_name)
|
21
|
+
options[:except].present? && options[:except].include?(field_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def options
|
25
|
+
@options ||= (path.last.try(:options) || {})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/search_magic/version.rb
CHANGED
data/lib/search_magic.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SearchMagic::FullTextSearch do
|
4
|
+
context "when given two models which search on each other" do
|
5
|
+
describe "calculating searchables from the first model" do
|
6
|
+
subject { Graph1ModelA }
|
7
|
+
it("should not raise an error") { expect { subject.searchables }.to_not raise_error }
|
8
|
+
its("searchables.keys") { should include(:foo, :graph_1_model_b_bar) }
|
9
|
+
its("searchables.keys") { should_not include(:graph_1_model_b_graph_1_model_a, :graph_1_model_b_graph_1_model_a_foo) }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "calculating searchables from the second model" do
|
13
|
+
subject { Graph1ModelB }
|
14
|
+
it("should not raise an error") { expect { subject.searchables }.to_not raise_error }
|
15
|
+
its("searchables.keys") { should include(:bar, :graph_1_model_a_foo) }
|
16
|
+
its("searchables.keys") { should_not include(:graph_1_model_a_graph_1_model_b, :graph_1_model_a_graph_1_model_b_bar) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: search_magic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.9
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Joshua Bowers
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-04-
|
13
|
+
date: 2011-04-05 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -73,8 +73,10 @@ files:
|
|
73
73
|
- README.textile
|
74
74
|
- Rakefile
|
75
75
|
- lib/search_magic.rb
|
76
|
+
- lib/search_magic/breadcrumb.rb
|
76
77
|
- lib/search_magic/full_text_search.rb
|
77
78
|
- lib/search_magic/metadata.rb
|
79
|
+
- lib/search_magic/stack_frame.rb
|
78
80
|
- lib/search_magic/version.rb
|
79
81
|
- search_magic.gemspec
|
80
82
|
- spec/models/absolutely_not_searchable.rb
|
@@ -82,6 +84,8 @@ files:
|
|
82
84
|
- spec/models/asset.rb
|
83
85
|
- spec/models/developer.rb
|
84
86
|
- spec/models/game.rb
|
87
|
+
- spec/models/graph_1_model_a.rb
|
88
|
+
- spec/models/graph_1_model_b.rb
|
85
89
|
- spec/models/is_searchable.rb
|
86
90
|
- spec/models/no_searchables.rb
|
87
91
|
- spec/models/part.rb
|
@@ -94,6 +98,7 @@ files:
|
|
94
98
|
- spec/unit/search_magic/arrangements_spec.rb
|
95
99
|
- spec/unit/search_magic/associations_spec.rb
|
96
100
|
- spec/unit/search_magic/fields_spec.rb
|
101
|
+
- spec/unit/search_magic/graph_searches_spec.rb
|
97
102
|
- spec/unit/search_magic/model_updates_spec.rb
|
98
103
|
has_rdoc: true
|
99
104
|
homepage: http://github.com/joshuabowers/search_magic
|
@@ -129,6 +134,8 @@ test_files:
|
|
129
134
|
- spec/models/asset.rb
|
130
135
|
- spec/models/developer.rb
|
131
136
|
- spec/models/game.rb
|
137
|
+
- spec/models/graph_1_model_a.rb
|
138
|
+
- spec/models/graph_1_model_b.rb
|
132
139
|
- spec/models/is_searchable.rb
|
133
140
|
- spec/models/no_searchables.rb
|
134
141
|
- spec/models/part.rb
|
@@ -141,4 +148,5 @@ test_files:
|
|
141
148
|
- spec/unit/search_magic/arrangements_spec.rb
|
142
149
|
- spec/unit/search_magic/associations_spec.rb
|
143
150
|
- spec/unit/search_magic/fields_spec.rb
|
151
|
+
- spec/unit/search_magic/graph_searches_spec.rb
|
144
152
|
- spec/unit/search_magic/model_updates_spec.rb
|