M3: the update operators, and 's modifiers #8
@@ -610,40 +610,67 @@ fn apply_operator(
|
||||
value: bson.Value,
|
||||
opts: Options,
|
||||
) UpdateError!void {
|
||||
const diag = opts.diag;
|
||||
if (std.mem.eql(u8, op, "$set")) {
|
||||
// Every operator's argument is a document of path/operand pairs, so the
|
||||
// shape is checked once here rather than at the top of each.
|
||||
const ops = doc_pairs(value) orelse return error.InvalidUpdate;
|
||||
if (std.mem.eql(u8, op, "$set")) return op_set(arena, pairs, ops, opts);
|
||||
if (std.mem.eql(u8, op, "$unset")) return op_unset(arena, pairs, ops, opts);
|
||||
if (std.mem.eql(u8, op, "$inc")) return op_inc(arena, pairs, ops, opts);
|
||||
if (std.mem.eql(u8, op, "$push")) return op_push(arena, pairs, ops, opts);
|
||||
if (std.mem.eql(u8, op, "$pull")) return op_pull(arena, pairs, ops, opts);
|
||||
if (std.mem.eql(u8, op, "$rename")) return op_rename(arena, pairs, ops, opts);
|
||||
return error.InvalidUpdate;
|
||||
}
|
||||
|
||||
fn op_set(
|
||||
arena: std.mem.Allocator,
|
||||
pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
ops: []const bson.Pair,
|
||||
opts: Options,
|
||||
) UpdateError!void {
|
||||
for (ops) |p| {
|
||||
if (std.mem.eql(u8, p.key, "_id")) return error.ImmutableId;
|
||||
for (try resolve(arena, pairs.items, p.key, opts)) |segs| {
|
||||
try set_path(arena, pairs, segs, try bson.copy_value(arena, p.value), p.key, diag);
|
||||
try set_path(arena, pairs, segs, try bson.copy_value(arena, p.value), p.key, opts.diag);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (std.mem.eql(u8, op, "$unset")) {
|
||||
const ops = doc_pairs(value) orelse return error.InvalidUpdate;
|
||||
|
||||
fn op_unset(
|
||||
arena: std.mem.Allocator,
|
||||
pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
ops: []const bson.Pair,
|
||||
opts: Options,
|
||||
) UpdateError!void {
|
||||
for (ops) |p| {
|
||||
for (try resolve(arena, pairs.items, p.key, opts)) |segs| {
|
||||
unset_path(arena, pairs, segs);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (std.mem.eql(u8, op, "$inc")) {
|
||||
const ops = doc_pairs(value) orelse return error.InvalidUpdate;
|
||||
|
||||
fn op_inc(
|
||||
arena: std.mem.Allocator,
|
||||
pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
ops: []const bson.Pair,
|
||||
opts: Options,
|
||||
) UpdateError!void {
|
||||
for (ops) |p| {
|
||||
for (try resolve(arena, pairs.items, p.key, opts)) |segs| {
|
||||
const current = get_value(pairs.items, segs) orelse bson.Value{ .int32 = 0 };
|
||||
if (!current.is_number() or !p.value.is_number()) return error.InvalidUpdate;
|
||||
const sum = try numeric_add(current, p.value);
|
||||
try set_path(arena, pairs, segs, sum, p.key, diag);
|
||||
try set_path(arena, pairs, segs, sum, p.key, opts.diag);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (std.mem.eql(u8, op, "$push")) {
|
||||
const ops = doc_pairs(value) orelse return error.InvalidUpdate;
|
||||
|
||||
fn op_push(
|
||||
arena: std.mem.Allocator,
|
||||
pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
ops: []const bson.Pair,
|
||||
opts: Options,
|
||||
) UpdateError!void {
|
||||
for (ops) |p| {
|
||||
for (try resolve(arena, pairs.items, p.key, opts)) |segs| {
|
||||
const current_opt = get_value(pairs.items, segs);
|
||||
@@ -663,18 +690,22 @@ fn apply_operator(
|
||||
else => return error.InvalidUpdate,
|
||||
};
|
||||
for (arr) |item| try items.append(arena, try bson.copy_value(arena, item));
|
||||
try set_path(arena, pairs, segs, .{ .array = try items.toOwnedSlice(arena) }, p.key, diag);
|
||||
try set_path(arena, pairs, segs, .{ .array = try items.toOwnedSlice(arena) }, p.key, opts.diag);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
try items.append(arena, try bson.copy_value(arena, p.value));
|
||||
try set_path(arena, pairs, segs, .{ .array = try items.toOwnedSlice(arena) }, p.key, diag);
|
||||
try set_path(arena, pairs, segs, .{ .array = try items.toOwnedSlice(arena) }, p.key, opts.diag);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (std.mem.eql(u8, op, "$pull")) {
|
||||
const ops = doc_pairs(value) orelse return error.InvalidUpdate;
|
||||
|
||||
fn op_pull(
|
||||
arena: std.mem.Allocator,
|
||||
pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
ops: []const bson.Pair,
|
||||
opts: Options,
|
||||
) UpdateError!void {
|
||||
for (ops) |p| {
|
||||
for (try resolve(arena, pairs.items, p.key, opts)) |segs| {
|
||||
const current = get_value(pairs.items, segs) orelse continue;
|
||||
@@ -689,15 +720,19 @@ fn apply_operator(
|
||||
try items.append(arena, elem);
|
||||
}
|
||||
}
|
||||
try set_path(arena, pairs, segs, .{ .array = try items.toOwnedSlice(arena) }, p.key, diag);
|
||||
try set_path(arena, pairs, segs, .{ .array = try items.toOwnedSlice(arena) }, p.key, opts.diag);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// `$rename` alone keeps the plain split: `validate` has already refused a
|
||||
// positional path on either end of it, which is what mongod does too.
|
||||
if (std.mem.eql(u8, op, "$rename")) {
|
||||
const ops = doc_pairs(value) orelse return error.InvalidUpdate;
|
||||
|
||||
/// `$rename` alone keeps the plain split: `validate` has already refused a
|
||||
/// positional path on either end of it, which is what mongod does too.
|
||||
fn op_rename(
|
||||
arena: std.mem.Allocator,
|
||||
pairs: *std.ArrayListUnmanaged(bson.Pair),
|
||||
ops: []const bson.Pair,
|
||||
opts: Options,
|
||||
) UpdateError!void {
|
||||
for (ops) |p| {
|
||||
if (p.value != .string) return error.InvalidUpdate;
|
||||
if (std.mem.eql(u8, p.key, "_id") or std.mem.eql(u8, p.value.string, "_id")) return error.ImmutableId;
|
||||
@@ -707,11 +742,8 @@ fn apply_operator(
|
||||
unset_path(arena, pairs, old_segs[0..old_n]);
|
||||
var new_segs: [max_path_segments][]const u8 = undefined;
|
||||
const new_n = split_path(p.value.string, &new_segs) orelse return error.InvalidUpdate;
|
||||
try set_path(arena, pairs, new_segs[0..new_n], v, p.value.string, diag);
|
||||
try set_path(arena, pairs, new_segs[0..new_n], v, p.value.string, opts.diag);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return error.InvalidUpdate;
|
||||
}
|
||||
|
||||
fn doc_pairs(v: bson.Value) ?[]const bson.Pair {
|
||||
|
||||
Reference in New Issue
Block a user