- #Redwood::log "Building thread for #{m.id}: #{m.subj}"
- messages = {}
- searched = {}
- num_queries = 0
-
- pending = [m.id]
- if $config[:thread_by_subject] # do subject queries
- date_min = m.date - (SAME_SUBJECT_DATE_LIMIT * 12 * 3600)
- date_max = m.date + (SAME_SUBJECT_DATE_LIMIT * 12 * 3600)
-
- q = Ferret::Search::BooleanQuery.new true
- sq = Ferret::Search::PhraseQuery.new(:subject)
- wrap_subj(Message.normalize_subj(m.subj)).split.each do |t|
- sq.add_term t
- end
- q.add_query sq, :must
- q.add_query Ferret::Search::RangeQuery.new(:date, :>= => date_min.to_indexable_s, :<= => date_max.to_indexable_s), :must
-
- q = build_query :qobj => q
-
- p1 = @index_mutex.synchronize { @index.search(q).hits.map { |hit| @index[hit.doc][:message_id] } }
- Redwood::log "found #{p1.size} results for subject query #{q}"
-
- p2 = @index_mutex.synchronize { @index.search(q.to_s, :limit => :all).hits.map { |hit| @index[hit.doc][:message_id] } }
- Redwood::log "found #{p2.size} results in string form"
-
- pending = (pending + p1 + p2).uniq
- end
-
- until pending.empty? || (opts[:limit] && messages.size >= opts[:limit])
- q = Ferret::Search::BooleanQuery.new true
- # this disappeared in newer ferrets... wtf.
- # q.max_clause_count = 2048
-
- lim = [MAX_CLAUSES / 2, pending.length].min
- pending[0 ... lim].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 = pending[lim .. -1]
-
- q = build_query :qobj => q
-
- num_queries += 1
- killed = false
- @index_mutex.synchronize do
- @index.search_each(q, :limit => :all) do |docid, score|
- break if opts[:limit] && messages.size >= opts[:limit]
- if @index[docid][:label].split(/\s+/).include?("killed") && opts[:skip_killed]
- killed = true
- break
- end
- 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.select { |id| !searched[id] }
- end
- 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} messages for #{m.id}: #{m.subj}" if num_queries > 0
- messages.each { |mid, builder| yield mid, builder }
- true
- end
- end
-
- ## builds a message object from a ferret result
- def build_message docid
- @index_mutex.synchronize do
- doc = @index[docid]
-
- source = @source_mutex.synchronize { @sources[doc[:source_id].to_i] }
- raise "invalid source #{doc[:source_id]}" unless source
-
- #puts "building message #{doc[:message_id]} (#{source}##{doc[:source_info]})"
-
- fake_header = {
- "date" => Time.at(doc[:date].to_i),
- "subject" => unwrap_subj(doc[:subject]),
- "from" => doc[:from],
- "to" => doc[:to].split.join(", "), # reformat
- "message-id" => doc[:message_id],
- "references" => doc[:refs].split.map { |x| "<#{x}>" }.join(" "),
- }
-
- m = Message.new :source => source, :source_info => doc[:source_info].to_i,
- :labels => doc[:label].symbolistize,
- :snippet => doc[:snippet]
- m.parse_header fake_header
- m
- end
- end
-
- def fresh_thread_id; @next_thread_id += 1; end
- def wrap_subj subj; "__START_SUBJECT__ #{subj} __END_SUBJECT__"; end
- def unwrap_subj subj; subj =~ /__START_SUBJECT__ (.*?) __END_SUBJECT__/ && $1; end
-
- def drop_entry docno; @index_mutex.synchronize { @index.delete docno } end
-
- def load_entry_for_id mid
- @index_mutex.synchronize do
- results = @index.search Ferret::Search::TermQuery.new(:message_id, mid)
- return if results.total_hits == 0
- docid = results.hits[0].doc
- entry = @index[docid]
- entry_dup = entry.fields.inject({}) { |h, f| h[f] = entry[f]; h }
- [docid, entry_dup]
- end
- end
-
- def load_contacts emails, h={}
- q = Ferret::Search::BooleanQuery.new true
- emails.each do |e|
- qq = Ferret::Search::BooleanQuery.new true
- qq.add_query Ferret::Search::TermQuery.new(:from, e), :should
- qq.add_query Ferret::Search::TermQuery.new(:to, e), :should
- q.add_query qq
- end
- q.add_query Ferret::Search::TermQuery.new(:label, "spam"), :must_not
-
- Redwood::log "contact search: #{q}"
- contacts = {}
- num = h[:num] || 20
- @index_mutex.synchronize do
- @index.search_each q, :sort => "date DESC", :limit => :all do |docid, score|
- break if contacts.size >= num
- #Redwood::log "got message #{docid} to: #{@index[docid][:to].inspect} and from: #{@index[docid][:from].inspect}"
- f = @index[docid][:from]
- t = @index[docid][:to]
-
- if AccountManager.is_account_email? f
- t.split(" ").each { |e| contacts[Person.from_address(e)] = true }
- else
- contacts[Person.from_address(f)] = true
- end
- end
- end
-
- contacts.keys.compact