pose 1.2.3 → 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,60 +1,52 @@
1
1
  # Pose <a href="http://travis-ci.org/#!/kevgo/pose" target="_blank"><img src="https://secure.travis-ci.org/kevgo/pose.png" alt="Build status"></a> <a href="https://codeclimate.com/github/kevgo/pose" target="_blank"><img src="https://codeclimate.com/badge.png" /></a>
2
2
 
3
- Pose ("Polymorphic Search") allows fulltext search for ActiveRecord objects.
3
+ Pose ("Polymorphic Search") allows fulltext search for ActiveRecord objects in Ruby on Rails.
4
4
 
5
- * Searches over several ActiveRecord classes at once.
6
- * The searchable fulltext content per document can be freely customized.
7
- * Uses the main Rails database, no sparate search engines are necessary.
8
- * Does not require to add attributes to the searchable classes or their database tables.
5
+ * Searches over several classes at once.
6
+ * The searchable content of each class and document can be freely customized.
7
+ * Uses the main Rails database, no separate servers, databases, or search engines are necessary.
8
+ * Does not pollute the searchable classes or their database tables with any attributes.
9
+ * Allows to combine the fulltext search with any other custom database searches.
9
10
  * The algorithm is designed to work with any data store that allows for range queries, which covers pretty much every SQL or NoSQL database.
10
11
  * The search is very fast, doing only simple queries over fully indexed columns.
11
- * The search index provides data for autocomplete search fields.
12
12
 
13
13
 
14
14
  # Installation
15
15
 
16
- 1. Add the gem to your Gemfile.
16
+ ## Set up the gem.
17
17
 
18
- ```ruby
19
- gem 'pose'
20
- ```
18
+ Add the gem to your Gemfile and run `bundle install`
21
19
 
22
- 2. Update your gem bundle.
23
-
24
- ```bash
25
- $ bundle install
26
- ```
20
+ ```ruby
21
+ gem 'pose'
22
+ ```
27
23
 
28
- 3. Create the database tables for pose.
24
+ ## Create the database tables for pose.
29
25
 
30
- ```bash
31
- $ rails generate pose
32
- $ rake db:migrate
33
- ```
26
+ ```bash
27
+ $ rails generate pose:install
28
+ $ rake db:migrate
29
+ ```
34
30
 
35
- Pose creates two tables in your database:
31
+ Pose creates two tables in your database. These tables are automatically populated and kept up to date.
36
32
 
37
- * _pose_words_: index of all the words that occur in the searchable content.
38
- * _pose_assignments_: lists which word occurs in which document.
33
+ * _pose_words_: index of all the words that occur in the searchable content.
34
+ * _pose_assignments_: lists which word occurs in which document.
39
35
 
40
36
 
41
- # Make your ActiveRecord models searchable
37
+ ## Make your ActiveRecord models searchable
42
38
 
43
39
  ```ruby
44
40
  class MyClass < ActiveRecord::Base
45
41
 
46
- # This line tells Pose that your class should be searchable.
47
- # Once Pose knows that, it will update the search index every time an instance is saved or deleted.
48
- #
42
+ # This line makes your class searchable.
49
43
  # The given block must return the searchble content as a string.
50
- # Note that you can return whatever content you want here,
51
- # not only data from this object but also data from related objects, class names, etc.
52
44
  posify do
53
45
 
54
46
  # Only active instances should show up in search results.
55
- return unless status == :active
47
+ return nil unless status == :active
56
48
 
57
- # Return the fulltext content.
49
+ # The searchable content.
58
50
  [ self.foo,
59
51
  self.parent.bar,
60
52
  self.children.map &:name ].join ' '
@@ -62,44 +54,31 @@ class MyClass < ActiveRecord::Base
62
54
  end
63
55
  ```
64
56
 
57
+ Note that you can return whatever content you want in the `posify` block,
58
+ not only data from this object, but also data from related objects, class names, etc.
65
59
 
66
- # Maintain the search index
60
+ Now that this class is posified, any `create`, `update`, or `delete` operation on any instance of this class will update the search index automatically.
67
61
 
68
- The search index is automatically updated when Objects are saved or deleted.
69
62
 
70
- ## Indexing existing objects in the database
71
- If you had existing data in your database before adding Pose, it isn't automatically included in the search index.
72
- They will be added on the next save/update operation on them.
73
- You can also manually add existing objects to the search index.
63
+ ## Index existing records in your database
74
64
 
75
- ```bash
76
- $ rake pose:reindex_all[MyClass]
77
- ```
65
+ Data that existed in your database before adding Pose isn't automatically included in the search index.
66
+ You have to index those records manually once. Future updates will happen automatically.
78
67
 
79
- ## Optimizing the search index
80
- The search index keeps all the words that were ever used around, in order to try to reuse them in the future.
81
- If you deleted a lot of objects, you can shrink the memory consumption of the search index by removing unused words.
82
-
83
- ```bash
84
- $ rake pose:cleanup_index
85
- ```
68
+ To index all entries of `MyClass`, run `rake pose:reindex_all[MyClass]` on the command line.
86
69
 
87
- ## Removing the search index
88
- For development purposes, or if something went wrong, you can remove the search index for a class
89
- (let's call it "MyClass") completely.
70
+ At this point, you are all set up. Let's perform a search!
90
71
 
91
- ```bash
92
- rake pose:delete_index[MyClass]
93
- ```
94
72
 
73
+ # Searching
95
74
 
96
- # Perform a search
75
+ To search, simply call Pose's `search` method, and tell it the search query as well as in which classes it should search.
97
76
 
98
77
  ```ruby
99
78
  result = Pose.search 'foo', [MyClass, MyOtherClass]
100
79
  ```
101
80
 
102
- This searches for all instances of MyClass and MyOtherClass that contain the word 'foo'.
81
+ This searches for all instances of `MyClass` and `MyOtherClass` that contain the word 'foo'.
103
82
  The method returns a hash that looks like this:
104
83
 
105
84
  ```ruby
@@ -110,34 +89,92 @@ The method returns a hash that looks like this:
110
89
  ```
111
90
 
112
91
  In this example, it found two results of type _MyClass_ and no results of type _MyOtherClass_.
113
-
114
- Happy searching! :)
92
+ A Pose search returns the object instances that match the query. This behavior, as well as many others, is configurable through
93
+ search options.
115
94
 
116
95
 
117
96
  ## Search options
118
97
 
98
+ ### Configure the searched classes
99
+
100
+ Pose accepts an array of classes to search over. When searching a single class, it can be provided directly, i.e. not as an array.
101
+
102
+ ```ruby
103
+ result = Pose.search 'foo', MyClass
104
+ ```
105
+
106
+
107
+ ### Configure the result data
108
+
109
+ By default, search results are the instances of the objects matching the search query.
110
+ If you want to just get the ids of the search results, and not the full instances, use the parameter `:result_type`.
111
+
112
+ ```ruby
113
+ result = Pose.search 'foo', MyClass, result_type: :ids # Returns ids instead of object instances.
114
+ ```
115
+
116
+
117
+ ### Limit the amount of search results
118
+
119
+ By default, Pose returns all matching items. Large result sets can become very slow and resource intensive to process.
120
+ To limit the result set, use the `:limit` search parameter.
121
+
119
122
  ```ruby
120
- result = Pose.search 'foo',
121
- MyClass,
122
- limit: 3, # Limit the result count to 3.
123
- result_type: :ids # Don't load the resulting objects, return just their ids.
124
- where: [ public: true, ['user_id <> ?', @user.id] ] # Additional where clauses for when the result entries are loaded from the database.
123
+ result = Pose.search 'foo', MyClass, limit: 20 # Returns only 20 search results.
125
124
  ```
126
125
 
127
126
 
128
- # Autocomplete support
127
+ ### Combine fulltext search with structured data search
129
128
 
130
- Because the search index contains a list of all the words known to the search engine,
131
- it can provide data for autocompletion functionality through the following convenience method:
129
+ You can add your own ActiveRecord query clauses to a fulltext search operation.
130
+ For example, given a class `Note` that belongs to a `User` class and has a boolean attribute `public`,
131
+ finding all public notes from other users containing "foo" is as easy as:
132
132
 
133
133
  ```ruby
134
- # Returns an array of strings that start with 'cat'.
135
- autocomplete_words = Pose.autocomplete_words 'cat'
134
+ result = Pose.search 'foo', MyClass, where: [ public: true, ['user_id <> ?', @current_user.id] ]
135
+ ```
136
+
137
+
138
+ # Maintenance
139
+
140
+ Besides an accasional search index cleanup, Pose is relatively maintenance free.
141
+ The search index is automatically updated when objects are created, updated, or deleted.
142
+
143
+
144
+ ## Optimizing the search index
145
+
146
+ For performance reasons, the search index keeps all the words that were ever used around, in order to try to reuse them as much as possible.
147
+ After deleting or changing a large number of objects, you can shrink the memory consumption of Pose's search index by
148
+ removing no longer used search terms from it.
149
+
150
+ ```bash
151
+ $ rake pose:index:vacuum
152
+ ```
153
+
154
+
155
+ ## Recreating the search index from scratch
156
+ To index existing data in your database, or after loading additional data outside of ActiveRecord into your database,
157
+ you should recreate the search index from scratch.
158
+
159
+ ```bash
160
+ rake pose:index:recreate[MyClass]
136
161
  ```
137
162
 
163
+
164
+ # Uninstalling
165
+
166
+ To remove all traces of Pose from your database, run:
167
+
168
+ ```bash
169
+ rails generate pose:remove
170
+ ```
171
+
172
+ Also don't forget to remove the `posify` block from your models as well as the gem entry from your Gemfile.
173
+
174
+
138
175
  # Use Pose in your tests
139
176
 
140
- By default, Pose doesn't run in Rails' test environment. This is to not slow down tests due to constant updating of the search index when objects are created.
177
+ By default, Pose doesn't run in Rails' `test` environment. This is to not slow down tests due to constant updating of the search index when objects are created.
141
178
  If you want to test your models search functionality, you need to enable searching in tests:
142
179
 
143
180
  ```ruby
@@ -153,6 +190,7 @@ If you find a bug, have a question, or a better idea, please open an issue on th
153
190
  <a href="https://github.com/kevgo/pose/issues">Pose issue tracker</a>.
154
191
  Or, clone the repository, make your changes, and submit a pull request.
155
192
 
193
+
156
194
  ## Run the unit tests for the Pose Gem
157
195
 
158
196
  Pose uses Postgresql for tests, since it is the most strict database.
@@ -171,6 +209,8 @@ $ rake spec
171
209
 
172
210
  ## Road Map
173
211
 
174
- Pose's algorithm works with all sorts of storage technologies that support range queries, i.e. relational databases,
175
- Google's DataStore, and other NoSQL stores. Right now, only relational databases are supported. NoSQL support is easy,
176
- but not yet implemented.
212
+ * add `join` to search parameters
213
+ * pagination of search results
214
+ * ordering
215
+ * weighting search results
216
+ * test Pose with more types of data stores (NoSQL, Google DataStore etc)
@@ -1,15 +1,19 @@
1
1
  require 'rails/generators'
2
2
  require 'rails/generators/migration'
3
3
 
4
- class PoseGenerator < Rails::Generators::Base
5
- include Rails::Generators::Migration
6
- source_root File.expand_path('../templates', __FILE__)
7
-
8
- def create_migration_file
9
- migration_template 'migration.rb', 'db/migrate/add_pose_tables.rb'
10
- end
11
-
12
- def self.next_migration_number(path)
13
- Time.now.utc.strftime("%Y%m%d%H%M%S")
4
+ module Pose
5
+
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ def create_migration_file
11
+ migration_template 'add_migration.rb', 'db/migrate/add_pose.rb'
12
+ end
13
+
14
+ def self.next_migration_number(path)
15
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
16
+ end
14
17
  end
18
+
15
19
  end
@@ -0,0 +1,19 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Pose
5
+
6
+ class RemoveGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ def create_migration_file
11
+ migration_template 'remove_migration.rb', 'db/migrate/remove_pose.rb'
12
+ end
13
+
14
+ def self.next_migration_number(path)
15
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,25 @@
1
+ class AddPoseTables < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ drop_table 'pose_assignments'
5
+ drop_table 'pose_words'
6
+ end
7
+
8
+ def self.down
9
+ create_table "pose_assignments" do |t|
10
+ t.integer "pose_word_id", null: false
11
+ t.integer "posable_id", null: false
12
+ t.string "posable_type", limit: 40, null: false
13
+ end
14
+
15
+ add_index "pose_assignments", :pose_word_id
16
+ add_index "pose_assignments", :posable_id
17
+
18
+ create_table "pose_words" do |t|
19
+ t.string "text", limit: 80, null: false
20
+ end
21
+
22
+ add_index "pose_words", :text
23
+ end
24
+ end
25
+
@@ -3,17 +3,6 @@ module Pose
3
3
  module Helpers
4
4
  class <<self
5
5
 
6
- # Returns all words that begin with the given query string.
7
- # This can be used for autocompletion functionality.
8
- #
9
- # @param [String]
10
- # @return [Array<String>]
11
- def autocomplete_words query
12
- return [] if query.blank?
13
- PoseWord.where('text LIKE ?', "#{Pose::Helpers.root_word(query)[0]}%").map(&:text)
14
- end
15
-
16
-
17
6
  # Returns all strings that are in new_words, but not in existing_words.
18
7
  # Helper method.
19
8
  #
@@ -1,4 +1,5 @@
1
- # Static helper methods of the Pose gem.
1
+ # This is the public API of static helper methods of the Pose gem.
2
+
2
3
  module Pose
3
4
 
4
5
  # By default, doesn't run in tests.
@@ -7,9 +8,16 @@ module Pose
7
8
 
8
9
  class <<self
9
10
 
10
- ######################
11
- # PUBLIC METHODS
11
+ # Returns all words that begin with the given query string.
12
+ # This can be used for autocompletion functionality.
12
13
  #
14
+ # @param [String]
15
+ # @return [Array<String>]
16
+ def autocomplete_words query
17
+ return [] if query.blank?
18
+ PoseWord.where('text LIKE ?', "#{Pose::Helpers.root_word(query)[0]}%").map(&:text)
19
+ end
20
+
13
21
 
14
22
  # Returns whether Pose is configured to perform search.
15
23
  # This setting exists to disable search in tests.
data/lib/pose/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pose
2
- VERSION = "1.2.3"
2
+ VERSION = "1.2.4"
3
3
  end
@@ -0,0 +1,21 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Pose
5
+ module Generators
6
+
7
+ class InstallGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ source_root File.expand_path('../templates', __FILE__)
10
+
11
+ def create_migration_file
12
+ migration_template 'add_migration.rb', 'db/migrate/add_pose.rb'
13
+ end
14
+
15
+ def self.next_migration_number(path)
16
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Pose
5
+ module Generators
6
+
7
+ class RemoveGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ source_root File.expand_path('../templates', __FILE__)
10
+
11
+ def create_migration_file
12
+ migration_template 'remove_migration.rb', 'db/migrate/remove_pose.rb'
13
+ end
14
+
15
+ def self.next_migration_number(path)
16
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ class AddPoseTables < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table "pose_assignments" do |t|
5
+ t.integer "pose_word_id", null: false
6
+ t.integer "posable_id", null: false
7
+ t.string "posable_type", limit: 40, null: false
8
+ end
9
+
10
+ add_index "pose_assignments", :pose_word_id
11
+ add_index "pose_assignments", :posable_id
12
+
13
+ create_table "pose_words" do |t|
14
+ t.string "text", limit: 80, null: false
15
+ end
16
+
17
+ add_index "pose_words", :text
18
+ end
19
+
20
+ def self.down
21
+ drop_table 'pose_assignments'
22
+ drop_table 'pose_words'
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ class AddPoseTables < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ drop_table 'pose_assignments'
5
+ drop_table 'pose_words'
6
+ end
7
+
8
+ def self.down
9
+ create_table "pose_assignments" do |t|
10
+ t.integer "pose_word_id", null: false
11
+ t.integer "posable_id", null: false
12
+ t.string "posable_type", limit: 40, null: false
13
+ end
14
+
15
+ add_index "pose_assignments", :pose_word_id
16
+ add_index "pose_assignments", :posable_id
17
+
18
+ create_table "pose_words" do |t|
19
+ t.string "text", limit: 80, null: false
20
+ end
21
+
22
+ add_index "pose_words", :text
23
+ end
24
+ end
25
+
@@ -2,36 +2,39 @@ include Rake::DSL if defined?(Rake::DSL)
2
2
  require 'progressbar'
3
3
  namespace :pose do
4
4
 
5
- desc "Cleans out unused data from the search index."
6
- task :cleanup_index => :environment do |t, args|
7
- puts "Cleaning Pose search index ...\n\n"
8
- progress_bar = ProgressBar.new ' assignments', PoseAssignment.count
9
- PoseAssignment.cleanup_orphaned_pose_assignments progress_bar
10
- progress_bar.finish
5
+ namespace :index do
11
6
 
12
- progress_bar = ProgressBar.new ' words', PoseWord.count
13
- PoseWord.remove_unused_words progress_bar
14
- progress_bar.finish
7
+ desc "Cleans out unused data from the search index."
8
+ task :vacuum => :environment do |t, args|
9
+ puts "Cleaning Pose search index ...\n\n"
10
+ progress_bar = ProgressBar.new ' assignments', PoseAssignment.count
11
+ PoseAssignment.cleanup_orphaned_pose_assignments progress_bar
12
+ progress_bar.finish
15
13
 
16
- puts "\nPose search index cleanup complete.\n\n"
17
- end
14
+ progress_bar = ProgressBar.new ' words', PoseWord.count
15
+ PoseWord.remove_unused_words progress_bar
16
+ progress_bar.finish
18
17
 
19
- desc "Removes the search index for all instances of the given classes."
20
- task :delete_index, [:class_name] => :environment do |t, args|
21
- clazz = Kernel.const_get args.class_name
22
- PoseAssignment.cleanup_class_index clazz
23
- puts "Search index for class #{clazz.name} deleted.\n\n"
24
- end
18
+ puts "\nPose search index cleanup complete.\n\n"
19
+ end
20
+
21
+ desc "Removes the search index for all instances of the given classes."
22
+ task :remove, [:class_name] => :environment do |t, args|
23
+ clazz = Kernel.const_get args.class_name
24
+ PoseAssignment.cleanup_class_index clazz
25
+ puts "Search index for class #{clazz.name} deleted.\n\n"
26
+ end
25
27
 
26
- desc "Deletes and recreates the search index for all instances of the given class."
27
- task :reindex_all, [:class_name] => [:environment] do |t, args|
28
- clazz = Kernel.const_get args.class_name
29
- progress_bar = ProgressBar.new " reindexing", clazz.count
30
- clazz.find_each do |instance|
31
- instance.update_pose_words
32
- progress_bar.inc
28
+ desc "Deletes and recreates the search index for all instances of the given class."
29
+ task :reindex_all, [:class_name] => [:environment] do |t, args|
30
+ clazz = Kernel.const_get args.class_name
31
+ progress_bar = ProgressBar.new " reindexing", clazz.count
32
+ clazz.find_each do |instance|
33
+ instance.update_pose_words
34
+ progress_bar.inc
35
+ end
36
+ progress_bar.finish
33
37
  end
34
- progress_bar.finish
38
+
35
39
  end
36
-
37
40
  end
@@ -273,7 +273,7 @@ describe Pose do
273
273
  it 'returns words that start with the given phrase' do
274
274
  PosableOne.create text: 'great green pine tree'
275
275
 
276
- result = Pose::Helpers.autocomplete_words 'gr'
276
+ result = Pose.autocomplete_words 'gr'
277
277
 
278
278
  result.should have(2).words
279
279
  result.should include 'great'
@@ -282,20 +282,20 @@ describe Pose do
282
282
 
283
283
  it 'returns words that match the given phrase exactly' do
284
284
  PoseWord.create text: 'cat'
285
- result = Pose::Helpers.autocomplete_words 'cat'
285
+ result = Pose.autocomplete_words 'cat'
286
286
  result.should == ['cat']
287
287
  end
288
288
 
289
289
  it 'stems the search query' do
290
290
  PosableOne.create text: 'car'
291
- result = Pose::Helpers.autocomplete_words 'cars'
291
+ result = Pose.autocomplete_words 'cars'
292
292
  result.should have(1).words
293
293
  result[0].should == 'car'
294
294
  end
295
295
 
296
296
  it 'returns nothing if the search query is empty' do
297
297
  PosableOne.create text: 'foo bar'
298
- result = Pose::Helpers.autocomplete_words ''
298
+ result = Pose.autocomplete_words ''
299
299
  result.should be_empty
300
300
  end
301
301
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pose
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.2.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-07 00:00:00.000000000 Z
12
+ date: 2012-10-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -198,7 +198,9 @@ files:
198
198
  - doc/PoseWord.html
199
199
  - doc/top-level-namespace.html
200
200
  - lib/generators/pose_generator.rb
201
- - lib/generators/templates/migration.rb
201
+ - lib/generators/remove_pose_generator.rb
202
+ - lib/generators/templates/add_migration.rb
203
+ - lib/generators/templates/remove_migration.rb
202
204
  - lib/pose/activerecord_base_additions.rb
203
205
  - lib/pose/internal_helpers.rb
204
206
  - lib/pose/model_additions.rb
@@ -208,6 +210,10 @@ files:
208
210
  - lib/pose/static_api.rb
209
211
  - lib/pose/version.rb
210
212
  - lib/pose.rb
213
+ - lib/rails/generators/pose/pose_generator.rb
214
+ - lib/rails/generators/pose/remove_pose_generator.rb
215
+ - lib/rails/generators/templates/add_migration.rb
216
+ - lib/rails/generators/templates/remove_migration.rb
211
217
  - lib/tasks/pose_tasks.rake
212
218
  - MIT-LICENSE
213
219
  - Rakefile