///|
/// Stable read view over one immutable Segment set.
///
/// Searchers keep the exact Segment array and tombstones captured by their
/// IndexReader generation. Later commits cannot mutate this view.
pub struct Searcher {
segments : ReadOnlyArray[SnapshotSegment]
statistics : SearchStatistics
}
///|
/// Creates a Searcher for one in-memory Segment.
pub fn Searcher::new(segment : Segment) -> Searcher {
Searcher::from_segments(ReadOnlyArray::from_array([live_snapshot(segment)]))
}
///|
pub fn Searcher::from_segments(
segments : ReadOnlyArray[SnapshotSegment],
) -> Searcher {
{ segments, statistics: SearchStatistics::from_segments(segments) }
}
///|
pub fn Searcher::segment_count(self : Searcher) -> Int {
self.segments.length()
}
///|
/// Returns the number of live documents visible in this Searcher snapshot.
pub fn Searcher::doc_count(self : Searcher) -> Int {
self.statistics.doc_count()
}
///|
/// Builds one Weight from snapshot-wide statistics, runs a Scorer against
/// every Segment, then performs one global Top-K selection.
pub fn Searcher::search(
self : Searcher,
query : &Query,
collector : TopKCollector,
) -> ReadOnlyArray[SearchHit] {
let weight = query.weight(self.statistics)
let heap = TopKHeap::new(collector.limit)
for segment_ord in 0.. Array[SearchHit] {
let hits : Array[SearchHit] = []
let weight = query.weight(self.statistics)
for segment_ord in 0.. Int {
if collector.enabled {
self.all_hits(query).length()
} else {
0
}
}
///|
pub fn Searcher::top_docs(
self : Searcher,
query : &Query,
collector : TopDocsCollector,
) -> ReadOnlyArray[SearchHit] {
self.search(query, TopKCollector::new(collector.limit))
}
///|
pub fn Searcher::collect(
self : Searcher,
query : &Query,
collector : MultiCollector,
) -> MultiCollectorResult {
let weight = query.weight(self.statistics)
let heap = TopKHeap::new(collector.top_docs_limit)
let mut count = 0
for segment_ord in 0.. FieldValue? {
if values.length() == 0 {
return None
}
let mut selected = values[0]
for index in 1..
if (order == Ascending && comparison < 0) ||
(order == Descending && comparison > 0) {
selected = values[index]
}
None => ()
}
}
Some(selected)
}
///|
fn Searcher::compare_sorted_hits(
self : Searcher,
left : SearchHit,
right : SearchHit,
sort : Sort,
) -> Int {
let left_segment = self.segments[left.address.segment_ord].segment
let right_segment = self.segments[right.address.segment_ord].segment
for field in sort.fields {
let left_value = representative_sort_value(
left_segment.fast_values(field.field_id, left.address.doc_id),
field.order,
)
let right_value = representative_sort_value(
right_segment.fast_values(field.field_id, right.address.doc_id),
field.order,
)
let comparison = match (left_value, right_value) {
(None, None) => 0
(None, Some(_)) => if field.missing == MissingFirst { -1 } else { 1 }
(Some(_), None) => if field.missing == MissingFirst { 1 } else { -1 }
(Some(left_value), Some(right_value)) =>
match compare_field_values(left_value, right_value) {
Some(comparison) =>
if field.order == Descending {
-comparison
} else {
comparison
}
None => 0
}
}
if comparison != 0 {
return comparison
}
}
if left.address.segment_ord != right.address.segment_ord {
left.address.segment_ord.compare(right.address.segment_ord)
} else {
left.address.doc_id.value.compare(right.address.doc_id.value)
}
}
///|
pub fn Searcher::search_sorted(
self : Searcher,
query : &Query,
sort : Sort,
limit : Int,
) -> ReadOnlyArray[SearchHit] {
if limit <= 0 {
return []
}
let hits = self.all_hits(query)
hits.sort_by((left, right) => self.compare_sorted_hits(left, right, sort))
let result : Array[SearchHit] = []
let count = if hits.length() < limit { hits.length() } else { limit }
for index in 0.. StoredDocument? {
if address.segment_ord < 0 || address.segment_ord >= self.segments.length() {
None
} else {
let snapshot = self.segments[address.segment_ord]
if snapshot.is_deleted(address.doc_id) {
None
} else {
snapshot.segment.doc(address.doc_id)
}
}
}