reifydb_catalog/store/dictionary/
find.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4use reifydb_core::{
5	interface::{DictionaryDef, DictionaryId, MultiVersionValues, NamespaceId, QueryTransaction},
6	key::{DictionaryKey, NamespaceDictionaryKey},
7};
8use reifydb_type::Type;
9
10use crate::{
11	CatalogStore,
12	store::dictionary::layout::{dictionary, dictionary_namespace},
13};
14
15impl CatalogStore {
16	pub async fn find_dictionary(
17		rx: &mut impl QueryTransaction,
18		dictionary_id: DictionaryId,
19	) -> crate::Result<Option<DictionaryDef>> {
20		let Some(multi) = rx.get(&DictionaryKey::encoded(dictionary_id)).await? else {
21			return Ok(None);
22		};
23
24		let row = multi.values;
25		let id = DictionaryId(dictionary::LAYOUT.get_u64(&row, dictionary::ID));
26		let namespace = NamespaceId(dictionary::LAYOUT.get_u64(&row, dictionary::NAMESPACE));
27		let name = dictionary::LAYOUT.get_utf8(&row, dictionary::NAME).to_string();
28		let value_type_ordinal = dictionary::LAYOUT.get_u8(&row, dictionary::VALUE_TYPE);
29		let id_type_ordinal = dictionary::LAYOUT.get_u8(&row, dictionary::ID_TYPE);
30
31		Ok(Some(DictionaryDef {
32			id,
33			namespace,
34			name,
35			value_type: Type::from_u8(value_type_ordinal),
36			id_type: Type::from_u8(id_type_ordinal),
37		}))
38	}
39
40	pub async fn find_dictionary_by_name(
41		rx: &mut impl QueryTransaction,
42		namespace: NamespaceId,
43		name: impl AsRef<str>,
44	) -> crate::Result<Option<DictionaryDef>> {
45		let name = name.as_ref();
46		let batch = rx.range(NamespaceDictionaryKey::full_scan(namespace)).await?;
47		let Some(dictionary_id) = batch.items.iter().find_map(|multi: &MultiVersionValues| {
48			let row = &multi.values;
49			let dictionary_name = dictionary_namespace::LAYOUT.get_utf8(row, dictionary_namespace::NAME);
50			if name == dictionary_name {
51				Some(DictionaryId(dictionary_namespace::LAYOUT.get_u64(row, dictionary_namespace::ID)))
52			} else {
53				None
54			}
55		}) else {
56			return Ok(None);
57		};
58
59		Ok(Some(Self::get_dictionary(rx, dictionary_id).await?))
60	}
61}
62
63#[cfg(test)]
64mod tests {
65	use reifydb_core::interface::DictionaryId;
66	use reifydb_engine::test_utils::create_test_command_transaction;
67	use reifydb_type::Type;
68
69	use crate::{
70		CatalogStore, namespace::NamespaceToCreate, store::dictionary::create::DictionaryToCreate,
71		test_utils::ensure_test_namespace,
72	};
73
74	#[tokio::test]
75	async fn test_find_dictionary_exists() {
76		let mut txn = create_test_command_transaction().await;
77		let test_namespace = ensure_test_namespace(&mut txn).await;
78
79		let to_create = DictionaryToCreate {
80			namespace: test_namespace.id,
81			dictionary: "test_dict".to_string(),
82			value_type: Type::Utf8,
83			id_type: Type::Uint2,
84			fragment: None,
85		};
86
87		let created = CatalogStore::create_dictionary(&mut txn, to_create).await.unwrap();
88
89		let found = CatalogStore::find_dictionary(&mut txn, created.id)
90			.await
91			.unwrap()
92			.expect("Dictionary should exist");
93
94		assert_eq!(found.id, created.id);
95		assert_eq!(found.name, created.name);
96		assert_eq!(found.namespace, created.namespace);
97		assert_eq!(found.value_type, Type::Utf8);
98		assert_eq!(found.id_type, Type::Uint2);
99	}
100
101	#[tokio::test]
102	async fn test_find_dictionary_not_exists() {
103		let mut txn = create_test_command_transaction().await;
104
105		let result = CatalogStore::find_dictionary(&mut txn, DictionaryId(999)).await.unwrap();
106
107		assert!(result.is_none());
108	}
109
110	#[tokio::test]
111	async fn test_find_dictionary_by_name_exists() {
112		let mut txn = create_test_command_transaction().await;
113		let namespace = ensure_test_namespace(&mut txn).await;
114
115		let to_create = DictionaryToCreate {
116			namespace: namespace.id,
117			dictionary: "token_mints".to_string(),
118			value_type: Type::Utf8,
119			id_type: Type::Uint4,
120			fragment: None,
121		};
122
123		let created = CatalogStore::create_dictionary(&mut txn, to_create).await.unwrap();
124
125		let found = CatalogStore::find_dictionary_by_name(&mut txn, namespace.id, "token_mints")
126			.await
127			.unwrap()
128			.expect("Should find dictionary by name");
129
130		assert_eq!(found.id, created.id);
131		assert_eq!(found.name, "token_mints");
132		assert_eq!(found.value_type, Type::Utf8);
133		assert_eq!(found.id_type, Type::Uint4);
134	}
135
136	#[tokio::test]
137	async fn test_find_dictionary_by_name_not_exists() {
138		let mut txn = create_test_command_transaction().await;
139		let namespace = ensure_test_namespace(&mut txn).await;
140
141		let result = CatalogStore::find_dictionary_by_name(&mut txn, namespace.id, "nonexistent_dict")
142			.await
143			.unwrap();
144
145		assert!(result.is_none());
146	}
147
148	#[tokio::test]
149	async fn test_find_dictionary_by_name_different_namespace() {
150		let mut txn = create_test_command_transaction().await;
151		let namespace1 = ensure_test_namespace(&mut txn).await;
152
153		// Create namespace2
154		let namespace2 = CatalogStore::create_namespace(
155			&mut txn,
156			NamespaceToCreate {
157				namespace_fragment: None,
158				name: "namespace2".to_string(),
159			},
160		)
161		.await
162		.unwrap();
163
164		// Create dictionary in namespace1
165		let to_create = DictionaryToCreate {
166			namespace: namespace1.id,
167			dictionary: "shared_name".to_string(),
168			value_type: Type::Utf8,
169			id_type: Type::Uint2,
170			fragment: None,
171		};
172
173		CatalogStore::create_dictionary(&mut txn, to_create).await.unwrap();
174
175		// Try to find in namespace2 - should not exist
176		let result =
177			CatalogStore::find_dictionary_by_name(&mut txn, namespace2.id, "shared_name").await.unwrap();
178
179		assert!(result.is_none());
180
181		// Find in namespace1 - should exist
182		let found =
183			CatalogStore::find_dictionary_by_name(&mut txn, namespace1.id, "shared_name").await.unwrap();
184
185		assert!(found.is_some());
186	}
187}