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/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.