class XapianDb::Database
Base class for a Xapian database
Attributes
A readable xapian database (see xapian.org/docs/apidoc/html/classXapian_1_1Database.html)
Public Instance Methods
Delete a document identified by a unique term; this method is used by the orm adapters @param [String] term A term that uniquely identifies a document
# File lib/xapian_db/database.rb 33 def delete_doc_with_unique_term(term) 34 writer.delete_document("Q#{term}") 35 true 36 end
Delete all docs of a specific class.
If ‘klass` tracks its descendants, then docs of any subclasses will be deleted, too. (ActiveRecord does this by default; the gem ’descendants_tracker’ offers an alternative.)
@param [Class] klass A class that has a {XapianDb::DocumentBlueprint} configuration
# File lib/xapian_db/database.rb 44 def delete_docs_of_class(klass) 45 writer.delete_document("C#{klass}") 46 if klass.respond_to? :descendants 47 klass.descendants.each do |subclass| 48 writer.delete_document("C#{subclass}") 49 end 50 end 51 true 52 end
A very simple implementation of facets using Xapian collapse key. @param [Symbol, String] attribute the name of an attribute declared in one ore more blueprints @param [String] expression A valid search expression (see {#search} for examples). @return [Hash<Class, Integer>] A hash containing the classes and the hits per class
# File lib/xapian_db/database.rb 156 def facets(attribute, expression) 157 # return an empty hash if no search expression is given 158 return {} if expression.nil? || expression.strip.empty? 159 value_number = XapianDb::DocumentBlueprint.value_number_for(attribute) 160 @query_parser ||= QueryParser.new(self) 161 query = @query_parser.parse(expression) 162 enquiry = Xapian::Enquire.new(reader) 163 enquiry.query = query 164 enquiry.collapse_key = value_number 165 facets = {} 166 enquiry.mset(0, size).matches.each do |match| 167 facet_value = match.document.value(value_number) 168 # We must add 1 to the collapse_count since collapse_count means 169 # "how many other matches are there?" 170 facets[facet_value] = match.collapse_count + 1 171 end 172 facets 173 end
Find documents that are similar to one or more reference documents. It is basically the implementation of this suggestion: trac.xapian.org/wiki/FAQ/FindSimilar @param [Array<Xapian::Document> or Xapian::Document] docs One or more reference docs @param [Hash] options query options @option options [Class] :class an indexed class; if a class is passed, the result will
contain objects of this class only
@return [XapianDb::Resultset] The resultset
# File lib/xapian_db/database.rb 128 def find_similar_to(docs, options={}) 129 docs = [docs].flatten 130 reference = Xapian::RSet.new 131 docs.each { |doc| reference.add_document doc.docid } 132 pk_terms = docs.map { |doc| "Q#{doc.data}" } 133 class_terms = docs.map { |doc| "C#{doc.indexed_class}" } 134 135 relevant_terms = Xapian::Enquire.new(reader).eset(40, reference).terms.map {|e| e.name } - pk_terms - class_terms 136 relevant_terms.reject! { |term| term =~ /INDEXED_CLASS/ } 137 138 reference_query = Xapian::Query.new Xapian::Query::OP_OR, pk_terms 139 terms_query = Xapian::Query.new Xapian::Query::OP_OR, relevant_terms 140 final_query = Xapian::Query.new Xapian::Query::OP_AND_NOT, terms_query, reference_query 141 if options[:class] 142 class_scope = "indexed_class:#{options[:class].name.downcase}" 143 @query_parser ||= QueryParser.new(self) 144 class_query = @query_parser.parse(class_scope) 145 final_query = Xapian::Query.new Xapian::Query::OP_AND, class_query, final_query 146 end 147 enquiry = Xapian::Enquire.new(reader) 148 enquiry.query = final_query 149 Resultset.new(enquiry, :db_size => self.size, :limit => options[:limit]) 150 end
Perform a search @param [String] expression A valid search expression. @param [Hash] options @option options [Integer] :per_page How many docs per page? @option options [Array<Integer>] :sort_indices (nil) An array of attribute indices to sort by. This
option is used internally by the search method implemented on configured classes. Do not use it directly unless you know what you do
@option options [Boolean] :sort_decending (false) Reverse the sort order? @example Simple Query
resultset = db.search("foo")
@example Wildcard Query
resultset = db.search("fo*")
@example Boolean Query
resultset = db.search("foo or baz")
@example Field Query
resultset = db.search("name:foo")
@return [XapianDb::Resultset] The resultset
# File lib/xapian_db/database.rb 71 def search(expression, options={}) 72 opts = {:sort_decending => false}.merge(options) 73 @query_parser ||= QueryParser.new(self) 74 query = @query_parser.parse(expression) 75 76 # If we do not have a valid query we return an empty result set 77 return Resultset.new(nil, opts) unless query 78 79 start = Time.now 80 81 enquiry = Xapian::Enquire.new(reader) 82 enquiry.query = query 83 sort_indices = opts.delete :sort_indices 84 sort_decending = opts.delete :sort_decending 85 order = opts.delete :order 86 raise ArgumentError.new "you can't use sort_indices and order, only one of them" if sort_indices && order 87 88 if order 89 sort_indices = order.map{ |attr_name| XapianDb::DocumentBlueprint.value_number_for attr_name.to_sym } 90 end 91 92 sorter = Xapian::MultiValueKeyMaker.new 93 if sort_indices 94 sort_indices.each { |index| sorter.add_value index } 95 enquiry.set_sort_by_key_then_relevance(sorter, sort_decending) 96 else 97 sorter.add_value DocumentBlueprint.value_number_for(:natural_sort_order) 98 enquiry.set_sort_by_relevance_then_key sorter, false 99 end 100 101 opts[:spelling_suggestion] = @query_parser.spelling_suggestion 102 opts[:db_size] = self.size 103 104 retries = 0 105 begin 106 result = Resultset.new(enquiry, opts) 107 rescue IOError => ex 108 raise unless ex.message =~ /DatabaseModifiedError: / 109 raise if retries >= 5 110 sleep 0.1 111 retries += 1 112 Rails.logger.warn "XapianDb: DatabaseModifiedError, retry #{retries}" if defined?(Rails) 113 @reader.reopen 114 retry 115 end 116 117 Rails.logger.debug "XapianDb search (#{(Time.now - start) * 1000}ms) #{expression}" if defined?(Rails) 118 result 119 end
Size of the database (number of docs) @return [Integer] The number of docs in the database
# File lib/xapian_db/database.rb 17 def size 18 reader.doccount 19 end
Store a Xapian document @param [Xapian::Document] doc A Xapian document (see xapian.org/docs/sourcedoc/html/classXapian_1_1Document.html).
While you can pass any valid xapian document, you might want to use the {XapianDb::Indexer} to build a xapian doc
# File lib/xapian_db/database.rb 24 def store_doc(doc) 25 # We always replace; Xapian adds the document automatically if 26 # it is not found 27 writer.replace_document("Q#{doc.data}", doc) 28 end