index: secondary index core — entries, search, planner, _id fast path
Adds src/index.zig with the full secondary-index machinery: entry generation mirroring field_matches (array value + elements), BSON-order sorted entries with binary search, compound prefix and range lookups, unique/sparse options, the query planner (longest equality/$in run + optional range, $in cartesian cap, sparse/null bail), and the _id_ fast path guarded against serialization-ambiguous values (numbers, strings, symbols, codes, opaque payloads). query.collect_values is now pub so entry generation can mirror it exactly. storage.zig gains record_type_index_create/drop; lib.zig exports index.
This commit is contained in:
@@ -13,7 +13,7 @@ const max_path_segments = 16;
|
||||
/// Apply an update document (whose fields are operator documents) to `doc`.
|
||||
pub fn apply(doc: *bson.Document, update: *const bson.Document) UpdateError!void {
|
||||
const arena = doc.arena.allocator();
|
||||
var pairs = try copy_pairs_to_list(arena, doc.pairs);
|
||||
var pairs = try copy_to_list(bson.Pair, arena, doc.pairs);
|
||||
for (update.pairs) |op| {
|
||||
if (op.key.len == 0 or op.key[0] != '$') return error.InvalidUpdate;
|
||||
try apply_operator(arena, &pairs, op.key, op.value);
|
||||
@@ -48,7 +48,7 @@ fn apply_operator(arena: std.mem.Allocator, pairs: *std.ArrayListUnmanaged(bson.
|
||||
const n = split_path(p.key, &segs) orelse return error.InvalidUpdate;
|
||||
const current = get_value(pairs.items, segs[0..n]) orelse bson.Value{ .int32 = 0 };
|
||||
if (!current.is_number() or !p.value.is_number()) return error.InvalidUpdate;
|
||||
const sum = try numeric_add(arena, current, p.value);
|
||||
const sum = try numeric_add(current, p.value);
|
||||
try set_path(arena, pairs, segs[0..n], sum);
|
||||
}
|
||||
return;
|
||||
@@ -145,19 +145,15 @@ fn parse_index(seg: []const u8) ?usize {
|
||||
return std.fmt.parseInt(usize, seg, 10) catch null;
|
||||
}
|
||||
|
||||
fn copy_pairs_to_list(arena: std.mem.Allocator, pairs: []const bson.Pair) UpdateError!std.ArrayListUnmanaged(bson.Pair) {
|
||||
var out: std.ArrayListUnmanaged(bson.Pair) = .empty;
|
||||
/// Shallow-copy a slice into a growable list backed by `arena`.
|
||||
fn copy_to_list(comptime T: type, arena: std.mem.Allocator, items: []const T) UpdateError!std.ArrayListUnmanaged(T) {
|
||||
var out: std.ArrayListUnmanaged(T) = .empty;
|
||||
errdefer out.deinit(arena);
|
||||
try out.appendSlice(arena, pairs);
|
||||
try out.appendSlice(arena, items);
|
||||
return out;
|
||||
}
|
||||
|
||||
fn find_pair(pairs: []const bson.Pair, key: []const u8) ?usize {
|
||||
for (pairs, 0..) |p, i| {
|
||||
if (std.mem.eql(u8, p.key, key)) return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const find_pair = bson.get_pair_index;
|
||||
|
||||
fn get_value(pairs: []const bson.Pair, segs: []const []const u8) ?bson.Value {
|
||||
const idx = find_pair(pairs, segs[0]) orelse return null;
|
||||
@@ -193,7 +189,7 @@ fn set_path(arena: std.mem.Allocator, pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
};
|
||||
switch (pairs.items[idx].value) {
|
||||
.doc => |sub| {
|
||||
var sub_pairs = try copy_pairs_to_list(arena, sub);
|
||||
var sub_pairs = try copy_to_list(bson.Pair, arena, sub);
|
||||
defer sub_pairs.deinit(arena);
|
||||
try set_path(arena, &sub_pairs, segs[1..], value);
|
||||
pairs.items[idx].value = .{ .doc = try sub_pairs.toOwnedSlice(arena) };
|
||||
@@ -207,7 +203,7 @@ fn set_path(arena: std.mem.Allocator, pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
pairs.items[idx].value = .{ .doc = try sub_pairs.toOwnedSlice(arena) };
|
||||
return;
|
||||
};
|
||||
var items = try copy_array_to_list(arena, arr);
|
||||
var items = try copy_to_list(bson.Value, arena, arr);
|
||||
defer items.deinit(arena);
|
||||
if (index >= items.items.len) {
|
||||
try items.appendNTimes(arena, .null, index + 1 - items.items.len);
|
||||
@@ -217,7 +213,7 @@ fn set_path(arena: std.mem.Allocator, pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
} else {
|
||||
switch (items.items[index]) {
|
||||
.doc => |sub| {
|
||||
var sub_pairs = try copy_pairs_to_list(arena, sub);
|
||||
var sub_pairs = try copy_to_list(bson.Pair, arena, sub);
|
||||
defer sub_pairs.deinit(arena);
|
||||
try set_path(arena, &sub_pairs, segs[2..], value);
|
||||
items.items[index] = .{ .doc = try sub_pairs.toOwnedSlice(arena) };
|
||||
@@ -241,13 +237,6 @@ fn set_path(arena: std.mem.Allocator, pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_array_to_list(arena: std.mem.Allocator, arr: []const bson.Value) UpdateError!std.ArrayListUnmanaged(bson.Value) {
|
||||
var out: std.ArrayListUnmanaged(bson.Value) = .empty;
|
||||
errdefer out.deinit(arena);
|
||||
try out.appendSlice(arena, arr);
|
||||
return out;
|
||||
}
|
||||
|
||||
fn unset_path(arena: std.mem.Allocator, pairs: *std.ArrayListUnmanaged(bson.Pair), segs: []const []const u8) void {
|
||||
if (segs.len == 1) {
|
||||
if (find_pair(pairs.items, segs[0])) |idx| {
|
||||
@@ -258,7 +247,7 @@ fn unset_path(arena: std.mem.Allocator, pairs: *std.ArrayListUnmanaged(bson.Pair
|
||||
const idx = find_pair(pairs.items, segs[0]) orelse return;
|
||||
switch (pairs.items[idx].value) {
|
||||
.doc => |sub| {
|
||||
var sub_pairs = copy_pairs_to_list(arena, sub) catch return;
|
||||
var sub_pairs = copy_to_list(bson.Pair, arena, sub) catch return;
|
||||
unset_path(arena, &sub_pairs, segs[1..]);
|
||||
pairs.items[idx].value = .{ .doc = sub_pairs.items };
|
||||
},
|
||||
@@ -273,14 +262,7 @@ fn pull_matches(arena: std.mem.Allocator, condition: bson.Value, elem: bson.Valu
|
||||
.doc => |pairs| pairs,
|
||||
else => return false,
|
||||
};
|
||||
var all_operators = cond_pairs.len > 0;
|
||||
for (cond_pairs) |p| {
|
||||
if (p.key.len == 0 or p.key[0] != '$') {
|
||||
all_operators = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_operators) {
|
||||
if (query.all_operator_keys(cond_pairs)) {
|
||||
// Operator condition against the element's value at each
|
||||
// operator's field — treat element doc as the doc.
|
||||
var ok = true;
|
||||
@@ -297,8 +279,7 @@ fn pull_matches(arena: std.mem.Allocator, condition: bson.Value, elem: bson.Valu
|
||||
}
|
||||
}
|
||||
|
||||
fn numeric_add(arena: std.mem.Allocator, a: bson.Value, b: bson.Value) UpdateError!bson.Value {
|
||||
_ = arena;
|
||||
fn numeric_add(a: bson.Value, b: bson.Value) UpdateError!bson.Value {
|
||||
if (a == .double or b == .double) {
|
||||
const sum: f64 = @floatCast(a.as_f128() + b.as_f128());
|
||||
return .{ .double = sum };
|
||||
|
||||
Reference in New Issue
Block a user