lib/sup/index.rb in sup-0.14.1.1 vs lib/sup/index.rb in sup-0.15.0
- old
+ new
@@ -166,10 +166,36 @@
xapian_query = build_xapian_query query
matchset = run_query xapian_query, 0, 0, 100
matchset.matches_estimated
end
+ ## check if a message is part of a killed thread
+ ## (warning: duplicates code below)
+ ## NOTE: We can be more efficient if we assume every
+ ## killed message that hasn't been initially added
+ ## to the indexi s this way
+ def message_joining_killed? m
+ return false unless doc = find_doc(m.id)
+ queue = doc.value(THREAD_VALUENO).split(',')
+ seen_threads = Set.new
+ seen_messages = Set.new [m.id]
+ while not queue.empty?
+ thread_id = queue.pop
+ next if seen_threads.member? thread_id
+ return true if thread_killed?(thread_id)
+ seen_threads << thread_id
+ docs = term_docids(mkterm(:thread, thread_id)).map { |x| @xapian.document x }
+ docs.each do |doc|
+ msgid = doc.value MSGID_VALUENO
+ next if seen_messages.member? msgid
+ seen_messages << msgid
+ queue.concat doc.value(THREAD_VALUENO).split(',')
+ end
+ end
+ false
+ end
+
## yield all messages in the thread containing 'm' by repeatedly
## querying the index. yields pairs of message ids and
## message-building lambdas, so that building an unwanted message
## can be skipped in the block if desired.
##
@@ -246,26 +272,30 @@
contacts.to_a.compact[0...num].map { |n,e| Person.from_name_and_email n, e }
end
## Yield each message-id matching query
EACH_ID_PAGE = 100
- def each_id query={}
+ def each_id query={}, ignore_neg_terms = true
offset = 0
page = EACH_ID_PAGE
- xapian_query = build_xapian_query query
+ xapian_query = build_xapian_query query, ignore_neg_terms
while true
ids = run_query_ids xapian_query, offset, (offset+page)
ids.each { |id| yield id }
break if ids.size < page
offset += page
end
end
## Yield each message matching query
- def each_message query={}, &b
- each_id query do |id|
+ ## The ignore_neg_terms parameter is used to display result even if
+ ## it contains "forbidden" labels such as :deleted, it is used in
+ ## Poll#poll_from when we need to get the location of a message that
+ ## may contain these labels
+ def each_message query={}, ignore_neg_terms = true, &b
+ each_id query, ignore_neg_terms do |id|
yield build_message(id)
end
end
# Search messages. Returns an Enumerator.
@@ -311,13 +341,13 @@
end
## Yields (in lexicographical order) the source infos of all locations from
## the given source with the given source_info prefix
def each_source_info source_id, prefix='', &b
- prefix = mkterm :location, source_id, prefix
- each_prefixed_term prefix do |x|
- yield x[prefix.length..-1]
+ p = mkterm :location, source_id, prefix
+ each_prefixed_term p do |x|
+ yield prefix + x[p.length..-1]
end
end
class ParseError < StandardError; end
@@ -490,26 +520,30 @@
xapian_query = qp.parse_query(subs, Xapian::QueryParser::FLAG_PHRASE|Xapian::QueryParser::FLAG_BOOLEAN|Xapian::QueryParser::FLAG_LOVEHATE|Xapian::QueryParser::FLAG_WILDCARD)
rescue RuntimeError => e
raise ParseError, "xapian query parser error: #{e}"
end
- debug "parsed xapian query: #{Util::Query.describe(xapian_query)}"
+ debug "parsed xapian query: #{Util::Query.describe(xapian_query, subs)}"
raise ParseError if xapian_query.nil? or xapian_query.empty?
query[:qobj] = xapian_query
query[:text] = s
query
end
+ def save_message m
+ if @sync_worker
+ @sync_queue << m
+ else
+ update_message_state m
+ end
+ m.clear_dirty
+ end
+
def save_thread t
t.each_dirty_message do |m|
- if @sync_worker
- @sync_queue << m
- else
- update_message_state m
- end
- m.clear_dirty
+ save_message m
end
end
def start_sync_worker
@sync_worker = Redwood::reporting_thread('index sync') { run_sync_worker }
@@ -612,11 +646,11 @@
matchset = run_query xapian_query, offset, limit
matchset.matches.map { |r| r.document.value MSGID_VALUENO }
end
Q = Xapian::Query
- def build_xapian_query opts
+ def build_xapian_query opts, ignore_neg_terms = true
labels = ([opts[:label]] + (opts[:labels] || [])).compact
neglabels = [:spam, :deleted, :killed].reject { |l| (labels.include? l) || opts.member?("load_#{l}".intern) }
pos_terms, neg_terms = [], []
pos_terms << mkterm(:type, 'mail')
@@ -628,11 +662,11 @@
if opts[:participants]
participant_terms = opts[:participants].map { |p| [:from,:to].map { |d| mkterm(:email, d, (Redwood::Person === p) ? p.email : p) } }.flatten
pos_terms << Q.new(Q::OP_OR, participant_terms)
end
- neg_terms.concat(neglabels.map { |l| mkterm(:label,l) })
+ neg_terms.concat(neglabels.map { |l| mkterm(:label,l) }) if ignore_neg_terms
pos_query = Q.new(Q::OP_AND, pos_terms)
neg_query = Q.new(Q::OP_OR, neg_terms)
if neg_query.empty?
@@ -641,9 +675,13 @@
Q.new(Q::OP_AND_NOT, [pos_query, neg_query])
end
end
def sync_message m, overwrite
+ ## TODO: we should not save the message if the sync_back failed
+ ## since it would overwrite the location field
+ m.sync_back
+
doc = synchronize { find_doc(m.id) }
existed = doc != nil
doc ||= Xapian::Document.new
do_index_static = overwrite || !existed
old_entry = !do_index_static && doc.entry