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
Blueprint DSL methods
Blueprint DSL methods
Blueprint DSL methods
Instance methods
Public Class Methods
Source
# File lib/xapian_db/document_blueprint.rb 144 def attributes 145 @attributes || [] 146 end
Return an array of all defined attributes @return [Array<Symbol>] All defined attributes
Source
# 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
Get the blueprint for a class @return [DocumentBlueprint]
Source
# File lib/xapian_db/document_blueprint.rb 65 def configured?(name) 66 @blueprints && @blueprints.has_key?(name.to_s) 67 end
is a blueprint configured for the given name? @return [Boolean]
Source
# 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
Get all configured classes @return [Array<Class>]
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
Source
# 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
Construct the blueprint
Source
# File lib/xapian_db/document_blueprint.rb 59 def reset 60 @blueprints = {} 61 end
reset the blueprint setup
Source
# File lib/xapian_db/document_blueprint.rb 138 def searchable_prefixes 139 @searchable_prefixes || [] 140 end
Return an array of all configured text methods in any blueprint @return [Array<String>] All searchable prefixes
Source
# 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
Configure the blueprint for a class. Available options:
-
adapter (see {#adapter} for details)
-
attribute (see {#attribute} for details)
-
index (see {#index} for details)
Source
# 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
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
Source
# 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
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
Private Class Methods
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
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
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
Source
# File lib/xapian_db/document_blueprint.rb 293 def _adapter 294 @_adapter || XapianDb::Config.adapter || XapianDb::Adapters::GenericAdapter 295 end
return the adpater to use for this blueprint
Source
# 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
Lazily build and return a module that implements accessors for each field @return [Module] A module containing all accessor methods
Source
# 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
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})
Source
# 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
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
Source
# File lib/xapian_db/document_blueprint.rb 189 def attribute_names 190 @attributes_hash.keys.sort 191 end
Get the names of all configured attributes sorted alphabetically @return [Array<Symbol>] The names of the attributes
Source
# 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
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
Source
# File lib/xapian_db/document_blueprint.rb 299 def autoindex(boolean) 300 @autoindex = boolean 301 end
Should objects for this blueprint be automatically reindexed? @param [Boolean] boolean Yes or no?
Source
# File lib/xapian_db/document_blueprint.rb 305 def autoindex? 306 @autoindex 307 end
Get the autoindex value @return [Boolean] The autoindex value
Source
# 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
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)
Source
# File lib/xapian_db/document_blueprint.rb 196 def block_for_attribute(attribute) 197 @attributes_hash[attribute][:block] 198 end
Get the block associated with an attribute @param [Symbol] attribute The name of the attribute @return [Block] The block
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
Source
# File lib/xapian_db/document_blueprint.rb 386 def ignore_if &block 387 @ignore_expression = block 388 end
Add a block of code that evaluates if a model should not be indexed
Source
# 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
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
Source
# File lib/xapian_db/document_blueprint.rb 202 def indexed_method_names 203 @indexed_methods_hash.keys.sort 204 end
Get the names of all configured index methods sorted alphabetically @return [Array<Symbol>] The names of the index_methods
Source
# File lib/xapian_db/document_blueprint.rb 430 def indexer_preprocess_callback(method) 431 @indexer_preprocess_callback = method 432 end
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
Source
# 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
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
Source
# File lib/xapian_db/document_blueprint.rb 209 def options_for_indexed_method(method) 210 @indexed_methods_hash[method] 211 end
Get the options for an indexed method @param [Symbol] method The name of the method @return [IndexOptions] The options
Source
# File lib/xapian_db/document_blueprint.rb 436 def preprocess_terms 437 @indexer_preprocess_callback || XapianDb::Config.preprocess_terms 438 end
Reader for indexer_preprocess_callback. Returns the terms preprocessing method for this blueprint, the global method from config or nil.
Source
# File lib/xapian_db/document_blueprint.rb 215 def searchable_prefixes 216 @searchable_prefixes ||= indexed_method_names 217 end
Return an array of all configured text methods in this blueprint @return [Array<String>] All searchable prefixes
Source
# 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
Should the object go into the index? Evaluates an ignore expression, if defined
Private Instance Methods
Source
# 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
Add index configurations from an array
Source
# 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
Check if the attribute name does not collide with a method name of Xapian::Document