///|
/// Compares two values of the same logical field type.
pub fn compare_field_values(left : FieldValue, right : FieldValue) -> Int? {
match (left, right) {
(@core.Text(left), @core.Text(right)) => Some(left.compare(right))
(@core.Keyword(left), @core.Keyword(right)) => Some(left.compare(right))
(@core.I64(left), @core.I64(right)) => Some(left.compare(right))
(@core.U64(left), @core.U64(right)) => Some(left.compare(right))
(@core.F64(left), @core.F64(right)) =>
if left.is_nan() || right.is_nan() {
None
} else if left < right {
Some(-1)
} else if left > right {
Some(1)
} else {
Some(0)
}
(@core.Bool(left), @core.Bool(right)) =>
Some(if left == right { 0 } else if left { 1 } else { -1 })
(@core.Date(left), @core.Date(right)) => Some(left.compare(right))
(@core.Bytes(left), @core.Bytes(right)) => {
let common = if left.length() < right.length() {
left.length()
} else {
right.length()
}
for index in 0.. None
}
}
///|
pub fn field_value_as_double(value : FieldValue) -> Double? {
match value {
@core.I64(value) => Some(value.to_double())
@core.U64(value) => Some(value.to_double())
@core.F64(value) if !value.is_nan() => Some(value)
@core.Date(value) => Some(value.to_double())
_ => None
}
}
///|
priv enum TypedPredicate {
Exact(FieldValue)
Range(FieldValue?, Bool, FieldValue?, Bool)
Exists
}
///|
pub struct ExactQuery {
field_id : FieldId
value : FieldValue
}
///|
pub fn ExactQuery::new(field_id : FieldId, value : FieldValue) -> ExactQuery {
{ field_id, value }
}
///|
pub struct RangeQuery {
field_id : FieldId
lower : FieldValue?
lower_inclusive : Bool
upper : FieldValue?
upper_inclusive : Bool
}
///|
pub fn RangeQuery::new(
field_id : FieldId,
lower : FieldValue?,
lower_inclusive : Bool,
upper : FieldValue?,
upper_inclusive : Bool,
) -> RangeQuery {
{ field_id, lower, lower_inclusive, upper, upper_inclusive }
}
///|
pub struct ExistsQuery {
field_id : FieldId
}
///|
pub fn ExistsQuery::new(field_id : FieldId) -> ExistsQuery {
{ field_id, }
}
///|
priv struct TypedWeight {
field_id : FieldId
predicate : TypedPredicate
}
///|
priv struct TypedScorer {
segment : Segment
field_id : FieldId
predicate : TypedPredicate
mut current_doc : DocId
}
///|
fn TypedScorer::matches(self : TypedScorer, doc_id : DocId) -> Bool {
let values = self.segment.field_values(doc_id, self.field_id)
match self.predicate {
Exists => values.length() > 0
Exact(expected) =>
values.search_by(value => compare_field_values(value, expected) == Some(0))
is Some(_)
Range(lower, lower_inclusive, upper, upper_inclusive) => {
for value in values {
let lower_matches = match lower {
None => true
Some(bound) =>
match compare_field_values(value, bound) {
Some(order) => order > 0 || (lower_inclusive && order == 0)
None => false
}
}
let upper_matches = match upper {
None => true
Some(bound) =>
match compare_field_values(value, bound) {
Some(order) => order < 0 || (upper_inclusive && order == 0)
None => false
}
}
if lower_matches && upper_matches {
return true
}
}
false
}
}
}
///|
impl Scorer for TypedScorer with fn advance(self) {
let target = if self.current_doc.value < 0 {
DocId::new(0)
} else {
DocId::new(self.current_doc.value + 1)
}
Scorer::advance_to(self, target)
}
///|
impl Scorer for TypedScorer with fn advance_to(self, target) {
if self.current_doc.value >= target.value {
return true
}
let mut candidate = if target.value < 0 { 0 } else { target.value }
while candidate < self.segment.doc_count() {
let doc_id = DocId::new(candidate)
if self.matches(doc_id) {
self.current_doc = doc_id
return true
}
candidate += 1
}
self.current_doc = DocId::new(-1)
false
}
///|
impl Scorer for TypedScorer with fn doc(self) {
self.current_doc
}
///|
impl Scorer for TypedScorer with fn score(_self) {
1.0
}
///|
impl Weight for TypedWeight with fn scorer(self, segment) {
TypedScorer::{
segment,
field_id: self.field_id,
predicate: self.predicate,
current_doc: DocId::new(-1),
}
as &Scorer
}
///|
pub impl Query for ExactQuery with fn weight(self, _statistics) {
TypedWeight::{ field_id: self.field_id, predicate: Exact(self.value) }
as &Weight
}
///|
pub impl Query for RangeQuery with fn weight(self, _statistics) {
TypedWeight::{
field_id: self.field_id,
predicate: Range(
self.lower,
self.lower_inclusive,
self.upper,
self.upper_inclusive,
),
}
as &Weight
}
///|
pub impl Query for ExistsQuery with fn weight(self, _statistics) {
TypedWeight::{ field_id: self.field_id, predicate: Exists } as &Weight
}