feat(search): build documents resolving term/authority labels; reindex_all
Implements build_document in the search crate: resolves Term and Authority flexible-field values to their human-readable labels so reindex_all produces documents that Meilisearch can match on label text, not raw UUIDs. Adds integration test covering the full reindex→search round-trip. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -174,11 +174,77 @@ impl SearchClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a SearchDocument from an object (implemented in Task 2).
|
||||
#[allow(clippy::unimplemented)] // Task 2
|
||||
/// Build a [`SearchDocument`] from a catalogue object, resolving term and authority
|
||||
/// references to their human-readable labels so Meilisearch can match on them.
|
||||
pub async fn build_document(
|
||||
_db: &Db,
|
||||
_object: &CatalogueObject,
|
||||
db: &Db,
|
||||
object: &CatalogueObject,
|
||||
) -> Result<SearchDocument, SearchError> {
|
||||
unimplemented!("implemented in Task 2")
|
||||
let mut fields_text = Vec::new();
|
||||
|
||||
if let Some(map) = object.fields.as_object() {
|
||||
for (key, value) in map {
|
||||
let Some(def) = db::fields::field_definition_by_key(db.pool(), key).await? else {
|
||||
// Stale field with no definition — skip.
|
||||
continue;
|
||||
};
|
||||
|
||||
match def.field_type {
|
||||
domain::FieldType::Text | domain::FieldType::Date => {
|
||||
if let Some(s) = value.as_str() {
|
||||
fields_text.push(s.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
domain::FieldType::Integer | domain::FieldType::Boolean => {
|
||||
fields_text.push(value.to_string());
|
||||
}
|
||||
|
||||
domain::FieldType::LocalizedText => {
|
||||
if let Some(obj) = value.as_object() {
|
||||
for v in obj.values() {
|
||||
if let Some(s) = v.as_str() {
|
||||
fields_text.push(s.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
domain::FieldType::Term { .. } => {
|
||||
if let Some(term_id) = value
|
||||
.as_str()
|
||||
.and_then(|s| s.parse::<domain::TermId>().ok())
|
||||
{
|
||||
if let Some(term) = db::vocab::term_by_id(db.pool(), term_id).await? {
|
||||
fields_text.extend(term.labels.into_iter().map(|l| l.label));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
domain::FieldType::Authority { .. } => {
|
||||
if let Some(authority_id) = value
|
||||
.as_str()
|
||||
.and_then(|s| s.parse::<domain::AuthorityId>().ok())
|
||||
{
|
||||
if let Some(authority) =
|
||||
db::authority::authority_by_id(db.pool(), authority_id).await?
|
||||
{
|
||||
fields_text.extend(authority.labels.into_iter().map(|l| l.label));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SearchDocument {
|
||||
id: object.id.to_string(),
|
||||
object_number: object.object_number.clone(),
|
||||
object_name: object.object_name.clone(),
|
||||
brief_description: object.brief_description.clone(),
|
||||
current_owner: object.current_owner.clone(),
|
||||
recorder: object.recorder.clone(),
|
||||
visibility: object.visibility.as_str().to_owned(),
|
||||
fields_text,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user