lib/sup/index.rb in sup-0.2 vs lib/sup/index.rb in sup-0.3
- old
+ new
@@ -1,10 +1,16 @@
## the index structure for redwood. interacts with ferret.
-require 'thread'
require 'fileutils'
require 'ferret'
+begin
+ require 'chronic'
+ $have_chronic = true
+rescue LoadError => e
+ Redwood::log "optional 'chronic' library not found (run 'gem install chronic' to install)"
+ $have_chronic = false
+end
module Redwood
class Index
class LockError < StandardError
@@ -16,10 +22,11 @@
end
include Singleton
attr_reader :index
+ alias ferret index
def initialize dir=BASE_DIR
@dir = dir
@sources = {}
@sources_dirty = false
@@ -44,11 +51,11 @@
raise LockError, @lock.lockinfo_on_disk
end
end
def start_lock_update_thread
- @lock_update_thread = Redwood::reporting_thread do
+ @lock_update_thread = Redwood::reporting_thread("lock update") do
while true
sleep 30
@lock.touch_yourself
end
end
@@ -110,12 +117,12 @@
end
def add_source source
raise "duplicate source!" if @sources.include? source
@sources_dirty = true
- source.id ||= @sources.size
- ##TODO: why was this necessary?
+ max = @sources.max_of { |id, s| s.is_a?(DraftLoader) || s.is_a?(SentLoader) ? 0 : id }
+ source.id ||= (max || 0) + 1
##source.id += 1 while @sources.member? source.id
@sources[source.id] = source
end
def source_for uri; @sources.values.find { |s| s.is_source_for? uri }; end
@@ -169,11 +176,11 @@
:source_id => source_id,
:source_info => m.source_info,
:date => m.date.to_indexable_s,
:body => m.content,
:snippet => m.snippet,
- :label => m.labels.join(" "),
+ :label => m.labels.uniq.join(" "),
:from => m.from ? m.from.email : "",
:to => (m.to + m.cc + m.bcc).map { |x| x.email }.join(" "),
:subject => wrap_subj(Message.normalize_subj(m.subj)),
:refs => (m.refs + m.replytos).uniq.join(" "),
}
@@ -254,17 +261,19 @@
else
pending = [m.id]
end
until pending.empty? || (opts[:limit] && messages.size >= opts[:limit])
- id = pending.pop
- next if searched.member? id
- searched[id] = true
q = Ferret::Search::BooleanQuery.new true
- q.add_query Ferret::Search::TermQuery.new(:message_id, id), :should
- q.add_query Ferret::Search::TermQuery.new(:refs, id), :should
+ pending.each do |id|
+ searched[id] = true
+ q.add_query Ferret::Search::TermQuery.new(:message_id, id), :should
+ q.add_query Ferret::Search::TermQuery.new(:refs, id), :should
+ end
+ pending = []
+
q = build_query :qobj => q
num_queries += 1
killed = false
@index.search_each(q, :limit => :all) do |docid, score|
@@ -276,14 +285,15 @@
mid = @index[docid][:message_id]
unless messages.member?(mid)
#Redwood::log "got #{mid} as a child of #{id}"
messages[mid] ||= lambda { build_message docid }
refs = @index[docid][:refs].split(" ")
- pending += refs
+ pending += refs.select { |id| !searched[id] }
end
end
end
+
if killed
Redwood::log "thread for #{m.id} is killed, ignoring"
false
else
Redwood::log "ran #{num_queries} queries to build thread of #{messages.size + 1} messages for #{m.id}: #{m.subj}" if num_queries > 0
@@ -368,21 +378,49 @@
index.search(q, :limit => 1).total_hits > 0
end
protected
+ ## do any specialized parsing
+ ## returns nil and flashes error message if parsing failed
def parse_user_query_string str
- str2 = str.gsub(/(to|from):(\S+)/) do
+ result = str.gsub(/\b(to|from):(\S+)\b/) do
field, name = $1, $2
if(p = ContactManager.contact_for(name))
[field, p.email]
else
[field, name]
end.join(":")
end
- Redwood::log "translated #{str} to #{str2}" unless str2 == str
- @qparser.parse str2
+ if $have_chronic
+ chronic_failure = false
+ result = result.gsub(/\b(before|on|in|after):(\((.+?)\)\B|(\S+)\b)/) do
+ break if chronic_failure
+ field, datestr = $1, ($3 || $4)
+ realdate = Chronic.parse(datestr, :guess => false, :context => :none)
+ if realdate
+ case field
+ when "after"
+ Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.end}"
+ "date:(>= #{sprintf "%012d", realdate.end.to_i})"
+ when "before"
+ Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.begin}"
+ "date:(<= #{sprintf "%012d", realdate.begin.to_i})"
+ else
+ Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate}"
+ "date:(<= #{sprintf "%012d", realdate.end.to_i}) date:(>= #{sprintf "%012d", realdate.begin.to_i})"
+ end
+ else
+ BufferManager.flash "Don't understand date #{datestr.inspect}!"
+ chronic_failure = true
+ end
+ end
+ result = nil if chronic_failure
+ end
+
+ Redwood::log "translated #{str.inspect} to #{result}" unless result == str
+ @qparser.parse result if result
end
def build_query opts
query = Ferret::Search::BooleanQuery.new
query.add_query opts[:qobj], :must if opts[:qobj]