Allen Young
2007-Aug-10 03:21 UTC
[Ferret-talk] Different ferret fields for instances of the same model?
Hi all, So far as I know, while using acts_as_ferret, we should add the following declaration in the ActiveRecord model which is going to be indexed: acts_as_ferret({:fields => @@ferrect_fields}) in which @@ferrect_fields is a hash containing all the field to be indexed. This is pretty much for some simple situations. But I got a more complex situation that I want to define the fields to be indexed for every instance of the same model. My requirements are something like this: Suppose we have a model "Product", in "Product" I''ve declared a polymorphic relationship with model "Property1" and "Property2", the following code will show this: class Product < ActiveRecord::Base belongs_to :property, :polymorphic => true @@ferret_fields = {...} acts_as_ferret({:fields => @@ferret_fields}) end class Property1 < ActiveRecord::Base has_one :product, :as => :property end class Property2 < ActiveRecord::Base has_one :product, :as => :property end Now I want to provide full text search capability for "Product" and it''s obvious that "Product" should contains its "property" while being indexed. So I should define "ferret_fields" class method in "Property1" and "Property2" to collect all their fields and dynamically define the corresponding method in "Product". The code is something like this: class Property1 < ActiveRecord::Base has_one :product, :as => :property def self.ferret_fields # return a hash containing all the fields to be indexed in aaf''s format end end class Property2 < ActiveRecord::Base has_one :product, :as => :property def self.ferret_fields # return a hash containing all the fields to be indexed in aaf''s format end end class Product < ActiveRecord::Base belongs_to :property, :polymorphic => true @@ferret_fields = {...} @@ferret_fields.merge!(Property1.ferret_fields) @@ferret_fields.merge!(Property2.ferret_fields) acts_as_ferret({:fields => @@ferret_fields}) Property1.ferret_fields.keys.each do |field| define_method("#{field}") do result = property.send("#{field}") end end Property2.ferret_fields.keys.each do |field| define_method("#{field}") do result = property.send("#{field}") end end end But there are two problems in the above code: 1. If the property object in a product object is "Property1", property.send("#{field}") in "Property2"''s block will cause a method missing error, vice versa. 2. Say "Property1" has 500 fields as well as "Property2", each product will be indexed using 1000 fields while only at most 500 fields contains value. How can I solve these problems and meet my requirements? Any ideas about this? -- Posted via http://www.ruby-forum.com/.
Jens Kraemer
2007-Aug-13 10:49 UTC
[Ferret-talk] Different ferret fields for instances of the same model?
Hi Allen! comments inline On Fri, Aug 10, 2007 at 05:21:38AM +0200, Allen Young wrote:> Suppose we have a model "Product", in "Product" I''ve declared a > polymorphic relationship with model "Property1" and "Property2", the > following code will show this: > > class Product < ActiveRecord::Base > belongs_to :property, :polymorphic => true > @@ferret_fields = {...} > acts_as_ferret({:fields => @@ferret_fields}) > end >[..]> > Now I want to provide full text search capability for "Product" and it''s > obvious that "Product" should contains its "property" while being > indexed. So I should define "ferret_fields" class method in "Property1" > and "Property2" to collect all their fields and dynamically define the > corresponding method in "Product". The code is something like this: >[..]> > class Product < ActiveRecord::Base > belongs_to :property, :polymorphic => true > @@ferret_fields = {...} > @@ferret_fields.merge!(Property1.ferret_fields) > @@ferret_fields.merge!(Property2.ferret_fields) > acts_as_ferret({:fields => @@ferret_fields}) > Property1.ferret_fields.keys.each do |field| > define_method("#{field}") do > result = property.send("#{field}") > end > end > Property2.ferret_fields.keys.each do |field| > define_method("#{field}") do > result = property.send("#{field}") > end > end > end > > But there are two problems in the above code: > > 1. If the property object in a product object is "Property1", > property.send("#{field}") in "Property2"''s block will cause a method > missing error, vice versa.I''d just rescue that and return nil for the field: Property2.ferret_fields.keys.each do |field| define_method("#{field}") do result = property.send("#{field}") rescue nil end end> 2. Say "Property1" has 500 fields as well as "Property2", each product > will be indexed using 1000 fields while only at most 500 fields contains > value.Do you really need to be able to run queries against each single one of these 1000 fields? If not, you could concatenate their values into a single large :properties field. Cheers, Jens -- Jens Kr?mer http://www.jkraemer.net/ - Blog http://www.omdb.org/ - The new free film database
Allen Young
2007-Aug-14 02:51 UTC
[Ferret-talk] Different ferret fields for instances of the same model?
Jens Kraemer wrote:> I''d just rescue that and return nil for the field: > > Property2.ferret_fields.keys.each do |field| > define_method("#{field}") do > result = property.send("#{field}") rescue nil > end > end >For now, I''m rescuing the "NoMethodError" which works fine as well.>> 2. Say "Property1" has 500 fields as well as "Property2", each product >> will be indexed using 1000 fields while only at most 500 fields contains >> value. > > Do you really need to be able to run queries against each single one of > these 1000 fields? If not, you could concatenate their values into a > single large :properties field.I''m afraid so... because many of these fields are number fields and users will want to search these fields like "field1 is bigger than 1.5 and smaller than 0.7". -- Posted via http://www.ruby-forum.com/.
Jens Kraemer
2007-Aug-14 08:07 UTC
[Ferret-talk] Different ferret fields for instances of the same model?
On Tue, Aug 14, 2007 at 04:51:59AM +0200, Allen Young wrote:> Jens Kraemer wrote: > > I''d just rescue that and return nil for the field: > > > > Property2.ferret_fields.keys.each do |field| > > define_method("#{field}") do > > result = property.send("#{field}") rescue nil > > end > > end > > > For now, I''m rescuing the "NoMethodError" which works fine as well. > > >> 2. Say "Property1" has 500 fields as well as "Property2", each product > >> will be indexed using 1000 fields while only at most 500 fields contains > >> value. > > > > Do you really need to be able to run queries against each single one of > > these 1000 fields? If not, you could concatenate their values into a > > single large :properties field. > I''m afraid so... because many of these fields are number fields and > users will want to search these fields like "field1 is bigger than 1.5 > and smaller than 0.7".Ok. To avoid the 500 nil fields per record you could override the to_doc instance method in your model and only add the relevant fields depending on the type of your property. The original implementation is in acts_as_ferret''s instance_methods.rb . Jens -- Jens Kr?mer http://www.jkraemer.net/ - Blog http://www.omdb.org/ - The new free film database