feat(completion): complete mongo arrayFilters and pull
This commit is contained in:
1 parent
472f70b218
commit
e0f2072bef
2 files changed
+33
-1
No files matched your search
@@ -43,6 +43,7 @@ export type MongoCompletionMode =
|
||||
| "cursorMethod"
|
||||
| "field"
|
||||
| "filterField"
|
||||
| "pullCondition"
|
||||
| "fieldPath"
|
||||
| "fieldRef"
|
||||
| "value"
|
||||
@@ -476,6 +477,10 @@ export function buildMongoCompletionItemsFromContext(context: MongoCompletionCon
|
||||
// Fields lead; `$and` / `$or` and the other whole-filter operators follow once `$` is typed.
|
||||
items = [...fieldItems(prefix, fields), ...specItems(TOP_LEVEL_QUERY_OPERATORS, prefix, "query operator", 80)];
|
||||
break;
|
||||
case "pullCondition":
|
||||
// Fields lead; query operators follow once `$` is typed.
|
||||
items = [...fieldItems(prefix, fields), ...specItems(FIELD_QUERY_OPERATORS, prefix, "query operator", 80)];
|
||||
break;
|
||||
case "fieldPath":
|
||||
items = fieldPathItems(prefix, fields);
|
||||
break;
|
||||
@@ -543,7 +548,7 @@ export function buildMongoCompletionItemsFromContext(context: MongoCompletionCon
|
||||
|
||||
/** Modes whose items are built from the target collection's sampled fields. */
|
||||
export function mongoCompletionNeedsFields(mode: MongoCompletionMode): boolean {
|
||||
return mode === "field" || mode === "filterField" || mode === "fieldPath" || mode === "fieldRef" || mode === "expression";
|
||||
return mode === "field" || mode === "filterField" || mode === "pullCondition" || mode === "fieldPath" || mode === "fieldRef" || mode === "expression";
|
||||
}
|
||||
|
||||
/** Modes whose items are built from the database's collection names. */
|
||||
@@ -990,6 +995,16 @@ function classifyUpdateArgument(scan: MongoCallScan, rootIndex: number): MongoCu
|
||||
function classifyUpdate(scan: MongoCallScan, rootIndex: number): MongoCursorClass {
|
||||
const inner = innermost(scan);
|
||||
if (!inner || innerDepth(scan, rootIndex) < 0) return { mode: "none" };
|
||||
|
||||
const pullIndex = findContainerIndex(scan, rootIndex, "$pull");
|
||||
if (pullIndex >= 0 && scan.stack.length > pullIndex + 1) {
|
||||
const classified = classifyFilter(scan, pullIndex + 1);
|
||||
if (innerDepth(scan, pullIndex + 1) === 0 && classified.mode === "filterField") {
|
||||
return { ...classified, mode: "pullCondition" };
|
||||
}
|
||||
return classified;
|
||||
}
|
||||
|
||||
const parent = scan.stack[scan.stack.length - 2];
|
||||
if (scan.inValue) {
|
||||
// `$currentDate: { at: { $type: "timestamp" } }`.
|
||||
@@ -1067,6 +1082,7 @@ function classifyMethodOptions(method: string, scan: MongoCallScan): MongoCursor
|
||||
return { mode: scan.inString ? "none" : "keyMapValue", method, keyMap: option };
|
||||
}
|
||||
if (FILTER_OPTION_KEYS.has(option)) return { ...classifyFilter(scan, 1), method };
|
||||
if (option === "arrayFilters") return { ...classifyFilter(scan, 2), method };
|
||||
if (option === "pipeline" && scan.stack[1]?.kind === "array") {
|
||||
return { ...classifyPipeline(scan, findPipelineArrayIndex(scan.stack), method === "createCollection" ? "view" : "aggregate"), method };
|
||||
}
|
||||
|
||||
+16
@@ -457,6 +457,16 @@ test("suggests fields under an update operator and array modifiers under $push",
|
||||
const modifiers = labels("db.users.updateOne({}, { $push: { tags: { ", { fields });
|
||||
assert.ok(modifiers.includes("$each"));
|
||||
assert.equal(modifiers.includes("$set"), false);
|
||||
|
||||
const pullOperators = labels("db.users.updateOne({}, { $pull: { tags: { $", { fields });
|
||||
assert.ok(pullOperators.includes("$gte"));
|
||||
assert.ok(pullOperators.includes("$in"));
|
||||
assert.equal(pullOperators.includes("$set"), false);
|
||||
assert.equal(pullOperators.includes("name"), false);
|
||||
|
||||
assert.deepEqual(labels("db.users.updateOne({}, { $pull: { tags: { na", { fields }), ["name"]);
|
||||
assert.ok(labels("db.users.updateOne({}, { $pull: { tags: { $in: [").includes("ObjectId"));
|
||||
assert.equal(labels("db.users.updateOne({}, { $pullAll: { tags: [").length, 0);
|
||||
});
|
||||
|
||||
test("suggests fields inside a $match stage rather than operators", () => {
|
||||
@@ -690,6 +700,12 @@ test("suggests the option keys of methods that take an options argument", () =>
|
||||
// A `sort` or `projection` option holds field names.
|
||||
assert.deepEqual(labels("db.users.findOneAndUpdate({}, {$set:{a:1}}, { sort: { na", { fields }), ["name"]);
|
||||
assert.deepEqual(labels("db.users.findOneAndDelete({}, { projection: { na", { fields }), ["name"]);
|
||||
|
||||
// An `arrayFilters` option holds filter documents.
|
||||
assert.deepEqual(labels('db.users.updateOne({}, { $set: { "tags.$[e].x": 1 } }, { arrayFilters: [{ na', { fields }), ["name"]);
|
||||
assert.ok(labels('db.users.updateOne({}, { $set: { "tags.$[e].x": 1 } }, { arrayFilters: [{ "tags.x": { $', { fields }).includes("$gte"));
|
||||
assert.deepEqual(labels('db.users.updateMany({}, { $set: { "tags.$[e].x": 1 } }, { arrayFilters: [{ na', { fields }), ["name"]);
|
||||
assert.deepEqual(labels('db.users.findOneAndUpdate({}, { $set: { "tags.$[e].x": 1 } }, { arrayFilters: [{ na', { fields }), ["name"]);
|
||||
});
|
||||
|
||||
test("stays quiet in the trailing argument of methods that take no options", () => {
|
||||
|
||||
Reference in new issue
Block a user