From 5b596eba11846c1d8088576fbfec5e337cb0ed48 Mon Sep 17 00:00:00 2001 From: Hayden Hung Hoang Date: Wed, 19 Nov 2025 23:22:53 +0700 Subject: [PATCH] derive: add support for field attribute `reference` --- README.md | 1 + typesense/tests/derive/collection.rs | 4 ++++ typesense_derive/src/field_attributes.rs | 17 ++++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 96fef4b5..36cc700d 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ let result = client | `num_dim` | integer | Set this to a non-zero value to treat a field of type `float[]` as a vector field. | | `locale` | string | Locale for text processing | | `vec_dist` | string | Distance metric to be used for vector search | +| `reference` | string | Name of a field in another collection to be used for JOINs | | `type` | string | Override the field type in Typesense | | `rename` | string | Rename the field in the Typesense schema | | `flatten` | -- | Generate Typesense field schemas for a nested struct | diff --git a/typesense/tests/derive/collection.rs b/typesense/tests/derive/collection.rs index bec0faa7..87ac4d18 100644 --- a/typesense/tests/derive/collection.rs +++ b/typesense/tests/derive/collection.rs @@ -110,6 +110,9 @@ struct KitchenSinkProduct { // optional = false should override the Option type #[typesense(optional = false)] optional_field: Option, + + #[typesense(reference = "company.id")] + company_id: String, } #[test] @@ -137,6 +140,7 @@ fn derived_document_handles_all_attributes() { { "name": "btree_map_vec", "type": "object[]" }, { "name": "optional_field", "type": "int32", "optional": false }, + {"name": "company_id", "type": "string", "reference": "company.id"} ], "default_sorting_field": "renamed_price", "token_separators": ["-", "/"], diff --git a/typesense_derive/src/field_attributes.rs b/typesense_derive/src/field_attributes.rs index 8cdfcdbf..4bb15e7f 100644 --- a/typesense_derive/src/field_attributes.rs +++ b/typesense_derive/src/field_attributes.rs @@ -20,6 +20,7 @@ pub(crate) struct FieldAttributes { flatten: bool, pub(crate) rename: Option, skip: bool, + reference: Option, } // This function will parse #[typesense(...)] on a FIELD @@ -209,6 +210,16 @@ pub(crate) fn extract_field_attrs(field: &Field) -> syn::Result } res.vec_dist = Some(string_literal(&mut tt_iter)?); } + "reference" => { + skip_eq(&i, &mut tt_iter)?; + if res.reference.is_some() { + return Err(syn::Error::new_spanned( + &i, + "Attribute `reference` is duplicated", + )); + } + res.reference = Some(string_literal(&mut tt_iter)?); + } "type" => { skip_eq(&i, &mut tt_iter)?; if res.type_override.is_some() { @@ -283,12 +294,16 @@ fn build_regular_field(field: &Field, field_attrs: &FieldAttributes) -> proc_mac let range_index = field_attrs.range_index.map(|v| quote!(.range_index(#v))); let locale = field_attrs.locale.as_ref().map(|v| quote!(.locale(#v))); let vec_dist = field_attrs.vec_dist.as_ref().map(|v| quote!(.vec_dist(#v))); + let reference = field_attrs + .reference + .as_ref() + .map(|v| quote!(.reference(#v))); let num_dim = field_attrs.num_dim.map(|v| quote!(.num_dim(#v))); quote! { vec![ typesense::models::Field::builder().name(#field_name).r#type(#typesense_field_type) - #optional #facet #index #store #sort #infix #stem #range_index #locale #vec_dist #num_dim + #optional #facet #index #store #sort #infix #stem #range_index #locale #vec_dist #reference #num_dim .build() ] }