search_magic 0.1.1 → 0.2.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.
- data/Gemfile.lock +44 -27
- data/README.markdown +397 -0
- data/lib/search_magic.rb +2 -0
- data/lib/search_magic/breadcrumb.rb +1 -1
- data/lib/search_magic/full_text_search.rb +11 -4
- data/lib/search_magic/metadata.rb +14 -2
- data/lib/search_magic/version.rb +1 -1
- data/search_magic.gemspec +2 -0
- data/spec/fabricators/developer_fabricator.rb +4 -0
- data/spec/fabricators/field_skip_prefix_fabricator.rb +3 -0
- data/spec/models/field_skip_prefix.rb +7 -0
- data/spec/models/game.rb +1 -1
- data/spec/models/model_with_field_types.rb +22 -0
- data/spec/unit/search_magic/arrangements_spec.rb +2 -2
- data/spec/unit/search_magic/breadcrumb_spec.rb +33 -0
- data/spec/unit/search_magic/configuration_spec.rb +28 -0
- data/spec/unit/search_magic/date_parsing_spec.rb +15 -0
- data/spec/unit/search_magic/fields_spec.rb +31 -0
- data/spec/unit/search_magic/metadata_spec.rb +23 -0
- data/spec/unit/search_magic/metadata_type_spec.rb +19 -0
- data/spec/unit/search_magic/simple_inclusion_model_spec.rb +2 -0
- data/spec/unit/search_magic/stack_frame_spec.rb +28 -0
- metadata +106 -71
- data/README.textile +0 -178
data/README.textile
DELETED
@@ -1,178 +0,0 @@
|
|
1
|
-
h1. SearchMagic
|
2
|
-
|
3
|
-
SearchMagic provides full-text search capabilities to "mongoid":http://github.com/mongoid/mongoid documents, embedded documents, and referenced documents with a clean, consistent, and easy to use syntax. Searching can be performed on either word fragments, such as *foo*, or can use a selector-syntax, *foo:bar*, to target which fields of the document the search is to be restricted to.
|
4
|
-
|
5
|
-
h2. Installation
|
6
|
-
|
7
|
-
SearchMagic is built on top of mongoid; in all likelihood, it will only work with versions greater than or equal to _2.0.0_. The project can be installed as a gem on a target system:
|
8
|
-
|
9
|
-
bc. gem install search_magic
|
10
|
-
|
11
|
-
For environments where bundler is being used, it can be installed by adding the following to your Gemfile and running @bundle@.
|
12
|
-
|
13
|
-
bc. gem 'search_magic'
|
14
|
-
|
15
|
-
h2. Getting Started
|
16
|
-
|
17
|
-
h3. Adding FullTextSearch capabilities
|
18
|
-
|
19
|
-
Adding FullTextSearch is as simple as including the appropriate module into a mongoid document and defining which fields are to be searchable. In the following example, the *SearchMagic::FullTextSearch* module is included and each field of the model is made searchable.
|
20
|
-
|
21
|
-
bc.. class Address
|
22
|
-
include Mongoid::Document
|
23
|
-
include SearchMagic
|
24
|
-
field :street
|
25
|
-
field :city
|
26
|
-
field :state
|
27
|
-
field :post_code
|
28
|
-
embedded_in :person
|
29
|
-
|
30
|
-
search_on :street
|
31
|
-
search_on :city
|
32
|
-
search_on :state
|
33
|
-
search_on :post_code
|
34
|
-
end
|
35
|
-
|
36
|
-
p. At this point, *Address* can be searched by calling its _*:search_for*_ method:
|
37
|
-
|
38
|
-
bc. Address.search_for("state:ca")
|
39
|
-
|
40
|
-
It is also possible to sort models on fields which have been marked as searchable through the _*:arrange*_ method:
|
41
|
-
|
42
|
-
bc. Address.arrange(:state, :asc)
|
43
|
-
|
44
|
-
h3. :search_on
|
45
|
-
|
46
|
-
Fields that are made searchable by :search_on have their values cached in an embedded array within each document. This array, *:searchable_values*, should contain entries of the form *field_name:value*. The selector, *field_name*, represents a filter which can be used when searching to narrow the search space; it can be manually renamed by passing the *:as* option to :search_on:
|
47
|
-
|
48
|
-
bc. search_on :post_code, :as => :zip_code
|
49
|
-
|
50
|
-
The example in the previous section showcased using :search_on on basic *Mongoid::Document* fields. It can, however, be used on fields within a document which denote an association.
|
51
|
-
|
52
|
-
bc.. class Person
|
53
|
-
include Mongoid::Document
|
54
|
-
include SearchMagic
|
55
|
-
field :name
|
56
|
-
embeds_one :address
|
57
|
-
|
58
|
-
search_on :name
|
59
|
-
search_on :address
|
60
|
-
end
|
61
|
-
|
62
|
-
p. When an association is searched on, all of its searchable fields are automatically made searchable in the first document. In the previous example, this means that the four fields of *Address*, @[:street, :city, :state, :post_code]@ are now searchable from within *Person*. As such, each association will end up adding entries into the *:searchable_values* array. The searchable fields which are introduced from an association can be restricted by use of the *:only* and *:except* options, which may either take an array or an individual field name:
|
63
|
-
|
64
|
-
bc. search_on :address, :only => [:street, :state]
|
65
|
-
search_on :address, :except => :post_code
|
66
|
-
|
67
|
-
By default, an association's fields will be prefixed by name of the association. Therefore, the previous example would add entries to *:searchable_values* with the selectors @[:address_street, :address_city, :address_state, :address_post_code]@. The *:as* option alters the prefix:
|
68
|
-
|
69
|
-
bc. search_on :address, :as => :location # results in :location_street, :location_city, ...
|
70
|
-
|
71
|
-
It is also possible to prevent the prefix from being added to each absorbed searchable field through use of the *:skip_prefix* option:
|
72
|
-
|
73
|
-
bc. search_on :address, :skip_prefix => true # results in :street, :city, ...
|
74
|
-
|
75
|
-
:skip_prefix and :as cannot be used concurrently: :skip_prefix will always take precedence.
|
76
|
-
|
77
|
-
Values added to *:searchable_values* automatically are split on whitespace and have their punctuation removed. For most cases, searches performed on models are not going to need punctuation support. However, if it is desired to keep the punctuation present on a particular field, that can easily be done through the *:keep_punctuation* option:
|
78
|
-
|
79
|
-
bc.. class Asset
|
80
|
-
include Mongoid::Document
|
81
|
-
include SearchMagic
|
82
|
-
field :tags, :type => Array
|
83
|
-
|
84
|
-
search_on :tags, :keep_punctuation => true
|
85
|
-
end
|
86
|
-
|
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
|
-
|
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
|
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
|
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.
|
110
|
-
|
111
|
-
bc.. class Part
|
112
|
-
include Mongoid::Document
|
113
|
-
include Mongoid::Timestamps
|
114
|
-
include SearchMagic
|
115
|
-
field :serial
|
116
|
-
references_in :part_number
|
117
|
-
|
118
|
-
search_on :serial
|
119
|
-
search_on :part_number, :skip_prefix => true
|
120
|
-
end
|
121
|
-
|
122
|
-
class PartNumber
|
123
|
-
include Mongoid::Document
|
124
|
-
include SearchMagic
|
125
|
-
field :value
|
126
|
-
references_many :parts
|
127
|
-
referenced_in :part_category
|
128
|
-
|
129
|
-
search_on :number
|
130
|
-
search_on :part_category, :as => :category
|
131
|
-
end
|
132
|
-
|
133
|
-
class PartCategory
|
134
|
-
include Mongoid::Document
|
135
|
-
include SearchMagic
|
136
|
-
field :name
|
137
|
-
references_many :part_numbers
|
138
|
-
|
139
|
-
search_on :name
|
140
|
-
end
|
141
|
-
|
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_.
|
143
|
-
|
144
|
-
h3. :search_for
|
145
|
-
|
146
|
-
Searching a model with SearchMagic is simple: each model gains a class method called _*:search_for*_ which accepts one parameter, the search pattern. This method is a "mongoid scope":http://mongoid.org/docs/querying/; it will always return a criteria object after executing. As such, it plays nicely with other scopes on your models.
|
147
|
-
|
148
|
-
SearchMagic expects the incoming _pattern_ to be a string containing whitespace delimited phrases. Each phrase can either be a single word, or a _selector:value_ pair. Multiple phrases will narrow the search field: each additional phrase places an additional requirement on a matching document. Single word phrases are matched across all entries in a model's _:searchable_values_ array. The pairs, on the other hand, restrict the search for _value_ against only those entries which match _selector_. In either case, _word_ or _value_ may contain fragments of whole entries stored within _:searchable_values_.
|
149
|
-
|
150
|
-
Using the models defined in the previous section, the following searches are all perfectly valid:
|
151
|
-
|
152
|
-
bc. Part.search_for("table") # full text search on "table"
|
153
|
-
Part.search_for("category_name:table") # restricts the search for "table" to "category_name"
|
154
|
-
Part.search_for("bike serial:b1234") # full text search on "bike", with an extra requirement that the serial be "b1234"
|
155
|
-
|
156
|
-
As _*:search_for*_ is a scope, it can be called on any previous scope within the call chain:
|
157
|
-
|
158
|
-
bc. Part.where(:created_at.gt => 1.day.ago.time).search_for("table")
|
159
|
-
|
160
|
-
_*:search_for*_ can be called multiple times within the same scope chain. Doing so will append each successive pattern to the previous searches. Effectively, this is the same as performing a single _*:search_for*_ with whitespace delimited terms in the pattern. To make such expressions slightly more readable, _*:search_for*_ is aliased as _*:and_for*_:
|
161
|
-
|
162
|
-
bc. Part.search_for("bike").and_for("serial:b1234")
|
163
|
-
|
164
|
-
h3. :arrange
|
165
|
-
|
166
|
-
SearchMagic also provides a utility scope for arranging the model by the searchables defined within it. This method, _*:arrange*_, has one required parameter specifying the searchable to sort on and one optional parameter specifying the sort direction. (If the second parameter is omitted, it will default to ascending.)
|
167
|
-
|
168
|
-
bc. Part.arrange(:serial)
|
169
|
-
Part.arrange(:serial, :asc) # same as last example
|
170
|
-
Part.arrange(:category_name, :desc) # arrange the parts in descending order by :category_name
|
171
|
-
|
172
|
-
As mentioned, _*:arrange*_ is a scope, so it can be chained with other scopes on a given model:
|
173
|
-
|
174
|
-
bc. Part.search_for("category_name:table").arrange(:serial, :asc)
|
175
|
-
|
176
|
-
h2. Problems? Comments?
|
177
|
-
|
178
|
-
Feel free to add an "issue on GitHub":search_magic/issues or fork the project and send a pull request. I'm always looking for new ways of bending hardware to my will, so suggestions are welcome.
|