use tantivy::query::{
BooleanQuery, BoostQuery, FuzzyTermQuery, Occur, PhraseQuery, Query, RegexQuery, TermQuery,
};
use tantivy::schema::{Field, IndexRecordOption, Schema};
use tantivy::Term;
use super::query_parser_chirho::QueryNodeChirho;
pub struct TantivyQueryBuilderChirho {
schema_chirho: Schema,
text_field_chirho: Field,
strongs_field_chirho: Option<Field>,
lemma_field_chirho: Option<Field>,
morph_field_chirho: Option<Field>,
}
impl TantivyQueryBuilderChirho {
pub fn new_chirho(schema_chirho: Schema, text_field_chirho: Field) -> Self {
Self {
schema_chirho,
text_field_chirho,
strongs_field_chirho: None,
lemma_field_chirho: None,
morph_field_chirho: None,
}
}
pub fn with_strongs_field_chirho(mut self, field_chirho: Field) -> Self {
self.strongs_field_chirho = Some(field_chirho);
self
}
pub fn with_lemma_field_chirho(mut self, field_chirho: Field) -> Self {
self.lemma_field_chirho = Some(field_chirho);
self
}
pub fn with_morph_field_chirho(mut self, field_chirho: Field) -> Self {
self.morph_field_chirho = Some(field_chirho);
self
}
pub fn build_chirho(&self, node_chirho: &QueryNodeChirho) -> Box<dyn Query> {
match node_chirho {
QueryNodeChirho::TermChirho(term_chirho) => {
self.build_term_query_chirho(term_chirho, self.text_field_chirho)
}
QueryNodeChirho::PhraseChirho { words_chirho, slop_chirho } => {
self.build_phrase_query_chirho(words_chirho, *slop_chirho)
}
QueryNodeChirho::AndChirho(left_chirho, right_chirho) => {
self.build_boolean_chirho(left_chirho, right_chirho, true)
}
QueryNodeChirho::OrChirho(left_chirho, right_chirho) => {
self.build_boolean_chirho(left_chirho, right_chirho, false)
}
QueryNodeChirho::NotChirho(inner_chirho) => {
self.build_not_query_chirho(inner_chirho)
}
QueryNodeChirho::FieldChirho { field_chirho, query_chirho } => {
self.build_field_query_chirho(field_chirho, query_chirho.as_ref())
}
QueryNodeChirho::FuzzyChirho { term_chirho, distance_chirho } => {
self.build_fuzzy_query_chirho(term_chirho, *distance_chirho)
}
QueryNodeChirho::WildcardChirho(pattern_chirho) => {
self.build_wildcard_query_chirho(pattern_chirho)
}
QueryNodeChirho::GroupChirho(inner_chirho) => {
self.build_chirho(inner_chirho)
}
}
}
fn build_term_query_chirho(&self, term_chirho: &str, field_chirho: Field) -> Box<dyn Query> {
let term_value_chirho = Term::from_field_text(field_chirho, &term_chirho.to_lowercase());
Box::new(TermQuery::new(term_value_chirho, IndexRecordOption::WithFreqs))
}
fn build_phrase_query_chirho(
&self,
words_chirho: &[String],
slop_chirho: Option<u32>,
) -> Box<dyn Query> {
let terms_chirho: Vec<Term> = words_chirho
.iter()
.map(|w_chirho| Term::from_field_text(self.text_field_chirho, &w_chirho.to_lowercase()))
.collect();
let mut phrase_query_chirho = PhraseQuery::new(terms_chirho);
if let Some(slop_value_chirho) = slop_chirho {
phrase_query_chirho.set_slop(slop_value_chirho);
}
Box::new(phrase_query_chirho)
}
fn build_boolean_chirho(
&self,
left_chirho: &QueryNodeChirho,
right_chirho: &QueryNodeChirho,
is_and_chirho: bool,
) -> Box<dyn Query> {
let left_query_chirho = self.build_chirho(left_chirho);
let right_query_chirho = self.build_chirho(right_chirho);
let occur_chirho = if is_and_chirho {
Occur::Must
} else {
Occur::Should
};
Box::new(BooleanQuery::new(vec![
(occur_chirho, left_query_chirho),
(occur_chirho, right_query_chirho),
]))
}
fn build_not_query_chirho(&self, inner_chirho: &QueryNodeChirho) -> Box<dyn Query> {
let inner_query_chirho = self.build_chirho(inner_chirho);
Box::new(BoostQuery::new(inner_query_chirho, -1.0))
}
fn build_field_query_chirho(
&self,
field_name_chirho: &str,
query_chirho: &QueryNodeChirho,
) -> Box<dyn Query> {
let field_chirho = match field_name_chirho.to_lowercase().as_str() {
"strongs" | "strong" => self.strongs_field_chirho.unwrap_or(self.text_field_chirho),
"lemma" => self.lemma_field_chirho.unwrap_or(self.text_field_chirho),
"morph" | "morphology" => self.morph_field_chirho.unwrap_or(self.text_field_chirho),
"text" => self.text_field_chirho,
_ => {
self.schema_chirho
.get_field(field_name_chirho)
.unwrap_or(self.text_field_chirho)
}
};
match query_chirho {
QueryNodeChirho::TermChirho(term_chirho) => {
self.build_term_query_chirho(term_chirho, field_chirho)
}
QueryNodeChirho::WildcardChirho(pattern_chirho) => {
self.build_regex_query_chirho(pattern_chirho, field_chirho)
}
_ => self.build_chirho(query_chirho),
}
}
fn build_fuzzy_query_chirho(&self, term_chirho: &str, distance_chirho: u8) -> Box<dyn Query> {
let term_value_chirho = Term::from_field_text(
self.text_field_chirho,
&term_chirho.to_lowercase(),
);
Box::new(FuzzyTermQuery::new(term_value_chirho, distance_chirho, true))
}
fn build_wildcard_query_chirho(&self, pattern_chirho: &str) -> Box<dyn Query> {
self.build_regex_query_chirho(pattern_chirho, self.text_field_chirho)
}
fn build_regex_query_chirho(&self, pattern_chirho: &str, field_chirho: Field) -> Box<dyn Query> {
let regex_pattern_chirho = pattern_chirho
.to_lowercase()
.replace('*', ".*")
.replace('?', ".");
match RegexQuery::from_pattern(®ex_pattern_chirho, field_chirho) {
Ok(query_chirho) => Box::new(query_chirho),
Err(_) => {
self.build_term_query_chirho(pattern_chirho, field_chirho)
}
}
}
}
#[derive(Debug, Clone)]
pub struct ScoredResultChirho {
pub key_chirho: String,
pub score_chirho: f32,
}
impl ScoredResultChirho {
pub fn new_chirho(key_chirho: String, score_chirho: f32) -> Self {
Self {
key_chirho,
score_chirho,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct SearchBuilderChirho {
query_chirho: String,
max_results_chirho: usize,
include_scores_chirho: bool,
min_score_chirho: Option<f32>,
fields_chirho: Vec<String>,
sort_by_score_chirho: bool,
}
impl SearchBuilderChirho {
pub fn new_chirho(query_chirho: &str) -> Self {
Self {
query_chirho: query_chirho.to_string(),
max_results_chirho: 100,
include_scores_chirho: false,
min_score_chirho: None,
fields_chirho: vec!["text".to_string()],
sort_by_score_chirho: true,
}
}
pub fn max_results_chirho(mut self, max_chirho: usize) -> Self {
self.max_results_chirho = max_chirho;
self
}
pub fn with_scores_chirho(mut self) -> Self {
self.include_scores_chirho = true;
self
}
pub fn min_score_chirho(mut self, score_chirho: f32) -> Self {
self.min_score_chirho = Some(score_chirho);
self
}
pub fn add_field_chirho(mut self, field_chirho: &str) -> Self {
self.fields_chirho.push(field_chirho.to_string());
self
}
pub fn fields_chirho(mut self, fields_chirho: Vec<&str>) -> Self {
self.fields_chirho = fields_chirho.iter().map(|s_chirho| s_chirho.to_string()).collect();
self
}
pub fn unsorted_chirho(mut self) -> Self {
self.sort_by_score_chirho = false;
self
}
pub fn query_string_chirho(&self) -> &str {
&self.query_chirho
}
pub fn get_max_results_chirho(&self) -> usize {
self.max_results_chirho
}
pub fn has_scores_chirho(&self) -> bool {
self.include_scores_chirho
}
pub fn get_min_score_chirho(&self) -> Option<f32> {
self.min_score_chirho
}
}
#[cfg(test)]
mod tests_chirho {
use super::*;
use tantivy::schema::{Schema, TEXT, STORED};
fn create_test_schema_chirho() -> (Schema, Field) {
let mut schema_builder_chirho = Schema::builder();
let text_field_chirho = schema_builder_chirho.add_text_field("text", TEXT | STORED);
(schema_builder_chirho.build(), text_field_chirho)
}
#[test]
fn test_build_term_query_chirho() {
let (schema_chirho, text_field_chirho) = create_test_schema_chirho();
let builder_chirho = TantivyQueryBuilderChirho::new_chirho(schema_chirho, text_field_chirho);
let node_chirho = QueryNodeChirho::TermChirho("love".to_string());
let _query_chirho = builder_chirho.build_chirho(&node_chirho);
}
#[test]
fn test_build_phrase_query_chirho() {
let (schema_chirho, text_field_chirho) = create_test_schema_chirho();
let builder_chirho = TantivyQueryBuilderChirho::new_chirho(schema_chirho, text_field_chirho);
let node_chirho = QueryNodeChirho::PhraseChirho {
words_chirho: vec!["God".to_string(), "is".to_string(), "love".to_string()],
slop_chirho: Some(2),
};
let _query_chirho = builder_chirho.build_chirho(&node_chirho);
}
#[test]
fn test_build_and_query_chirho() {
let (schema_chirho, text_field_chirho) = create_test_schema_chirho();
let builder_chirho = TantivyQueryBuilderChirho::new_chirho(schema_chirho, text_field_chirho);
let node_chirho = QueryNodeChirho::AndChirho(
Box::new(QueryNodeChirho::TermChirho("God".to_string())),
Box::new(QueryNodeChirho::TermChirho("love".to_string())),
);
let _query_chirho = builder_chirho.build_chirho(&node_chirho);
}
#[test]
fn test_build_fuzzy_query_chirho() {
let (schema_chirho, text_field_chirho) = create_test_schema_chirho();
let builder_chirho = TantivyQueryBuilderChirho::new_chirho(schema_chirho, text_field_chirho);
let node_chirho = QueryNodeChirho::FuzzyChirho {
term_chirho: "love".to_string(),
distance_chirho: 2,
};
let _query_chirho = builder_chirho.build_chirho(&node_chirho);
}
#[test]
fn test_build_wildcard_query_chirho() {
let (schema_chirho, text_field_chirho) = create_test_schema_chirho();
let builder_chirho = TantivyQueryBuilderChirho::new_chirho(schema_chirho, text_field_chirho);
let node_chirho = QueryNodeChirho::WildcardChirho("lov*".to_string());
let _query_chirho = builder_chirho.build_chirho(&node_chirho);
}
#[test]
fn test_search_builder_chirho() {
let builder_chirho = SearchBuilderChirho::new_chirho("love AND God")
.max_results_chirho(50)
.with_scores_chirho()
.min_score_chirho(0.5);
assert_eq!(builder_chirho.query_string_chirho(), "love AND God");
assert_eq!(builder_chirho.get_max_results_chirho(), 50);
assert!(builder_chirho.has_scores_chirho());
assert_eq!(builder_chirho.get_min_score_chirho(), Some(0.5));
}
#[test]
fn test_scored_result_chirho() {
let result_chirho = ScoredResultChirho::new_chirho("John.3.16".to_string(), 0.95);
assert_eq!(result_chirho.key_chirho, "John.3.16");
assert_eq!(result_chirho.score_chirho, 0.95);
}
}