xapian_db 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
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