class XapianDb::DocumentBlueprint

A document blueprint describes the mapping of an object to a Xapian document for a given class. @example A simple document blueprint configuration for the class Person

XapianDb::DocumentBlueprint.setup(:Person) do |blueprint|
  blueprint.attribute       :name, :weight => 10
  blueprint.attribute       :first_name
  blueprint.index           :remarks
end

@example A document blueprint configuration with a complex attribute for the class Person

XapianDb::DocumentBlueprint.setup(:Person) do |blueprint|
  blueprint.attribute       :complex, :weight => 10 do
    # add some logic here to evaluate the value of 'complex'
  end
end

@author Gernot Kogler

Attributes

blueprints[R]
_natural_sort_order[R]

Blueprint DSL methods


dependencies[R]

Blueprint DSL methods


lazy_base_query[R]

Blueprint DSL methods


type_map[R]

Instance methods


Public Class Methods

attributes() click to toggle source

Return an array of all defined attributes @return [Array<Symbol>] All defined attributes

    # File lib/xapian_db/document_blueprint.rb
144 def attributes
145   @attributes || []
146 end
blueprint_for(klass_or_name) click to toggle source

Get the blueprint for a class @return [DocumentBlueprint]

    # File lib/xapian_db/document_blueprint.rb
 87 def blueprint_for(klass_or_name)
 88   if @blueprints
 89     if klass_or_name.is_a?(Class)
 90       warn "xapian_db: blueprint_for(Class) is deprecated; use blueprint_for(Symbol) or blueprint_for(String) instead"
 91       key = klass_or_name.name
 92     else
 93       key = klass_or_name.to_s
 94     end
 95     while key != "Object" && key != "BasicObject"
 96       if @blueprints.has_key? key
 97         return @blueprints[key]
 98       else
 99         klass = XapianDb::Utilities.constantize key
100         key = klass.superclass.name
101       end
102     end
103   end
104   return nil
105 end
configured?(name) click to toggle source

is a blueprint configured for the given name? @return [Boolean]

   # File lib/xapian_db/document_blueprint.rb
65 def configured?(name)
66   @blueprints && @blueprints.has_key?(name.to_s)
67 end
configured_classes() click to toggle source

Get all configured classes @return [Array<Class>]

   # File lib/xapian_db/document_blueprint.rb
71 def configured_classes
72   if @blueprints
73     @blueprints.keys.map {|class_name| XapianDb::Utilities.constantize(class_name) }
74   else
75     []
76   end
77 end
dependencies_for(klass_name, changed_attrs) click to toggle source
   # File lib/xapian_db/document_blueprint.rb
79 def dependencies_for(klass_name, changed_attrs)
80   @blueprints.values.map(&:dependencies)
81                     .flatten
82                     .select{ |dependency| dependency.dependent_on == klass_name && dependency.interested_in?(changed_attrs) }
83 end
new() click to toggle source

Construct the blueprint

    # File lib/xapian_db/document_blueprint.rb
272 def initialize
273   @attributes_hash      = {}
274   @indexed_methods_hash = {}
275   @type_map             = {}
276   @dependencies         = []
277   @_natural_sort_order  = :id
278   @autoindex            = true
279   @indexer_preprocess_callback = nil
280 end
reset() click to toggle source

reset the blueprint setup

   # File lib/xapian_db/document_blueprint.rb
59 def reset
60   @blueprints = {}
61 end
searchable_prefixes() click to toggle source

Return an array of all configured text methods in any blueprint @return [Array<String>] All searchable prefixes

    # File lib/xapian_db/document_blueprint.rb
138 def searchable_prefixes
139   @searchable_prefixes || []
140 end
setup(klass_or_name) { |blueprint| ... } click to toggle source

Configure the blueprint for a class. Available options:

  • adapter (see {#adapter} for details)

  • attribute (see {#attribute} for details)

  • index (see {#index} for details)

   # File lib/xapian_db/document_blueprint.rb
36 def setup(klass_or_name, &block)
37   name = class_name_from klass_or_name
38 
39   @blueprints ||= {}
40   blueprint = DocumentBlueprint.new
41   yield blueprint if block_given? # configure the blueprint through the block
42   validate_type_consistency_on blueprint
43 
44   # Remove a previously loaded blueprint for this class to avoid stale blueprint definitions
45   @blueprints.delete_if { |indexed_class, blueprint| indexed_class == name }
46   @blueprints[name] = blueprint
47 
48   lazy_load_adapter_for blueprint, name
49 
50   @searchable_prefixes = @blueprints.values.map { |blueprint| blueprint.searchable_prefixes }.flatten.compact.uniq || []
51 
52   # We can always do a field search on the name of the indexed class
53   @searchable_prefixes << "indexed_class"
54   @attributes = @blueprints.values.map { |blueprint| blueprint.attribute_names}.flatten.compact.uniq.sort || []
55   blueprint
56 end
type_info_for(attribute) click to toggle source

Get the type info of an attribute @param [attribute] The name of an indexed method @return [Symbol] The defined type or :untyped if no type is defined

    # File lib/xapian_db/document_blueprint.rb
128 def type_info_for(attribute)
129   return nil if @blueprints.nil?
130   @blueprints.values.each do |blueprint|
131     return blueprint.type_map[attribute] if blueprint.type_map.has_key?(attribute)
132   end
133   nil
134 end
value_number_for(attribute) click to toggle source

Get the value number for an attribute. Please note that this is not the index in the values array of a xapian document but the valueno. Therefore, document.values returns the wrong data, use document.value(value_number) instead. @param [attribute] The name of an attribute @return [Integer] The value number

    # File lib/xapian_db/document_blueprint.rb
112 def value_number_for(attribute)
113   return 0 if attribute.to_sym == :indexed_class
114   return 1 if attribute.to_sym == :natural_sort_order
115   raise ArgumentError.new "attribute #{attribute} is not configured in any blueprint" if @attributes.nil?
116   position = @attributes.index attribute.to_sym
117   if position
118     # We add 2 because slot 0 and 1 are reserved for indexed_class and natural_sort_order
119     return position + 2
120   else
121     raise ArgumentError.new "attribute #{attribute} is not configured in any blueprint"
122   end
123 end

Private Class Methods

class_name_from(klass_or_name) click to toggle source
    # File lib/xapian_db/document_blueprint.rb
150 def class_name_from(klass_or_name)
151   if klass_or_name.is_a?(Class)
152     warn "xapian_db: XapianDb::DocumentBlueprint.setup(Class) is deprecated; use XapianDb::DocumentBlueprint.setup(Symbol) or XapianDb::DocumentBlueprint.setup(String) instead"
153     name = klass_or_name.name
154   else
155     name = klass_or_name.to_s
156   end
157 end
lazy_load_adapter_for(blueprint, klass_name) click to toggle source
    # File lib/xapian_db/document_blueprint.rb
159 def lazy_load_adapter_for(blueprint, klass_name)
160   # lazy load the adapter
161   unless defined? blueprint._adapter
162     adapter_file = blueprint._adapter.name.split("::").last.downcase + "_adapter"
163     require File.dirname(__FILE__) + "../adapters/#{adapter_file}"
164   end
165 
166   # Needed to add class helper methods to indexed pure ruby classes
167   if Object.const_defined?(klass_name) && Object.const_get(klass_name).is_a?(Class)
168     blueprint._adapter.add_class_helper_methods_to XapianDb::Utilities.constantize(klass_name)
169   end
170 end
validate_type_consistency_on(blueprint) click to toggle source
    # File lib/xapian_db/document_blueprint.rb
172 def validate_type_consistency_on(blueprint)
173   blueprint.type_map.each do |method_name, type|
174     if type_info_for(method_name) && type_info_for(method_name) != type
175       raise ArgumentError.new "ambigous type definition for #{method_name} detected (#{type_info_for(method_name)}, #{type})"
176     end
177   end
178 end

Public Instance Methods

_adapter() click to toggle source

return the adpater to use for this blueprint

    # File lib/xapian_db/document_blueprint.rb
293 def _adapter
294   @_adapter || XapianDb::Config.adapter || XapianDb::Adapters::GenericAdapter
295 end
accessors_module() click to toggle source

Lazily build and return a module that implements accessors for each field @return [Module] A module containing all accessor methods

    # File lib/xapian_db/document_blueprint.rb
228 def accessors_module
229   return @accessors_module unless @accessors_module.nil?
230   @accessors_module = Module.new
231 
232   # Add the accessors for the indexed class and the score
233   @accessors_module.instance_eval do
234 
235     define_method :indexed_class do
236       self.values[0].value
237     end
238 
239     define_method :score do
240       @score
241     end
242 
243     define_method :attributes do
244       blueprint = XapianDb::DocumentBlueprint.blueprint_for indexed_class
245       blueprint.attribute_names.inject({}) { |hash, attr| hash.tap { |hash| hash[attr.to_s] = self.send attr } }
246     end
247   end
248 
249   # Add an accessor for each attribute
250   attribute_names.each do |attribute|
251     index = DocumentBlueprint.value_number_for(attribute)
252     codec = XapianDb::TypeCodec.codec_for @type_map[attribute]
253     @accessors_module.instance_eval do
254       define_method attribute do
255         codec.decode self.value(index)
256       end
257     end
258   end
259 
260   # Let the adapter add its document helper methods (if any)
261   _adapter.add_doc_helper_methods_to(@accessors_module)
262   @accessors_module
263 end
adapter(type) click to toggle source

Set the adapter @param [Symbol] type The adapter type; the following adapters are available:

- :generic ({XapianDb::Adapters::GenericAdapter})
- :active_record ({XapianDb::Adapters::ActiveRecordAdapter})
- :datamapper ({XapianDb::Adapters::DatamapperAdapter})
    # File lib/xapian_db/document_blueprint.rb
287 def adapter(type)
288   # We try to guess the adapter name
289   @_adapter = XapianDb::Adapters.const_get("#{camelize(type.to_s)}Adapter")
290 end
attribute(name, options={}, &block) click to toggle source

Add an attribute to the blueprint. Attributes will be stored in the xapian documents an can be accessed from a search result. @param [String] name The name of the method that delivers the value for the attribute @param [Hash] options @option options [Integer] :weight (1) The weight for this attribute. @option options [Boolean] :index (true) Should the attribute be indexed? @option options [Symbol] :as should add type info for range queries (:date, :numeric) @example For complex attribute configurations you may pass a block:

XapianDb::DocumentBlueprint.setup(:IndexedObject) do |blueprint|
  blueprint.attribute :complex do
    if @id == 1
      "One"
    else
      "Not one"
    end
  end
end
    # File lib/xapian_db/document_blueprint.rb
326 def attribute(name, options={}, &block)
327   raise ArgumentError.new("You cannot use #{name} as an attribute name since it is a reserved method name of Xapian::Document") if reserved_method_name?(name)
328   do_not_index    = options.delete(:index) == false
329   @type_map[name] = (options.delete(:as) || :string)
330 
331   if block_given?
332     @attributes_hash[name] = {:block => block}.merge(options)
333   else
334     @attributes_hash[name] = options
335   end
336   self.index(name, options, &block) unless do_not_index
337 end
attribute_names() click to toggle source

Get the names of all configured attributes sorted alphabetically @return [Array<Symbol>] The names of the attributes

    # File lib/xapian_db/document_blueprint.rb
189 def attribute_names
190   @attributes_hash.keys.sort
191 end
attributes(*attributes) click to toggle source

Add a list of attributes to the blueprint. Attributes will be stored in the xapian documents ans can be accessed from a search result. @param [Array] attributes An array of method names that deliver the values for the attributes

    # File lib/xapian_db/document_blueprint.rb
342 def attributes(*attributes)
343   attributes.each do |attr|
344     raise ArgumentError.new("You cannot use #{attr} as an attribute name since it is a reserved method name of Xapian::Document") if reserved_method_name?(attr)
345     @attributes_hash[attr] = {}
346     @type_map[attr] = :string
347     self.index attr
348   end
349 end
autoindex(boolean) click to toggle source

Should objects for this blueprint be automatically reindexed? @param [Boolean] boolean Yes or no?

    # File lib/xapian_db/document_blueprint.rb
299 def autoindex(boolean)
300   @autoindex = boolean
301 end
autoindex?() click to toggle source

Get the autoindex value @return [Boolean] The autoindex value

    # File lib/xapian_db/document_blueprint.rb
305 def autoindex?
306   @autoindex
307 end
base_query(expression = nil, &block) click to toggle source

Define a base query to select one or all objects of the indexed class. The reason for a base query is to optimize the query avoiding th 1+n problematic. The base query should only include joins(…) and includes(…) calls. @param [expression] a base query expression @example Include the adresses

blueprint.base_query Person.includes(:addresses)
    # File lib/xapian_db/document_blueprint.rb
396 def base_query(expression = nil, &block)
397   if expression
398     warn "xapian_db: directly passing a base query in a blueprint configuration is deprecated, wrap them in a block"
399     block = lambda { expression }
400   end
401   @lazy_base_query = block
402 end
block_for_attribute(attribute) click to toggle source

Get the block associated with an attribute @param [Symbol] attribute The name of the attribute @return [Block] The block

    # File lib/xapian_db/document_blueprint.rb
196 def block_for_attribute(attribute)
197   @attributes_hash[attribute][:block]
198 end
dependency(klass_name, when_changed: [], &block) click to toggle source
    # File lib/xapian_db/document_blueprint.rb
413 def dependency(klass_name, when_changed: [], &block)
414   @dependencies << Dependency.new(klass_name.to_s, when_changed, block)
415 end
ignore_if(&block) click to toggle source

Add a block of code that evaluates if a model should not be indexed

    # File lib/xapian_db/document_blueprint.rb
386 def ignore_if &block
387   @ignore_expression = block
388 end
index(*args, &block) click to toggle source

Add an indexed value to the blueprint. Indexed values are not accessible from a search result. @param [Array] args An array of arguments; you can pass a method name, an array of method names

or a method name and an options hash.

@param [Block] &block An optional block for complex configurations Available options:

  • :weight (default: 1) The weight for this indexed value

@example Simple index declaration

blueprint.index :name

@example Index declaration with options

blueprint.index :name, :weight => 10

@example Mass index declaration

blueprint.index :name, :first_name, :profession

@example Index declaration with a block

blueprint.index :complex, :weight => 10 do
  # add some logic here to calculate the value for 'complex'
end
    # File lib/xapian_db/document_blueprint.rb
367 def index(*args, &block)
368   case args.size
369     when 1
370       @indexed_methods_hash[args.first] = IndexOptions.new(:weight => 1, :block => block)
371     when 2
372       # Is it a method name with options?
373       if args.last.is_a? Hash
374         options = args.last
375         assert_valid_keys options, :weight, :prefixed, :no_split
376         @indexed_methods_hash[args.first] = IndexOptions.new(options.merge(:block => block))
377       else
378         add_indexes_from args
379       end
380     else # multiple arguments
381       add_indexes_from args
382   end
383 end
indexed_method_names() click to toggle source

Get the names of all configured index methods sorted alphabetically @return [Array<Symbol>] The names of the index_methods

    # File lib/xapian_db/document_blueprint.rb
202 def indexed_method_names
203   @indexed_methods_hash.keys.sort
204 end
indexer_preprocess_callback(method) click to toggle source

Set the indexer preprocess callback. @param [Method] method a class method; needs to take one parameter and return a string. @example

class Util
  def self.strip_accents(terms)
    terms.gsub(/[éèêëÉÈÊË]/, "e")
  end
end

XapianDb::DocumentBlueprint.setup(:IndexedObject) do |blueprint|
  blueprint.attribute :name
  blueprint.indexer_preprocess_callback Util.method(:strip_accents)
end
    # File lib/xapian_db/document_blueprint.rb
430 def indexer_preprocess_callback(method)
431   @indexer_preprocess_callback = method
432 end
natural_sort_order(name=nil, &block) click to toggle source

Define the natural sort order. @param [String] name The name of the method that delivers the sort expression @param [Block] &block An optional block for complex configurations Pass a method name or a block, but not both

    # File lib/xapian_db/document_blueprint.rb
408 def natural_sort_order(name=nil, &block)
409   raise ArgumentError.new("natural_sort_order accepts a method name or a block, but not both") if name && block
410   @_natural_sort_order = name || block
411 end
options_for_indexed_method(method) click to toggle source

Get the options for an indexed method @param [Symbol] method The name of the method @return [IndexOptions] The options

    # File lib/xapian_db/document_blueprint.rb
209 def options_for_indexed_method(method)
210   @indexed_methods_hash[method]
211 end
preprocess_terms() click to toggle source

Reader for indexer_preprocess_callback. Returns the terms preprocessing method for this blueprint, the global method from config or nil.

    # File lib/xapian_db/document_blueprint.rb
436 def preprocess_terms
437   @indexer_preprocess_callback || XapianDb::Config.preprocess_terms
438 end
searchable_prefixes() click to toggle source

Return an array of all configured text methods in this blueprint @return [Array<String>] All searchable prefixes

    # File lib/xapian_db/document_blueprint.rb
215 def searchable_prefixes
216   @searchable_prefixes ||= indexed_method_names
217 end
should_index?(obj) click to toggle source

Should the object go into the index? Evaluates an ignore expression, if defined

    # File lib/xapian_db/document_blueprint.rb
221 def should_index? obj
222   return obj.instance_eval(&@ignore_expression) == false if @ignore_expression
223   true
224 end

Private Instance Methods

add_indexes_from(array) click to toggle source

Add index configurations from an array

    # File lib/xapian_db/document_blueprint.rb
477 def add_indexes_from(array)
478   array.each do |arg|
479     @indexed_methods_hash[arg] = IndexOptions.new(:weight => 1)
480   end
481 end
reserved_method_name?(attr_name) click to toggle source

Check if the attribute name does not collide with a method name of Xapian::Document

    # File lib/xapian_db/document_blueprint.rb
484 def reserved_method_name?(attr_name)
485   @reserved_method_names ||= Xapian::Document.instance_methods
486   @reserved_method_names.include?(attr_name.to_sym)
487 end