Skip to main content

reifydb_core/key/
index_entry.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use std::collections::Bound;
5
6use reifydb_type::util::cowvec::CowVec;
7
8use super::{EncodableKey, EncodableKeyRange, KeyKind};
9use crate::{
10	encoded::key::{EncodedKey, EncodedKeyRange},
11	interface::catalog::{id::IndexId, primitive::PrimitiveId},
12	util::encoding::keycode::{deserializer::KeyDeserializer, serializer::KeySerializer},
13	value::index::{encoded::EncodedIndexKey, range::EncodedIndexKeyRange},
14};
15
16const VERSION: u8 = 1;
17
18/// Key for storing actual index entries with the encoded index key data
19#[derive(Debug, Clone, PartialEq)]
20pub struct IndexEntryKey {
21	pub primitive: PrimitiveId,
22	pub index: IndexId,
23	pub key: EncodedIndexKey,
24}
25
26impl IndexEntryKey {
27	pub fn new(primitive: impl Into<PrimitiveId>, index: IndexId, key: EncodedIndexKey) -> Self {
28		Self {
29			primitive: primitive.into(),
30			index,
31			key,
32		}
33	}
34
35	pub fn encoded(primitive: impl Into<PrimitiveId>, index: IndexId, key: EncodedIndexKey) -> EncodedKey {
36		Self::new(primitive, index, key).encode()
37	}
38}
39
40#[derive(Debug, Clone, PartialEq)]
41pub struct IndexEntryKeyRange {
42	pub primitive: PrimitiveId,
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 version = de.read_u8().ok()?;
51		if version != VERSION {
52			return None;
53		}
54
55		let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
56		if kind != Self::KIND {
57			return None;
58		}
59
60		let primitive = de.read_primitive_id().ok()?;
61		let index = de.read_index_id().ok()?;
62
63		Some(IndexEntryKeyRange {
64			primitive,
65			index,
66		})
67	}
68}
69
70impl EncodableKeyRange for IndexEntryKeyRange {
71	const KIND: KeyKind = KeyKind::IndexEntry;
72
73	fn start(&self) -> Option<EncodedKey> {
74		let mut serializer = KeySerializer::with_capacity(20); // 1 + 1 + 9 + 9
75		serializer
76			.extend_u8(VERSION)
77			.extend_u8(Self::KIND as u8)
78			.extend_primitive_id(self.primitive)
79			.extend_index_id(self.index);
80		Some(serializer.to_encoded_key())
81	}
82
83	fn end(&self) -> Option<EncodedKey> {
84		let mut serializer = KeySerializer::with_capacity(20);
85		serializer
86			.extend_u8(VERSION)
87			.extend_u8(Self::KIND as u8)
88			.extend_primitive_id(self.primitive)
89			.extend_index_id(self.index.prev());
90		Some(serializer.to_encoded_key())
91	}
92
93	fn decode(range: &EncodedKeyRange) -> (Option<Self>, Option<Self>)
94	where
95		Self: Sized,
96	{
97		let start_key = match &range.start {
98			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
99			Bound::Unbounded => None,
100		};
101
102		let end_key = match &range.end {
103			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
104			Bound::Unbounded => None,
105		};
106
107		(start_key, end_key)
108	}
109}
110
111impl EncodableKey for IndexEntryKey {
112	const KIND: KeyKind = KeyKind::IndexEntry;
113
114	fn encode(&self) -> EncodedKey {
115		let mut serializer = KeySerializer::with_capacity(20 + self.key.len());
116		serializer
117			.extend_u8(VERSION)
118			.extend_u8(Self::KIND as u8)
119			.extend_primitive_id(self.primitive)
120			.extend_index_id(self.index)
121			// Append the raw index key bytes
122			.extend_raw(self.key.as_slice());
123		serializer.to_encoded_key()
124	}
125
126	fn decode(key: &EncodedKey) -> Option<Self> {
127		let mut de = KeyDeserializer::from_bytes(key.as_slice());
128
129		let version = de.read_u8().ok()?;
130		if version != VERSION {
131			return None;
132		}
133
134		let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
135		if kind != Self::KIND {
136			return None;
137		}
138
139		let primitive = de.read_primitive_id().ok()?;
140		let index = de.read_index_id().ok()?;
141
142		// The remaining bytes are the index key
143		let remaining = de.remaining();
144		if remaining > 0 {
145			let remaining_bytes = de.read_raw(remaining).ok()?;
146			let index_key = EncodedIndexKey(CowVec::new(remaining_bytes.to_vec()));
147			Some(Self {
148				primitive,
149				index,
150				key: index_key,
151			})
152		} else {
153			None
154		}
155	}
156}
157
158impl IndexEntryKey {
159	/// Create a range for scanning all entries of a specific index
160	pub fn index_range(primitive: impl Into<PrimitiveId>, index: IndexId) -> EncodedKeyRange {
161		let range = IndexEntryKeyRange {
162			primitive: primitive.into(),
163			index,
164		};
165		EncodedKeyRange::new(Bound::Included(range.start().unwrap()), Bound::Excluded(range.end().unwrap()))
166	}
167
168	/// Create a range for scanning all entries of a primitive (all indexes)
169	pub fn primitive_range(primitive: impl Into<PrimitiveId>) -> EncodedKeyRange {
170		let primitive = primitive.into();
171		let mut start_serializer = KeySerializer::with_capacity(11);
172		start_serializer.extend_u8(VERSION).extend_u8(KeyKind::IndexEntry as u8).extend_primitive_id(primitive);
173
174		let next_primitive = primitive.next();
175		let mut end_serializer = KeySerializer::with_capacity(11);
176		end_serializer
177			.extend_u8(VERSION)
178			.extend_u8(KeyKind::IndexEntry as u8)
179			.extend_primitive_id(next_primitive);
180
181		EncodedKeyRange {
182			start: Bound::Included(start_serializer.to_encoded_key()),
183			end: Bound::Excluded(end_serializer.to_encoded_key()),
184		}
185	}
186
187	/// Create a range for scanning entries within an index with a specific
188	/// key prefix
189	pub fn key_prefix_range(
190		primitive: impl Into<PrimitiveId>,
191		index: IndexId,
192		key_prefix: &[u8],
193	) -> EncodedKeyRange {
194		let primitive = primitive.into();
195		let mut serializer = KeySerializer::with_capacity(20 + key_prefix.len());
196		serializer
197			.extend_u8(VERSION)
198			.extend_u8(KeyKind::IndexEntry as u8)
199			.extend_primitive_id(primitive)
200			.extend_index_id(index)
201			.extend_raw(key_prefix);
202		let start = serializer.to_encoded_key();
203
204		// For the end key, append 0xFF to get all keys with this prefix
205		let mut end = start.as_slice().to_vec();
206		end.push(0xFF);
207
208		EncodedKeyRange {
209			start: Bound::Included(start),
210			end: Bound::Excluded(EncodedKey::new(end)),
211		}
212	}
213
214	/// Create a range for entries from an EncodedIndexKeyRange
215	/// This method leverages the EncodedIndexKeyRange type for cleaner
216	/// range handling.
217	pub fn key_range(
218		primitive: impl Into<PrimitiveId>,
219		index: IndexId,
220		index_range: EncodedIndexKeyRange,
221	) -> EncodedKeyRange {
222		let primitive = primitive.into();
223		// Build the prefix for this primitive and index
224		let mut prefix_serializer = KeySerializer::with_capacity(20);
225		prefix_serializer
226			.extend_u8(VERSION)
227			.extend_u8(KeyKind::IndexEntry as u8)
228			.extend_primitive_id(primitive)
229			.extend_index_id(index);
230		let prefix = prefix_serializer.to_encoded_key().to_vec();
231
232		// Convert bounds to include the prefix
233		let start = match index_range.start {
234			Bound::Included(key) => {
235				let mut bytes = prefix.clone();
236				bytes.extend_from_slice(key.as_slice());
237				Bound::Included(EncodedKey::new(bytes))
238			}
239			Bound::Excluded(key) => {
240				let mut bytes = prefix.clone();
241				bytes.extend_from_slice(key.as_slice());
242				Bound::Excluded(EncodedKey::new(bytes))
243			}
244			Bound::Unbounded => {
245				// Start from the beginning of this index
246				Bound::Included(EncodedKey::new(prefix.clone()))
247			}
248		};
249
250		let end = match index_range.end {
251			Bound::Included(key) => {
252				let mut bytes = prefix.clone();
253				bytes.extend_from_slice(key.as_slice());
254				Bound::Included(EncodedKey::new(bytes))
255			}
256			Bound::Excluded(key) => {
257				let mut bytes = prefix.clone();
258				bytes.extend_from_slice(key.as_slice());
259				Bound::Excluded(EncodedKey::new(bytes))
260			}
261			Bound::Unbounded => {
262				// End at the beginning of the next index
263				let mut serializer = KeySerializer::with_capacity(20);
264				serializer
265					.extend_u8(VERSION)
266					.extend_u8(KeyKind::IndexEntry as u8)
267					.extend_primitive_id(primitive)
268					// Use prev() for end bound in descending order
269					.extend_index_id(index.prev());
270				Bound::Excluded(serializer.to_encoded_key())
271			}
272		};
273
274		EncodedKeyRange {
275			start,
276			end,
277		}
278	}
279}
280
281#[cfg(test)]
282pub mod tests {
283	use reifydb_type::value::r#type::Type;
284
285	use super::*;
286	use crate::{sort::SortDirection, value::index::layout::EncodedIndexLayout};
287
288	#[test]
289	fn test_encode_decode() {
290		// Create a simple index key
291		let layout =
292			EncodedIndexLayout::new(&[Type::Uint8, Type::Uint8], &[SortDirection::Asc, SortDirection::Asc])
293				.unwrap();
294
295		let mut index_key = layout.allocate_key();
296		layout.set_u64(&mut index_key, 0, 100u64);
297		layout.set_row_number(&mut index_key, 1, 1u64);
298
299		let entry = IndexEntryKey {
300			primitive: PrimitiveId::table(42),
301			index: IndexId::primary(7),
302			key: index_key.clone(),
303		};
304
305		let encoded = entry.encode();
306		let decoded = IndexEntryKey::decode(&encoded).unwrap();
307
308		assert_eq!(decoded.primitive, PrimitiveId::table(42));
309		assert_eq!(decoded.index, IndexId::primary(7));
310		assert_eq!(decoded.key.as_slice(), index_key.as_slice());
311	}
312
313	#[test]
314	fn test_ordering() {
315		let layout = EncodedIndexLayout::new(&[Type::Uint8], &[SortDirection::Asc]).unwrap();
316
317		let mut key1 = layout.allocate_key();
318		layout.set_u64(&mut key1, 0, 100u64);
319
320		let mut key2 = layout.allocate_key();
321		layout.set_u64(&mut key2, 0, 200u64);
322
323		// Same primitive and index, different keys
324		let entry1 = IndexEntryKey {
325			primitive: PrimitiveId::table(1),
326			index: IndexId::primary(1),
327			key: key1,
328		};
329
330		let entry2 = IndexEntryKey {
331			primitive: PrimitiveId::table(1),
332			index: IndexId::primary(1),
333			key: key2,
334		};
335
336		let encoded1 = entry1.encode();
337		let encoded2 = entry2.encode();
338
339		// entry1 should come before entry2 because 100 < 200
340		assert!(encoded1.as_slice() < encoded2.as_slice());
341	}
342
343	#[test]
344	fn test_index_range() {
345		let range = IndexEntryKey::index_range(PrimitiveId::table(10), IndexId::primary(5));
346
347		// Create entries that should be included
348		let layout = EncodedIndexLayout::new(&[Type::Uint8], &[SortDirection::Asc]).unwrap();
349
350		let mut key = layout.allocate_key();
351		layout.set_u64(&mut key, 0, 50u64);
352
353		let entry = IndexEntryKey {
354			primitive: PrimitiveId::table(10),
355			index: IndexId::primary(5),
356			key,
357		};
358
359		let encoded = entry.encode();
360
361		// Check that the entry falls within the range
362		if let (Bound::Included(start), Bound::Excluded(end)) = (&range.start, &range.end) {
363			assert!(encoded.as_slice() >= start.as_slice());
364			assert!(encoded.as_slice() < end.as_slice());
365		} else {
366			panic!("Expected Included/Excluded bounds");
367		}
368
369		// Entry with different index should not be in range
370		// Note: Due to keycode encoding, IndexId(6) will have a smaller
371		// encoded value than IndexId(5) since keycode inverts bits
372		// (larger numbers become smaller byte sequences)
373		let entry2 = IndexEntryKey {
374			primitive: PrimitiveId::table(10),
375			index: IndexId::primary(6),
376			key: layout.allocate_key(),
377		};
378
379		let encoded2 = entry2.encode();
380		// The entry with IndexId(6) should not be within the range for
381		// IndexId(5)
382		if let (Bound::Included(start), Bound::Excluded(end)) = (&range.start, &range.end) {
383			// encoded2 should either be < start or >= end
384			assert!(encoded2.as_slice() < start.as_slice() || encoded2.as_slice() >= end.as_slice());
385		}
386	}
387
388	#[test]
389	fn test_key_prefix_range() {
390		let layout =
391			EncodedIndexLayout::new(&[Type::Uint8, Type::Uint8], &[SortDirection::Asc, SortDirection::Asc])
392				.unwrap();
393
394		let mut key = layout.allocate_key();
395		layout.set_u64(&mut key, 0, 100u64);
396		layout.set_row_number(&mut key, 1, 0u64); // Set to 0 to get the minimal key with this prefix
397
398		// Use the full encoded key up to the first field as the prefix
399		let prefix = &key.as_slice()[..layout.fields[1].offset]; // Include bitvec and first field
400		let range = IndexEntryKey::key_prefix_range(PrimitiveId::table(1), IndexId::primary(1), prefix);
401
402		// Now create a full key with the same prefix
403		layout.set_row_number(&mut key, 1, 999u64);
404		let entry = IndexEntryKey {
405			primitive: PrimitiveId::table(1),
406			index: IndexId::primary(1),
407			key: key.clone(),
408		};
409
410		let encoded = entry.encode();
411
412		// Should be within range
413		if let (Bound::Included(start), Bound::Excluded(end)) = (&range.start, &range.end) {
414			assert!(encoded.as_slice() >= start.as_slice());
415			assert!(encoded.as_slice() < end.as_slice());
416		}
417
418		// Create a key with different prefix
419		let mut key2 = layout.allocate_key();
420		layout.set_u64(&mut key2, 0, 200u64); // Different first field
421		layout.set_row_number(&mut key2, 1, 1u64);
422
423		let entry2 = IndexEntryKey {
424			primitive: PrimitiveId::table(1),
425			index: IndexId::primary(1),
426			key: key2,
427		};
428
429		let encoded2 = entry2.encode();
430
431		// Should not be in range
432		if let Bound::Excluded(end) = &range.end {
433			assert!(encoded2.as_slice() >= end.as_slice());
434		}
435	}
436}