Skip to main content

reifydb_core/key/
index_entry.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::collections::Bound;
5
6use reifydb_codec::key::{
7	deserializer::KeyDeserializer,
8	encoded::{EncodedKey, EncodedKeyRange},
9	serializer::KeySerializer,
10};
11
12use super::{EncodableKey, EncodableKeyRange, KeyKind};
13use crate::{
14	interface::catalog::{id::IndexId, shape::ShapeId},
15	key::catalog::{KeyDeserializerCatalogExt, KeySerializerCatalogExt},
16	value::index::{encoded::EncodedIndexKey, range::EncodedIndexKeyRange},
17};
18
19#[derive(Debug, Clone, PartialEq)]
20pub struct IndexEntryKey {
21	pub shape: ShapeId,
22	pub index: IndexId,
23	pub key: EncodedIndexKey,
24}
25
26impl IndexEntryKey {
27	pub fn new(shape: impl Into<ShapeId>, index: IndexId, key: EncodedIndexKey) -> Self {
28		Self {
29			shape: shape.into(),
30			index,
31			key,
32		}
33	}
34
35	pub fn encoded(shape: impl Into<ShapeId>, index: IndexId, key: EncodedIndexKey) -> EncodedKey {
36		Self::new(shape, index, key).encode()
37	}
38}
39
40#[derive(Debug, Clone, PartialEq)]
41pub struct IndexEntryKeyRange {
42	pub shape: ShapeId,
43	pub index: IndexId,
44}
45
46impl IndexEntryKeyRange {
47	fn decode_key(key: &EncodedKey) -> Option<Self> {
48		let mut de = KeyDeserializer::from_bytes(key.as_slice());
49
50		let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
51		if kind != Self::KIND {
52			return None;
53		}
54
55		let shape = de.read_shape_id().ok()?;
56		let index = de.read_index_id().ok()?;
57
58		Some(IndexEntryKeyRange {
59			shape,
60			index,
61		})
62	}
63}
64
65impl EncodableKeyRange for IndexEntryKeyRange {
66	const KIND: KeyKind = KeyKind::IndexEntry;
67
68	fn start(&self) -> Option<EncodedKey> {
69		let mut serializer = KeySerializer::with_capacity(19);
70		serializer.extend_u8(Self::KIND as u8).extend_shape_id(self.shape).extend_index_id(self.index);
71		Some(serializer.to_encoded_key())
72	}
73
74	fn end(&self) -> Option<EncodedKey> {
75		let mut serializer = KeySerializer::with_capacity(19);
76		serializer.extend_u8(Self::KIND as u8).extend_shape_id(self.shape).extend_index_id(self.index.prev());
77		Some(serializer.to_encoded_key())
78	}
79
80	fn decode(range: &EncodedKeyRange) -> (Option<Self>, Option<Self>)
81	where
82		Self: Sized,
83	{
84		let start_key = match &range.start {
85			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
86			Bound::Unbounded => None,
87		};
88
89		let end_key = match &range.end {
90			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
91			Bound::Unbounded => None,
92		};
93
94		(start_key, end_key)
95	}
96}
97
98impl EncodableKey for IndexEntryKey {
99	const KIND: KeyKind = KeyKind::IndexEntry;
100
101	fn encode(&self) -> EncodedKey {
102		let mut serializer = KeySerializer::with_capacity(20 + self.key.len());
103		serializer
104			.extend_u8(Self::KIND as u8)
105			.extend_shape_id(self.shape)
106			.extend_index_id(self.index)
107			.extend_raw(self.key.as_slice());
108		serializer.to_encoded_key()
109	}
110
111	fn decode(key: &EncodedKey) -> Option<Self> {
112		let mut de = KeyDeserializer::from_bytes(key.as_slice());
113
114		let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
115		if kind != Self::KIND {
116			return None;
117		}
118
119		let shape = de.read_shape_id().ok()?;
120		let index = de.read_index_id().ok()?;
121
122		let remaining = de.remaining();
123		if remaining > 0 {
124			let remaining_bytes = de.read_raw(remaining).ok()?;
125			let index_key = EncodedIndexKey::new(remaining_bytes.to_vec());
126			Some(Self {
127				shape,
128				index,
129				key: index_key,
130			})
131		} else {
132			None
133		}
134	}
135}
136
137impl IndexEntryKey {
138	pub fn index_range(shape: impl Into<ShapeId>, index: IndexId) -> EncodedKeyRange {
139		let range = IndexEntryKeyRange {
140			shape: shape.into(),
141			index,
142		};
143		EncodedKeyRange::new(Bound::Included(range.start().unwrap()), Bound::Excluded(range.end().unwrap()))
144	}
145
146	pub fn shape_range(shape: impl Into<ShapeId>) -> EncodedKeyRange {
147		let shape = shape.into();
148		let mut start_serializer = KeySerializer::with_capacity(10);
149		start_serializer.extend_u8(KeyKind::IndexEntry as u8).extend_shape_id(shape);
150
151		let next_primitive = shape.next();
152		let mut end_serializer = KeySerializer::with_capacity(10);
153		end_serializer.extend_u8(KeyKind::IndexEntry as u8).extend_shape_id(next_primitive);
154
155		EncodedKeyRange {
156			start: Bound::Included(start_serializer.to_encoded_key()),
157			end: Bound::Excluded(end_serializer.to_encoded_key()),
158		}
159	}
160
161	pub fn key_prefix_range(shape: impl Into<ShapeId>, index: IndexId, key_prefix: &[u8]) -> EncodedKeyRange {
162		let shape = shape.into();
163		let mut serializer = KeySerializer::with_capacity(20 + key_prefix.len());
164		serializer
165			.extend_u8(KeyKind::IndexEntry as u8)
166			.extend_shape_id(shape)
167			.extend_index_id(index)
168			.extend_raw(key_prefix);
169		let start = serializer.to_encoded_key();
170
171		let mut end = start.as_slice().to_vec();
172		end.push(0xFF);
173
174		EncodedKeyRange {
175			start: Bound::Included(start),
176			end: Bound::Excluded(EncodedKey::new(end)),
177		}
178	}
179
180	pub fn key_range(
181		shape: impl Into<ShapeId>,
182		index: IndexId,
183		index_range: EncodedIndexKeyRange,
184	) -> EncodedKeyRange {
185		let shape = shape.into();
186
187		let mut prefix_serializer = KeySerializer::with_capacity(19);
188		prefix_serializer.extend_u8(KeyKind::IndexEntry as u8).extend_shape_id(shape).extend_index_id(index);
189		let prefix = prefix_serializer.to_encoded_key().to_vec();
190
191		let start = match index_range.start {
192			Bound::Included(key) => {
193				let mut bytes = prefix.clone();
194				bytes.extend_from_slice(key.as_slice());
195				Bound::Included(EncodedKey::new(bytes))
196			}
197			Bound::Excluded(key) => {
198				let mut bytes = prefix.clone();
199				bytes.extend_from_slice(key.as_slice());
200				Bound::Excluded(EncodedKey::new(bytes))
201			}
202			Bound::Unbounded => Bound::Included(EncodedKey::new(prefix.clone())),
203		};
204
205		let end = match index_range.end {
206			Bound::Included(key) => {
207				let mut bytes = prefix.clone();
208				bytes.extend_from_slice(key.as_slice());
209				Bound::Included(EncodedKey::new(bytes))
210			}
211			Bound::Excluded(key) => {
212				let mut bytes = prefix.clone();
213				bytes.extend_from_slice(key.as_slice());
214				Bound::Excluded(EncodedKey::new(bytes))
215			}
216			Bound::Unbounded => {
217				let mut serializer = KeySerializer::with_capacity(19);
218				serializer
219					.extend_u8(KeyKind::IndexEntry as u8)
220					.extend_shape_id(shape)
221					.extend_index_id(index.prev());
222				Bound::Excluded(serializer.to_encoded_key())
223			}
224		};
225
226		EncodedKeyRange {
227			start,
228			end,
229		}
230	}
231}
232
233#[cfg(test)]
234pub mod tests {
235	use reifydb_value::value::value_type::ValueType;
236
237	use super::*;
238	use crate::{sort::SortDirection, value::index::shape::IndexShape};
239
240	#[test]
241	fn test_encode_decode() {
242		let layout = IndexShape::new(
243			&[ValueType::Uint8, ValueType::Uint8],
244			&[SortDirection::Asc, SortDirection::Asc],
245		)
246		.unwrap();
247
248		let mut index_key = layout.allocate_key();
249		layout.set_u64(&mut index_key, 0, 100u64);
250		layout.set_row_number(&mut index_key, 1, 1u64);
251
252		let entry = IndexEntryKey {
253			shape: ShapeId::table(42),
254			index: IndexId::primary(7),
255			key: index_key.clone(),
256		};
257
258		let encoded = entry.encode();
259		let decoded = IndexEntryKey::decode(&encoded).unwrap();
260
261		assert_eq!(decoded.shape, ShapeId::table(42));
262		assert_eq!(decoded.index, IndexId::primary(7));
263		assert_eq!(decoded.key.as_slice(), index_key.as_slice());
264	}
265
266	#[test]
267	fn test_ordering() {
268		let layout = IndexShape::new(&[ValueType::Uint8], &[SortDirection::Asc]).unwrap();
269
270		let mut key1 = layout.allocate_key();
271		layout.set_u64(&mut key1, 0, 100u64);
272
273		let mut key2 = layout.allocate_key();
274		layout.set_u64(&mut key2, 0, 200u64);
275
276		let entry1 = IndexEntryKey {
277			shape: ShapeId::table(1),
278			index: IndexId::primary(1),
279			key: key1,
280		};
281
282		let entry2 = IndexEntryKey {
283			shape: ShapeId::table(1),
284			index: IndexId::primary(1),
285			key: key2,
286		};
287
288		let encoded1 = entry1.encode();
289		let encoded2 = entry2.encode();
290
291		assert!(encoded1.as_slice() < encoded2.as_slice());
292	}
293
294	#[test]
295	fn test_index_range() {
296		let range = IndexEntryKey::index_range(ShapeId::table(10), IndexId::primary(5));
297
298		let layout = IndexShape::new(&[ValueType::Uint8], &[SortDirection::Asc]).unwrap();
299
300		let mut key = layout.allocate_key();
301		layout.set_u64(&mut key, 0, 50u64);
302
303		let entry = IndexEntryKey {
304			shape: ShapeId::table(10),
305			index: IndexId::primary(5),
306			key,
307		};
308
309		let encoded = entry.encode();
310
311		if let (Bound::Included(start), Bound::Excluded(end)) = (&range.start, &range.end) {
312			assert!(encoded.as_slice() >= start.as_slice());
313			assert!(encoded.as_slice() < end.as_slice());
314		} else {
315			panic!("Expected Included/Excluded bounds");
316		}
317
318		let entry2 = IndexEntryKey {
319			shape: ShapeId::table(10),
320			index: IndexId::primary(6),
321			key: layout.allocate_key(),
322		};
323
324		let encoded2 = entry2.encode();
325
326		if let (Bound::Included(start), Bound::Excluded(end)) = (&range.start, &range.end) {
327			assert!(encoded2.as_slice() < start.as_slice() || encoded2.as_slice() >= end.as_slice());
328		}
329	}
330
331	#[test]
332	fn test_key_prefix_range() {
333		let layout = IndexShape::new(
334			&[ValueType::Uint8, ValueType::Uint8],
335			&[SortDirection::Asc, SortDirection::Asc],
336		)
337		.unwrap();
338
339		let mut key = layout.allocate_key();
340		layout.set_u64(&mut key, 0, 100u64);
341		layout.set_row_number(&mut key, 1, 0u64);
342
343		let prefix = &key.as_slice()[..layout.fields[1].offset];
344		let range = IndexEntryKey::key_prefix_range(ShapeId::table(1), IndexId::primary(1), prefix);
345
346		layout.set_row_number(&mut key, 1, 999u64);
347		let entry = IndexEntryKey {
348			shape: ShapeId::table(1),
349			index: IndexId::primary(1),
350			key: key.clone(),
351		};
352
353		let encoded = entry.encode();
354
355		if let (Bound::Included(start), Bound::Excluded(end)) = (&range.start, &range.end) {
356			assert!(encoded.as_slice() >= start.as_slice());
357			assert!(encoded.as_slice() < end.as_slice());
358		}
359
360		let mut key2 = layout.allocate_key();
361		layout.set_u64(&mut key2, 0, 200u64);
362		layout.set_row_number(&mut key2, 1, 1u64);
363
364		let entry2 = IndexEntryKey {
365			shape: ShapeId::table(1),
366			index: IndexId::primary(1),
367			key: key2,
368		};
369
370		let encoded2 = entry2.encode();
371
372		if let Bound::Excluded(end) = &range.end {
373			assert!(encoded2.as_slice() >= end.as_slice());
374		}
375	}
376}