ar_indexer 0.2.2 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aa39816650293a0f047abdb37904896c014c55bd
4
- data.tar.gz: ef02ddc7309dff62fe338855594d10974aa1d51d
3
+ metadata.gz: e7c29be1f921a9f40f47975390cd2658ef90eb91
4
+ data.tar.gz: 727458d961183a7e364d625085fa29dc6a7b97ee
5
5
  SHA512:
6
- metadata.gz: ee6a361b3a92fefcf3a8e633abf534ab97aac4051e0aedd1760bb91ef9ad08cd20e69f9213701abe185b1ce379466d8a756f438a62782912980e38ce4951db74
7
- data.tar.gz: 43eff60e824f598b2ce6acbf71a48ba626edc14c35dd6b3aa8e1ddc37024dc112cee5937a64cc4fbcea7b57e78257aba02bb428c7f5efdb1390b0e8dd28b8e35
6
+ metadata.gz: bce0e4fccb1de8992adab38ee2b81957cf76004f9c7d3c396744c4eb81e385149aef13ac7463453aaaea8b5b15ed039f26561f70f2cdf4582009c6be8729a2af
7
+ data.tar.gz: 8f2ca453649095cdca6d52dcf862e2c54dae0b3cc8cd4ebee86f985b3309d69cbdfc8ea85dd439ec617044d49317ef91379715e91d30464839888976b2b563d8
data/README.md CHANGED
@@ -10,17 +10,17 @@ Add ARIndexer to your Gemfile
10
10
 
11
11
  gem 'ar_indexer'
12
12
 
13
- Write a migration to add a reverse_indices table to the database (Rails migration generator coming soon)
13
+ Write a migration to add a reverse_indices table to the database (Rails migration generator coming soon). Due to an exception encountered in Rails 4.2 (DangerousAttributes exception), the model\_name column has been renamed to model\_constant.
14
14
 
15
15
  class CreateReverseIndices < ActiveRecord::Migration
16
16
  def change
17
17
  create_table :reverse_indices do |t|
18
- t.string :model_name
18
+ t.string :model_constant
19
19
  t.string :field_name
20
20
  t.string :word
21
21
  t.text :id_list
22
22
  end
23
- add_index :reverse_indices, [:model_name, :field_name, :word], :unique => true
23
+ add_index :reverse_indices, [:model_constant, :field_name, :word], :unique => true
24
24
  end
25
25
  end
26
26
 
@@ -30,30 +30,56 @@ Run `rake db:migrate`
30
30
 
31
31
  ###Indexing###
32
32
 
33
- Have an ActiveRecord model? Want to index some text for searching? Just add the `has_reverse_index` function to your model. Call the function with no parameters and ARIndexer will index all string and text fields. You can pass an optional array of field names (as symbols), and ARIndexer will index only these fields. Also, you can pass a hash with association names as keys and a lambda to access the necessary data. Note that the lambda must accept the AR object as its sole argument and must return a string.
33
+ Have an ActiveRecord model? Want to index some text for searching? Just add the `has_reverse_index` function to your model. Call the function with no parameters and ARIndexer will index all string and text fields. You can pass an optional hash of configuration values to customize which fields and associations are indexed, and how often each type of "field" are indexed. The default hash is below, and will be merged with the hash you pass in.
34
+
35
+ ari_configuration = {
36
+ fields: [],
37
+ associations: {},
38
+ index_on_create: [],
39
+ index_on_update: []
40
+ }
41
+
42
+ To expand on the above configuration:
43
+ * fields: If empty, will index all String and Text fields of the AR model. Pass an array of `Symbol` field names to only index the whitelisted fields
44
+ * associations: If empty, will not index any associations. For each association to be indexed, add a `Symbol` key as the name of the association, pointing to a `lambda` which takes the object being indexed, and returns a string value.
45
+ * index_on_create: If empty, will index both fields and associations as an after_create function. Add `:fields` and/or `:associations` to the array to control which are automatically indexed.
46
+ * index_on_update: If empty, will index both fields and associations as an after_update function. Add `:fields` and/or `:associations` to the array to control which are automatically indexed.
47
+
48
+ Below is an example configuration hash passed for an example `Article` model, which has a collection of `Tag` objects. In this example, we've chosen to only automatically index the fields, sometimes necessary when an AR object needs to have `reload` called on it to make sure associations are up to date. Include as many or as few options as you need.
49
+
50
+ {
51
+ fields: [:title, :subtitle, :content],
52
+ associations: {
53
+ tags: lambda {|object| object.tags.collect{|tag| tag.name}.join(', ')}
54
+ },
55
+ index_on_create: [:fields],
56
+ index_on_update: [:fields]
57
+ }
58
+
59
+ Now let's see some examples in the models:
34
60
 
35
61
  class Post < ActiveRecord::Base
36
62
  has_reverse_index
37
63
  end
38
64
 
39
65
  class Article < ActiveRecord::Base
40
- has_reverse_index([:title, :content])
66
+ has_reverse_index({
67
+ fields: [:title, :content]
68
+ })
41
69
  end
42
70
 
43
71
  class Article < ActiveRecord::Base
44
72
  has_many :article_tags
45
73
  has_many :tags, :through => :article_tags
46
- has_reverse_index(
47
- [
48
- :title,
49
- :content
50
- ],
51
- {
52
- :tags => lambda {|object| object.tags.collect{|tag| tag.name}.join(', ')}
74
+ has_reverse_index({
75
+ fields: [:title, :content],
76
+ associations: {
77
+ tags: lambda {|object| object.tags.collect{|tag| tag.name}.join(', ')}
53
78
  }
54
- )
79
+ })
80
+ end
55
81
 
56
- At this point, ARIndexer will build and maintain a reverse index for each record under these models. If you need to reindex the object at any time, the instance methods `reindex_object`, `reindex_fields`, and `reindex_associations` are added to all ActiveRecord objects with `has_reverse_index` declared.
82
+ At this point, ARIndexer will build and maintain a reverse index for each record under these models. If you need to reindex the object at any time, the instance methods `index_object`, `index_fields`, and `index_associations` are added to all ActiveRecord objects with `has_reverse_index` declared.
57
83
 
58
84
  ###Searching###
59
85
 
data/ar_indexer.gemspec CHANGED
@@ -1,8 +1,8 @@
1
1
  Gem::Specification.new do |spec|
2
2
  # Basic Gem Description
3
3
  spec.name = "ar_indexer"
4
- spec.version = "0.2.2"
5
- spec.date = "2014-10-08"
4
+ spec.version = "0.3.1"
5
+ spec.date = "2015-02-05"
6
6
  spec.summary = "Allows for reverse indexing selected ActiveRecord models. Handles searching and return of objects"
7
7
  spec.description = spec.summary
8
8
  spec.authors = ["Josh MacLachlan"]
@@ -19,5 +19,5 @@ Gem::Specification.new do |spec|
19
19
  spec.add_dependency('fast-stemmer')
20
20
 
21
21
  # Post-Install Message
22
- spec.post_install_message = "If you're upgrading from v0.1.4 to v0.2.0, read the updated documentation. Your application will throw exceptions when trying to access indexed models."
22
+ spec.post_install_message = "If upgrading from v0.1.x to 0.2.x or 0.2.x to 0.3.x, be sure to read the updated documentation. Major changes were made to initialization."
23
23
  end
@@ -5,7 +5,12 @@ module ARIndexer
5
5
  end
6
6
 
7
7
  module ClassMethods
8
- def has_reverse_index(fields = [], associations = {})
8
+ def has_reverse_index(indexing_opts = {})
9
+ if indexing_opts.nil?
10
+ indexing_opts = {}
11
+ end
12
+
13
+ fields = indexing_opts[:fields] || []
9
14
  fields.each do |field_name|
10
15
  unless self.columns_hash.keys.include?(field_name.to_s)
11
16
  unless ['string', 'text'].include?(self.columns_hash[field_name.to_s].type.to_s)
@@ -14,6 +19,7 @@ module ARIndexer
14
19
  end
15
20
  end
16
21
 
22
+ associations = indexing_opts[:associations] || {}
17
23
  associations.each do |association_name, access_function|
18
24
  unless access_function.class == Proc
19
25
  raise TypeError, 'Model associations must have a Proc provided in order to reach the appropriate value.'
@@ -22,10 +28,13 @@ module ARIndexer
22
28
 
23
29
  send :include, InstanceMethods
24
30
 
25
- class_attribute :indexed_fields
26
- class_attribute :indexed_associations
27
- self.indexed_fields = fields.clone || []
28
- self.indexed_associations = associations.clone || {}
31
+ class_attribute :ari_configuration
32
+ self.ari_configuration = {
33
+ fields: [],
34
+ associations: {},
35
+ index_on_create: [],
36
+ index_on_update: []
37
+ }.merge(indexing_opts)
29
38
 
30
39
  after_create :ar_indexer_on_create
31
40
  after_update :ar_indexer_on_update
@@ -34,26 +43,26 @@ module ARIndexer
34
43
  module_function :has_reverse_index
35
44
 
36
45
  module InstanceMethods
37
- def reindex_object
46
+ def index_object(cleanup = false)
38
47
  values_to_index = ar_indexer_get_indexable_values
39
48
  values_to_index.each do |field_name, value|
40
- Indexer.index_string(self.class.to_s.split('::').last, self.id, field_name, value, false)
49
+ Indexer.index_string(self.class.to_s.split('::').last, self.id, field_name, value, cleanup)
41
50
  end
42
51
  end
43
52
 
44
- def reindex_fields
53
+ def index_fields(cleanup = false)
45
54
  values_to_index = ar_indexer_get_indexable_values
46
- values_to_index.delete_if {|key, value| self.indexed_associations.keys.map{|field| field.to_s}.include?(key)}
55
+ values_to_index.delete_if {|key, value| self.ari_configuration[:associations].keys.map{|field| field.to_s}.include?(key)}
47
56
  values_to_index.each do |field_name, value|
48
- Indexer.index_string(self.class.to_s.split('::').last, self.id, field_name, value, false)
57
+ Indexer.index_string(self.class.to_s.split('::').last, self.id, field_name, value, cleanup)
49
58
  end
50
59
  end
51
60
 
52
- def reindex_associations
61
+ def index_associations(cleanup = false)
53
62
  values_to_index = ar_indexer_get_indexable_values
54
- values_to_index.delete_if {|key, value| self.indexed_fields.map{|field| field.to_s}.include?(key)}
63
+ values_to_index.delete_if {|key, value| self.ari_configuration[:fields].map{|field| field.to_s}.include?(key)}
55
64
  values_to_index.each do |field_name, value|
56
- Indexer.index_string(self.class.to_s.split('::').last, self.id, field_name, value, false)
65
+ Indexer.index_string(self.class.to_s.split('::').last, self.id, field_name, value, cleanup)
57
66
  end
58
67
  end
59
68
 
@@ -62,7 +71,7 @@ module ARIndexer
62
71
  def ar_indexer_get_indexable_values
63
72
  values_to_index = {}
64
73
 
65
- if self.indexed_fields.empty?
74
+ if self.class.ari_configuration[:fields].empty?
66
75
  self.class.columns.each do |column|
67
76
  if ['string', 'text'].include? column.type.to_s
68
77
  value = self.read_attribute(column.name)
@@ -74,7 +83,7 @@ module ARIndexer
74
83
  end
75
84
  end
76
85
  else
77
- self.indexed_fields.each do |field_name|
86
+ self.class.ari_configuration[:fields].each do |field_name|
78
87
  value = self[field_name]
79
88
  if value.class == String
80
89
  unless value.empty?
@@ -84,8 +93,8 @@ module ARIndexer
84
93
  end
85
94
  end
86
95
 
87
- unless self.indexed_associations.empty?
88
- self.indexed_associations.each do |association_name, access_function|
96
+ unless self.class.ari_configuration[:associations].empty?
97
+ self.class.ari_configuration[:associations].each do |association_name, access_function|
89
98
  value = access_function.call(self)
90
99
  if value.class == String
91
100
  unless value.empty?
@@ -99,14 +108,30 @@ module ARIndexer
99
108
  end
100
109
 
101
110
  def ar_indexer_on_create
102
- ar_indexer_get_indexable_values.each do |field_name, value|
103
- Indexer.index_string(self.class.to_s.split('::').last, self.id, field_name, value, false)
111
+ to_index = self.class.ari_configuration[:index_on_create]
112
+ if to_index == []
113
+ self.index_object(false)
114
+ else
115
+ if to_index.include? :fields
116
+ self.index_fields(false)
117
+ end
118
+ if to_index.include? :associations
119
+ self.index_associations(false)
120
+ end
104
121
  end
105
122
  end
106
123
 
107
124
  def ar_indexer_on_update
108
- ar_indexer_get_indexable_values.each do |field_name, value|
109
- Indexer.index_string(self.class.to_s.split('::').last, self.id, field_name, value, true)
125
+ to_index = self.class.ari_configuration[:index_on_update]
126
+ if to_index == []
127
+ self.index_object(true)
128
+ else
129
+ if to_index.include? :fields
130
+ self.index_fields(true)
131
+ end
132
+ if to_index.include? :associations
133
+ self.index_associations(true)
134
+ end
110
135
  end
111
136
  end
112
137
 
@@ -40,9 +40,9 @@ module ARIndexer
40
40
 
41
41
  # Execute AR query based on @options[:fields]
42
42
  if @options[:fields].empty?
43
- base_results = ReverseIndex.where(:model_name => self.search_models, :word => search_terms)
43
+ base_results = ReverseIndex.where(:model_constant => self.search_models, :word => search_terms)
44
44
  else
45
- base_results = ReverseIndex.where(:model_name => self.search_models, :field_name => @options[:fields], :word => search_terms)
45
+ base_results = ReverseIndex.where(:model_constant => self.search_models, :field_name => @options[:fields], :word => search_terms)
46
46
  end
47
47
 
48
48
  unless base_results.empty?
@@ -80,7 +80,7 @@ module ARIndexer
80
80
  relevancy_counts = {}
81
81
  unsorted_results = []
82
82
  search_models.each do |model|
83
- model_results = base_results.where(:model_name => model)
83
+ model_results = base_results.where(:model_constant => model)
84
84
  unless model_results.empty?
85
85
  relevancy_counts[model] = {}
86
86
  model_results.each do |result|
@@ -19,24 +19,24 @@ module ARIndexer
19
19
  return forward_index
20
20
  end
21
21
 
22
- def self.index_string(model_name, object_id, field_name, value, repair_on_completion)
22
+ def self.index_string(model_constant, object_id, field_name, value, repair_on_completion)
23
23
  forward_index = self.break_string(value)
24
24
  forward_index.each do |word|
25
- if index_record = ReverseIndex.where(:model_name => model_name, :field_name => field_name, :word => word).first
25
+ if index_record = ReverseIndex.where(:model_constant => model_constant, :field_name => field_name, :word => word).first
26
26
  current_id_array = index_record.retrieve_id_array
27
27
  unless current_id_array.include? object_id
28
28
  new_id_list = (current_id_array << object_id).sort.join(',')
29
29
  index_record.update(:id_list => new_id_list)
30
30
  end
31
31
  else
32
- ReverseIndex.create(:model_name => model_name, :field_name => field_name, :word => word, :id_list => object_id)
32
+ ReverseIndex.create(:model_constant => model_constant, :field_name => field_name, :word => word, :id_list => object_id)
33
33
  end
34
34
  end
35
- repair_index(model_name, object_id, field_name, forward_index) if repair_on_completion
35
+ repair_index(model_constant, object_id, field_name, forward_index) if repair_on_completion
36
36
  end
37
37
 
38
- def self.remove_index_id(model_name, object_id)
39
- index_records = ReverseIndex.where(:model_name => model_name)
38
+ def self.remove_index_id(model_constant, object_id)
39
+ index_records = ReverseIndex.where(:model_constant => model_constant)
40
40
  if index_records.count > 0
41
41
  index_records.each do |record|
42
42
  if record.id_list.match(/#{object_id},{0,1}/)
@@ -54,8 +54,8 @@ module ARIndexer
54
54
  end
55
55
  end
56
56
 
57
- def self.repair_index(model_name, object_id, field_name, forward_index)
58
- index_records = ReverseIndex.where(:model_name => model_name, :field_name => field_name)
57
+ def self.repair_index(model_constant, object_id, field_name, forward_index)
58
+ index_records = ReverseIndex.where(:model_constant => model_constant, :field_name => field_name)
59
59
  if index_records.count > 0
60
60
  index_records.each do |record|
61
61
  if record.id_list.match(/#{object_id},{0,1}/)
@@ -1,10 +1,10 @@
1
1
  module ARIndexer
2
2
  class ReverseIndex < ::ActiveRecord::Base
3
3
  if ::ActiveRecord::VERSION::MAJOR < 4
4
- attr_accessible :model_name, :field_name, :word, :id_list
4
+ attr_accessible :model_constant, :field_name, :word, :id_list
5
5
  end
6
6
 
7
- validates_uniqueness_of :word, :scope => [:model_name, :field_name]
7
+ validates_uniqueness_of :word, :scope => [:model_constant, :field_name]
8
8
 
9
9
  def retrieve_id_array
10
10
  id_array = self.id_list.split(',')
data/lib/ar_indexer.rb CHANGED
@@ -16,8 +16,8 @@ module ARIndexer
16
16
  # Gem version storage
17
17
  module Version
18
18
  MAJOR = '0'
19
- MINOR = '2'
20
- BUILD = '2'
19
+ MINOR = '3'
20
+ BUILD = '1'
21
21
 
22
22
  STRING = "#{MAJOR}.#{MINOR}.#{BUILD}"
23
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar_indexer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh MacLachlan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-08 00:00:00.000000000 Z
11
+ date: 2015-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -87,9 +87,8 @@ homepage: https://github.com/jtmaclachlan/ar_indexer
87
87
  licenses:
88
88
  - GPL-2
89
89
  metadata: {}
90
- post_install_message: If you're upgrading from v0.1.4 to v0.2.0, read the updated
91
- documentation. Your application will throw exceptions when trying to access indexed
92
- models.
90
+ post_install_message: If upgrading from v0.1.x to 0.2.x or 0.2.x to 0.3.x, be sure
91
+ to read the updated documentation. Major changes were made to initialization.
93
92
  rdoc_options: []
94
93
  require_paths:
95
94
  - lib
@@ -105,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
104
  version: '0'
106
105
  requirements: []
107
106
  rubyforge_project:
108
- rubygems_version: 2.2.2
107
+ rubygems_version: 2.4.5
109
108
  signing_key:
110
109
  specification_version: 4
111
110
  summary: Allows for reverse indexing selected ActiveRecord models. Handles searching