use crate::types::{SearchModifier, SearchValue};
use super::super::query_builder::{SqlFragment, SqlParam};
pub struct ReferenceHandler;
impl ReferenceHandler {
pub fn build_sql(
value: &SearchValue,
modifier: Option<&SearchModifier>,
param_offset: usize,
) -> SqlFragment {
let param_num = param_offset + 1;
let ref_value = &value.value;
if matches!(modifier, Some(SearchModifier::Identifier)) {
return Self::build_identifier_condition(ref_value, param_num);
}
if let Some(SearchModifier::Type(type_name)) = modifier {
let expected_prefix = format!("{}/", type_name);
if ref_value.contains('/') {
SqlFragment::with_params(
format!("value_reference = ?{}", param_num),
vec![SqlParam::string(ref_value)],
)
} else {
SqlFragment::with_params(
format!("value_reference = ?{}", param_num),
vec![SqlParam::string(format!(
"{}{}",
expected_prefix, ref_value
))],
)
}
} else {
Self::build_reference_condition(ref_value, param_num)
}
}
fn build_reference_condition(ref_value: &str, param_num: usize) -> SqlFragment {
if ref_value.contains('/') {
SqlFragment::with_params(
format!("value_reference = ?{}", param_num),
vec![SqlParam::string(ref_value)],
)
} else {
SqlFragment::with_params(
format!(
"(value_reference = ?{} OR value_reference LIKE '%/' || ?{})",
param_num,
param_num + 1
),
vec![SqlParam::string(ref_value), SqlParam::string(ref_value)],
)
}
}
fn build_identifier_condition(identifier_value: &str, param_num: usize) -> SqlFragment {
if let Some(pipe_pos) = identifier_value.find('|') {
let system = &identifier_value[..pipe_pos];
let value = &identifier_value[pipe_pos + 1..];
if system.is_empty() {
SqlFragment::with_params(
format!(
"EXISTS (SELECT 1 FROM search_index si2 WHERE si2.resource_id = SUBSTR(value_reference, INSTR(value_reference, '/') + 1) AND si2.param_name = 'identifier' AND (si2.value_token_system IS NULL OR si2.value_token_system = '') AND si2.value_token_code = ?{})",
param_num
),
vec![SqlParam::string(value)],
)
} else if value.is_empty() {
SqlFragment::with_params(
format!(
"EXISTS (SELECT 1 FROM search_index si2 WHERE si2.resource_id = SUBSTR(value_reference, INSTR(value_reference, '/') + 1) AND si2.param_name = 'identifier' AND si2.value_token_system = ?{})",
param_num
),
vec![SqlParam::string(system)],
)
} else {
SqlFragment::with_params(
format!(
"EXISTS (SELECT 1 FROM search_index si2 WHERE si2.resource_id = SUBSTR(value_reference, INSTR(value_reference, '/') + 1) AND si2.param_name = 'identifier' AND si2.value_token_system = ?{} AND si2.value_token_code = ?{})",
param_num,
param_num + 1
),
vec![SqlParam::string(system), SqlParam::string(value)],
)
}
} else {
SqlFragment::with_params(
format!(
"EXISTS (SELECT 1 FROM search_index si2 WHERE si2.resource_id = SUBSTR(value_reference, INSTR(value_reference, '/') + 1) AND si2.param_name = 'identifier' AND si2.value_token_code = ?{})",
param_num
),
vec![SqlParam::string(identifier_value)],
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::SearchPrefix;
#[test]
fn test_reference_with_type() {
let value = SearchValue::new(SearchPrefix::Eq, "Patient/123");
let frag = ReferenceHandler::build_sql(&value, None, 0);
assert!(frag.sql.contains("value_reference = ?1"));
assert_eq!(frag.params.len(), 1);
}
#[test]
fn test_reference_id_only() {
let value = SearchValue::new(SearchPrefix::Eq, "123");
let frag = ReferenceHandler::build_sql(&value, None, 0);
assert!(frag.sql.contains("OR"));
assert!(frag.sql.contains("LIKE"));
}
#[test]
fn test_reference_type_modifier() {
let value = SearchValue::new(SearchPrefix::Eq, "123");
let frag = ReferenceHandler::build_sql(
&value,
Some(&SearchModifier::Type("Patient".to_string())),
0,
);
assert!(frag.sql.contains("value_reference = ?1"));
}
#[test]
fn test_reference_identifier_modifier() {
let value = SearchValue::new(SearchPrefix::Eq, "http://example.org|12345");
let frag = ReferenceHandler::build_sql(&value, Some(&SearchModifier::Identifier), 0);
assert!(frag.sql.contains("EXISTS"));
assert!(frag.sql.contains("identifier"));
}
}