#[tokio::test]
async fn test_search_trigram_punctuated_and_decimal_queries_matches_exact_ids() {
let store = setup_trigram_store("trigram_punctuated");
let hyphen_id = Uuid::new_v4();
store
.upsert_document(make_document(hyphen_id, "doc", "khive-pack-memory crate"))
.await
.unwrap();
let dotted_id = Uuid::new_v4();
store
.upsert_document(make_document(
dotted_id,
"doc",
"the khive.pack.memory module",
))
.await
.unwrap();
let decimal_id = Uuid::new_v4();
store
.upsert_document(make_document(
decimal_id,
"doc",
"salience 0.9 vs 0.3 comparison",
))
.await
.unwrap();
let version_id = Uuid::new_v4();
store
.upsert_document(make_document(version_id, "doc", "released version 1.2.3"))
.await
.unwrap();
let legacy_id = Uuid::new_v4();
store
.upsert_document(make_document(
legacy_id,
"DSL docs",
"chain results with the $prev.id token",
))
.await
.unwrap();
let unrelated_id = Uuid::new_v4();
store
.upsert_document(make_document(
unrelated_id,
"doc",
"completely unrelated content about gardening",
))
.await
.unwrap();
for mode in [TextQueryMode::Plain, TextQueryMode::AnyTerm] {
for (query, expected_id, label) in [
("khive-pack-memory", hyphen_id, "hyphenated identifier"),
("khive.pack.memory", dotted_id, "dotted identifier"),
("0.9", decimal_id, "decimal"),
("1.2.3", version_id, "version string"),
("$prev.id", legacy_id, "legacy dollar+dot query"),
] {
let hits = store
.search(TextSearchRequest {
query: query.to_string(),
mode: mode.clone(),
filter: Some(ns_filter("test_ns")),
top_k: 10,
snippet_chars: 0,
})
.await
.unwrap();
let hit_ids: std::collections::HashSet<_> =
hits.iter().map(|hit| hit.subject_id).collect();
assert!(
hit_ids.contains(&expected_id),
"trigram {mode:?} query {query:?} ({label}) must match {expected_id}; got {hit_ids:?}"
);
assert!(
!hit_ids.contains(&unrelated_id),
"trigram {mode:?} query {query:?} ({label}) must not match unrelated doc {unrelated_id}; got {hit_ids:?}"
);
}
}
}
#[tokio::test]
async fn test_search_trigram_date_query_does_not_broaden_to_same_year() {
let store = setup_trigram_store("trigram_date");
let target_id = Uuid::new_v4();
store
.upsert_document(make_document(
target_id,
"doc",
"changelog entry dated 2026-07-10",
))
.await
.unwrap();
let other_day_id = Uuid::new_v4();
store
.upsert_document(make_document(
other_day_id,
"doc",
"changelog entry dated 2026-03-15",
))
.await
.unwrap();
for mode in [TextQueryMode::Plain, TextQueryMode::AnyTerm] {
let hits = store
.search(TextSearchRequest {
query: "2026-07-10".to_string(),
mode: mode.clone(),
filter: Some(ns_filter("test_ns")),
top_k: 10,
snippet_chars: 0,
})
.await
.unwrap();
let hit_ids: std::collections::HashSet<_> = hits.iter().map(|hit| hit.subject_id).collect();
assert!(
hit_ids.contains(&target_id),
"trigram {mode:?} date query must match its own date {target_id}; got {hit_ids:?}"
);
assert!(
!hit_ids.contains(&other_day_id),
"trigram {mode:?} date query for 2026-07-10 must not broaden to 2026-03-15 \
({other_day_id}); got {hit_ids:?}"
);
}
}
#[tokio::test]
async fn test_search_trigram_operator_short_operand_does_not_broaden() {
let store = setup_trigram_store("trigram_operator_short_operand");
let unrelated_id = Uuid::new_v4();
store
.upsert_document(make_document(
unrelated_id,
"doc",
"an alphabeta widget completely unrelated to any proximity or negation query",
))
.await
.unwrap();
for (query, label) in [
(
"NEAR(alpha-beta,5)",
"NEAR operator with a short numeric operand",
),
(
"NOT(alpha-beta,5)",
"NOT operator with a short numeric operand",
),
] {
for mode in [TextQueryMode::Plain, TextQueryMode::AnyTerm] {
let hits = store
.search(TextSearchRequest {
query: query.to_string(),
mode: mode.clone(),
filter: Some(ns_filter("test_ns")),
top_k: 10,
snippet_chars: 0,
})
.await
.unwrap();
let hit_ids: std::collections::HashSet<_> =
hits.iter().map(|hit| hit.subject_id).collect();
assert!(
!hit_ids.contains(&unrelated_id),
"trigram {mode:?} query {query:?} ({label}) must not broaden to match \
{unrelated_id} via the trigram-unsafe multi-term merged alternative; \
got {hit_ids:?}"
);
}
}
}