xapian_db 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ##0.5.4 (February 22th, 2011)
2
+
3
+ Fixes:
4
+
5
+ - relative database paths in the config file are resolved correctly when the Rails server is started as a daemon (-d)
6
+
7
+ Breaking Changes:
8
+
9
+ - the spelling suggestion of a query is now nil instead of an empty string if no suggestions were returned from xapian
10
+ - the resultset class (what you get back from a query) has been refactored for easier handling. See the README ("Process the results")
11
+ for details
12
+
1
13
  ##0.5.3 (February 15th, 2011)
2
14
 
3
15
  Fixes:
data/README.rdoc CHANGED
@@ -149,22 +149,27 @@ You can query objects of a specific class:
149
149
 
150
150
  results = Person.search "name:Foo"
151
151
 
152
- If you want to override the default of 10 docs per page, pass the :per_page argument:
152
+ If you want to paginate the result, pass the :per_page argument:
153
153
 
154
154
  results = Person.search "name:Foo", :per_page => 20
155
155
 
156
+ If you want to limit the number of results, pass the :limit argument (handy if you use the query for autocompletion):
157
+
158
+ results = Person.search "name:Foo", :limit => 10
159
+
156
160
  On class queries you can specifiy order options:
157
161
 
158
162
  results = Person.search "name:Foo", :order => :first_name
159
163
  results = Person.search "Fo*", :order => [:name, :first_name], :sort_decending => true
160
164
 
161
- Please note that the order option is not avaliable for global searches (XapianDb.search...)
165
+ Please note that the order option is not available for global searches (XapianDb.search...)
162
166
 
163
167
  === Process the results
164
168
 
165
169
  <code>XapianDb.search</code> returns a resultset object. You can access the number of hits directly:
166
170
 
167
- results.size # Very fast, does not load the resulting documents
171
+ results.hits # Very fast, does not load the resulting documents; always returns the actual hit count
172
+ # even if a limit option was set in the query
168
173
 
169
174
  If you use a persistent database, the resultset may contain a spelling correction:
170
175
 
@@ -172,14 +177,9 @@ If you use a persistent database, the resultset may contain a spelling correctio
172
177
  results = XapianDb.search("moose")
173
178
  results.spelling_suggestion # "mouse"
174
179
 
175
- To access the found documents, get a page from the resultset:
176
-
177
- page = results.paginate # Get the first page
178
- page = results.paginate :page => 2 # Get the second page
179
-
180
- Now you can access the documents:
180
+ The results behave like an array:
181
181
 
182
- doc = page.first
182
+ doc = results.first
183
183
  puts doc.indexed_class # Get the type of the indexed object as a string, e.g. "Person"
184
184
  puts doc.name # We can access the configured attributes
185
185
  person = doc.indexed_object # Access the object behind this doc (lazy loaded)
@@ -30,8 +30,9 @@ module XapianDb
30
30
  options = {:sort_decending => false}.merge options
31
31
  class_scope = "indexed_class:#{klass.name.downcase}"
32
32
 
33
- if options[:order]
34
- attr_names = [options[:order]].flatten
33
+ order = options.delete :order
34
+ if order
35
+ attr_names = [order].flatten
35
36
  blueprint = XapianDb::DocumentBlueprint.blueprint_for klass
36
37
  sort_indices = attr_names.map {|attr_name| blueprint.value_index_for(attr_name)}
37
38
  options[:sort_indices] = attr_names.map {|attr_name| blueprint.value_index_for(attr_name)}
@@ -39,7 +40,7 @@ module XapianDb
39
40
  result = XapianDb.database.search "#{class_scope} and (#{expression})", options
40
41
 
41
42
  # Remove the class scope from the spelling suggestion (if any)
42
- unless result.spelling_suggestion.empty?
43
+ if result.spelling_suggestion
43
44
  scope_length = "#{class_scope} and (".size
44
45
  result.spelling_suggestion = result.spelling_suggestion.slice scope_length..-2
45
46
  end
@@ -67,13 +67,17 @@ module XapianDb
67
67
 
68
68
  Rails.logger.info "Executing XapianDb search: #{expression}" if defined?(Rails)
69
69
 
70
- enquiry = Xapian::Enquire.new(reader)
71
- enquiry.query = query
72
- if opts[:sort_indices]
70
+ enquiry = Xapian::Enquire.new(reader)
71
+ enquiry.query = query
72
+ sort_indices = opts.delete :sort_indices
73
+ sort_decending = opts.delete :sort_decending
74
+
75
+ if sort_indices
73
76
  raise ArgumentError.new("Sorting is available for class scoped searches only") unless expression =~ /^indexed_class:/
74
77
  sorter = Xapian::MultiValueSorter.new
78
+
75
79
  options[:sort_indices].each do |index|
76
- sorter.add(index, opts[:sort_decending])
80
+ sorter.add(index, sort_decending)
77
81
  end
78
82
  enquiry.set_sort_by_key_then_relevance(sorter)
79
83
  end
@@ -41,6 +41,7 @@ module XapianDb
41
41
  XapianDb::DocumentBlueprint.searchable_prefixes.each{|prefix| parser.add_prefix(prefix.to_s.downcase, "X#{prefix.to_s.upcase}") }
42
42
  query = parser.parse_query(expression, @query_flags)
43
43
  @spelling_suggestion = parser.get_corrected_query_string
44
+ @spelling_suggestion = nil if @spelling_suggestion.empty?
44
45
  query
45
46
  end
46
47
 
@@ -21,13 +21,13 @@ module XapianDb
21
21
  if File.exist?(config_file_path)
22
22
  db_config = YAML::load_file config_file_path
23
23
  env_config = db_config[Rails.env]
24
- database_path = env_config["database"] || ":memory:"
24
+ database_path = File.expand_path(env_config["database"]) || ":memory:"
25
25
  adapter = env_config["adapter"] || :active_record
26
26
  writer = env_config["writer"] || :direct
27
27
  beanstalk_daemon = env_config["beanstalk_daemon"]
28
28
  else
29
29
  # No config file, set the defaults
30
- Rails.env == "test" ? database_path = ":memory:" : database_path = "db/xapian_db/#{Rails.env}"
30
+ Rails.env == "test" ? database_path = ":memory:" : database_path = File.expand_path("db/xapian_db/#{Rails.env}")
31
31
  adapter = :active_record
32
32
  writer = :direct
33
33
  beanstalk_daemon = nil
@@ -12,11 +12,11 @@ module XapianDb
12
12
  # @example Use the resultset and will_paginate in a view
13
13
  # <%= will_paginate resultset %>
14
14
  # @author Gernot Kogler
15
- class Resultset
15
+ class Resultset < Array
16
16
 
17
- # The number of documents found
17
+ # The number of hits
18
18
  # @return [Integer]
19
- attr_reader :size
19
+ attr_reader :hits
20
20
 
21
21
  # The number of pages
22
22
  # @return [Integer]
@@ -34,29 +34,36 @@ module XapianDb
34
34
  # @param [Xapian::Enquire] enquiry a Xapian query result (see http://xapian.org/docs/apidoc/html/classXapian_1_1Enquire.html).
35
35
  # Pass nil to get an empty result set.
36
36
  # @param [Hash] options
37
- # @option options [Integer] :per_page (10) How many docs per page?
37
+ # @option options [Integer] :db_size The current size (nr of docs) of the database
38
+ # @option options [Integer] :limit The maximum number of documents to retrieve
39
+ # @option options [Integer] :page (1) The page number to retrieve
40
+ # @option options [Integer] :per_page (10) How many docs per page? Ignored if a limit option is given
38
41
  # @option options [String] :spelling_suggestion (nil) The spelling corrected query (if a language is configured)
39
- def initialize(enquiry, options)
42
+ def initialize(enquiry, options={})
43
+
40
44
  @enquiry = enquiry
41
- if @enquiry.nil?
42
- @size = 0
43
- else
44
- # To get more accurate results, we pass the doc count to the mset method
45
- @size = enquiry.mset(0, options[:db_size]).matches_estimated
46
- end
47
- @spelling_suggestion = options[:spelling_suggestion]
48
- @per_page = options[:per_page]
49
- @total_pages = @size == 0 ? 0 : (@size / @per_page.to_f).ceil
50
- @current_page = 1
51
- end
45
+ return build_empty_resultset if @enquiry.nil?
46
+
47
+ db_size = options.delete :db_size
48
+ limit = options.delete :limit
49
+ page = options.delete :page
50
+ per_page = options.delete :per_page
51
+ @spelling_suggestion = options.delete :spelling_suggestion
52
+ raise ArgumentError.new "unsupported options for resultset: #{options}" if options.size > 0
53
+ raise ArgumentError.new "db_size option is required" unless db_size
54
+
55
+ @hits = enquiry.mset(0, db_size).matches_estimated
56
+ limit ||= @hits
57
+ per_page ||= limit
58
+ @total_pages = (limit / per_page.to_f).ceil
59
+ page = page.nil? ? 1 : page.to_i
60
+ offset = (page - 1) * per_page
61
+ count = offset + per_page < limit ? per_page : limit - offset
62
+ raise ArgumentError.new "page #{@page} does not exist" if @hits > 0 && offset >= limit
52
63
 
53
- # Paginate the result
54
- # @param [Hash] options Options for the persistent database
55
- # @option options [Integer] :page (1) The page to access
56
- def paginate(options={})
57
- @current_page = options[:page] ? options[:page].to_i : 1
58
- return [] if @current_page < 1 || @current_page > @total_pages
59
- build_page(@current_page)
64
+ result_window = @enquiry.mset(offset, count)
65
+ self.replace result_window.matches.map{|match| decorate(match.document)}
66
+ @current_page = page
60
67
  end
61
68
 
62
69
  # The previous page number
@@ -73,17 +80,12 @@ module XapianDb
73
80
 
74
81
  private
75
82
 
76
- # Build a page of Xapian documents
77
- # @return [Array<Xapian::Document>] An array of xapian documents
78
- def build_page(page)
79
- docs = []
80
- offset = (page - 1) * @per_page
81
- return [] if offset > @size
82
- result_window = @enquiry.mset(offset, @per_page)
83
- result_window.matches.each do |match|
84
- docs << decorate(match.document)
85
- end
86
- docs
83
+ # Build an empty resultset
84
+ def build_empty_resultset
85
+ @hits = 0
86
+ @total_pages = 0
87
+ @current_page = 0
88
+ self
87
89
  end
88
90
 
89
91
  # Decorate a Xapian document with field accessors for each configured attribute
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: xapian_db
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.5.3
5
+ version: 0.5.4
6
6
  platform: ruby
7
7
  authors:
8
8
  - Gernot Kogler
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-02-15 00:00:00 +01:00
13
+ date: 2011-02-22 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency