0
# Indexing
1
2
Automatic index creation and management using field and method annotations for efficient data access. The indexing system enables fast queries and sorting without loading all data into memory.
3
4
## Capabilities
5
6
### KVIndex Annotation
7
8
Marks fields or methods for indexing in the store, enabling efficient queries and sorted iteration.
9
10
```java { .api }
11
/**
12
* Tags a field to be indexed when storing an object.
13
*
14
* Types are required to have a natural index that uniquely identifies instances in the store.
15
* The default value of the annotation identifies the natural index for the type.
16
*
17
* Indexes allow for more efficient sorting of data read from the store. By annotating a field or
18
* "getter" method with this annotation, an index will be created that will provide sorting based on
19
* the string value of that field.
20
*
21
* Note that creating indices means more space will be needed, and maintenance operations like
22
* updating or deleting a value will become more expensive.
23
*
24
* Indices are restricted to String, integral types (byte, short, int, long, boolean), and arrays
25
* of those values.
26
*/
27
@Retention(RetentionPolicy.RUNTIME)
28
@Target({ElementType.FIELD, ElementType.METHOD})
29
public @interface KVIndex {
30
/**
31
* The name of the index to be created for the annotated entity. Must be unique within
32
* the class. Index names are not allowed to start with an underscore (that's reserved for
33
* internal use). The default value is the natural index name (which is always a copy index
34
* regardless of the annotation's values).
35
*/
36
String value() default NATURAL_INDEX_NAME;
37
38
/**
39
* The name of the parent index of this index. By default there is no parent index, so the
40
* generated data can be retrieved without having to provide a parent value.
41
*
42
* If a parent index is defined, iterating over the data using the index will require providing
43
* a single value for the parent index. This serves as a rudimentary way to provide relationships
44
* between entities in the store.
45
*/
46
String parent() default "";
47
48
/**
49
* Whether to copy the instance's data to the index, instead of just storing a pointer to the
50
* data. The default behavior is to just store a reference; that saves disk space but is slower
51
* to read, since there's a level of indirection.
52
*/
53
boolean copy() default false;
54
}
55
56
/**
57
* Constant for the natural index name used internally by the KVStore system.
58
*/
59
String NATURAL_INDEX_NAME = "__main__";
60
```
61
62
### Basic Field Indexing
63
64
Annotate fields to create indices for efficient querying and sorting.
65
66
**Usage Examples:**
67
68
```java
69
public class Person {
70
@KVIndex // Natural index (required) - uniquely identifies the object
71
public String id;
72
73
@KVIndex("name") // Create an index on the name field
74
public String name;
75
76
@KVIndex("age") // Create an index on the age field
77
public int age;
78
79
@KVIndex("active") // Create an index on the active field
80
public boolean active;
81
82
public Person(String id, String name, int age, boolean active) {
83
this.id = id;
84
this.name = name;
85
this.age = age;
86
this.active = active;
87
}
88
}
89
90
// Usage with the indexed class
91
KVStore store = new InMemoryStore();
92
93
// Write data
94
store.write(new Person("p1", "Alice", 30, true));
95
store.write(new Person("p2", "Bob", 25, false));
96
store.write(new Person("p3", "Charlie", 35, true));
97
98
// Query by name index
99
for (Person p : store.view(Person.class).index("name").first("Alice").last("Bob")) {
100
System.out.println(p.name);
101
}
102
103
// Query by age index
104
for (Person p : store.view(Person.class).index("age").first(25).last(30)) {
105
System.out.println(p.name + " is " + p.age);
106
}
107
```
108
109
### Method-Based Indexing
110
111
Annotate getter methods to create indices based on computed values.
112
113
**Usage Examples:**
114
115
```java
116
public class Employee {
117
@KVIndex
118
public String employeeId;
119
120
public String firstName;
121
public String lastName;
122
public Date birthDate;
123
124
@KVIndex("fullName")
125
public String getFullName() {
126
return firstName + " " + lastName;
127
}
128
129
@KVIndex("birthYear")
130
public int getBirthYear() {
131
return birthDate.getYear() + 1900; // Date.getYear() returns year - 1900
132
}
133
134
@KVIndex("displayName")
135
public String getDisplayName() {
136
return lastName + ", " + firstName;
137
}
138
}
139
140
// Usage
141
Employee emp = new Employee();
142
emp.employeeId = "emp123";
143
emp.firstName = "John";
144
emp.lastName = "Doe";
145
emp.birthDate = new Date(85, 5, 15); // June 15, 1985
146
147
store.write(emp);
148
149
// Query by computed fullName index
150
for (Employee e : store.view(Employee.class).index("fullName").first("John Doe")) {
151
System.out.println("Found: " + e.getDisplayName());
152
}
153
154
// Query by computed birthYear index
155
for (Employee e : store.view(Employee.class).index("birthYear").first(1985).last(1990)) {
156
System.out.println(e.getFullName() + " born in " + e.getBirthYear());
157
}
158
```
159
160
### Hierarchical Indices (Parent-Child Relationships)
161
162
Create parent-child index relationships for hierarchical data structures.
163
164
**Usage Examples:**
165
166
```java
167
public class Task {
168
@KVIndex
169
public String taskId;
170
171
@KVIndex("projectId") // Parent index
172
public String projectId;
173
174
@KVIndex(value = "priority", parent = "projectId") // Child index with parent
175
public String priority;
176
177
@KVIndex(value = "status", parent = "projectId") // Another child index
178
public String status;
179
180
public String title;
181
182
public Task(String taskId, String projectId, String priority, String status, String title) {
183
this.taskId = taskId;
184
this.projectId = projectId;
185
this.priority = priority;
186
this.status = status;
187
this.title = title;
188
}
189
}
190
191
// Usage with hierarchical indices
192
KVStore store = new LevelDB(new File("./tasks"));
193
194
// Add tasks for different projects
195
store.write(new Task("t1", "proj1", "high", "open", "Implement feature A"));
196
store.write(new Task("t2", "proj1", "medium", "open", "Write tests"));
197
store.write(new Task("t3", "proj1", "high", "closed", "Fix bug #123"));
198
store.write(new Task("t4", "proj2", "low", "open", "Update documentation"));
199
200
// Query high priority tasks for project 1
201
KVStoreView<Task> highPriorityProj1 = store.view(Task.class)
202
.index("priority")
203
.parent("proj1") // Filter by parent project ID
204
.first("high")
205
.last("high");
206
207
// Query all tasks for project 1 by status
208
KVStoreView<Task> proj1ByStatus = store.view(Task.class)
209
.index("status")
210
.parent("proj1");
211
212
// Query open tasks for project 2
213
KVStoreView<Task> openProj2 = store.view(Task.class)
214
.index("status")
215
.parent("proj2")
216
.first("open")
217
.last("open");
218
```
219
220
### Copy vs Reference Indexing
221
222
Control whether index entries store full object data or just references.
223
224
**Usage Examples:**
225
226
```java
227
public class Product {
228
@KVIndex
229
public String productId;
230
231
@KVIndex(value = "category", copy = false) // Reference index (default)
232
public String category;
233
234
@KVIndex(value = "price", copy = true) // Copy index - stores full object data
235
public double price;
236
237
@KVIndex(value = "inStock", copy = true) // Copy index for fast access
238
public boolean inStock;
239
240
public String name;
241
public String description;
242
243
public Product(String id, String category, double price, boolean inStock, String name) {
244
this.productId = id;
245
this.category = category;
246
this.price = price;
247
this.inStock = inStock;
248
this.name = name;
249
}
250
}
251
252
// Copy indices provide faster iteration since no additional lookups are needed
253
// Reference indices save disk space but require additional lookups during iteration
254
255
// Fast iteration over products by price (copy index)
256
for (Product p : store.view(Product.class).index("price").first(10.0).last(50.0)) {
257
// No additional lookup needed - object data stored in index
258
System.out.println(p.name + ": $" + p.price);
259
}
260
261
// Slower iteration over products by category (reference index)
262
for (Product p : store.view(Product.class).index("category").first("electronics")) {
263
// Additional lookup needed to get full object data
264
System.out.println(p.name + " in " + p.category);
265
}
266
```
267
268
### Array Indexing
269
270
Index array fields for multi-value queries.
271
272
**Usage Examples:**
273
274
```java
275
public class Document {
276
@KVIndex
277
public String documentId;
278
279
@KVIndex("tags")
280
public String[] tags;
281
282
@KVIndex("scores")
283
public int[] scores;
284
285
public String title;
286
287
public Document(String id, String title, String[] tags, int[] scores) {
288
this.documentId = id;
289
this.title = title;
290
this.tags = tags;
291
this.scores = scores;
292
}
293
}
294
295
// Usage with array indices
296
Document doc1 = new Document("doc1", "Java Guide",
297
new String[]{"java", "programming", "tutorial"},
298
new int[]{95, 87, 92});
299
Document doc2 = new Document("doc2", "Python Basics",
300
new String[]{"python", "programming", "beginner"},
301
new int[]{88, 91, 85});
302
303
store.write(doc1);
304
store.write(doc2);
305
306
// Note: Array indexing creates entries for each array element
307
// Query documents with "programming" tag
308
for (Document d : store.view(Document.class).index("tags").first("programming").last("programming")) {
309
System.out.println("Found: " + d.title);
310
}
311
```
312
313
### Type Introspection and Metadata
314
315
Access index metadata and configuration for stored types.
316
317
```java { .api }
318
/**
319
* Wrapper around types managed in a KVStore, providing easy access to their indexed fields.
320
*/
321
public class KVTypeInfo {
322
public KVTypeInfo(Class<?> type);
323
public Class<?> type();
324
public Object getIndexValue(String indexName, Object instance) throws Exception;
325
public Stream<KVIndex> indices();
326
}
327
```
328
329
**Usage Examples:**
330
331
```java
332
// Introspect a class to see its indices
333
KVTypeInfo typeInfo = new KVTypeInfo(Person.class);
334
335
System.out.println("Type: " + typeInfo.type().getSimpleName());
336
System.out.println("Indices:");
337
338
typeInfo.indices().forEach(index -> {
339
System.out.println(" - " + index.value() +
340
(index.parent().isEmpty() ? "" : " (parent: " + index.parent() + ")") +
341
(index.copy() ? " [copy]" : " [reference]"));
342
});
343
344
// Get index value from an instance
345
Person person = new Person("p1", "Alice", 30, true);
346
String nameValue = (String) typeInfo.getIndexValue("name", person);
347
Integer ageValue = (Integer) typeInfo.getIndexValue("age", person);
348
```
349
350
### Accessor Interface
351
352
Internal interface for accessing field and method values used by the type introspection system.
353
354
```java { .api }
355
/**
356
* Abstracts the difference between invoking a Field and a Method.
357
*/
358
interface Accessor {
359
/**
360
* Get the value from the specified instance.
361
* @param instance - The object instance to access
362
* @return The field or method return value
363
* @throws ReflectiveOperationException If access fails
364
*/
365
Object get(Object instance) throws ReflectiveOperationException;
366
367
/**
368
* Get the type of the field or method return value.
369
* @return The field type or method return type
370
*/
371
Class<?> getType();
372
}
373
```