Skip to main content

reifydb_core/key/
index_entry.rs

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