feat(mongodb): improve document quick filters
This commit is contained in:
1 parent
c417e54698
commit
681da22c33
32 files changed
+1639
-116
No files matched your search
+160
-2
@@ -1,14 +1,20 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import {
|
||||
arrayObjectAncestorPathForDocumentField,
|
||||
buildDocumentFilterCondition,
|
||||
buildElasticsearchQueryFromRules,
|
||||
combineDocumentFilterConditions,
|
||||
currentDocumentFilterJson,
|
||||
documentFieldPathOptionsFromDocuments,
|
||||
documentFieldPathTreeFromDocuments,
|
||||
documentStoreProviderFor,
|
||||
elasticsearchQueryTypeOptions,
|
||||
elasticsearchSearchBodyFromDocumentQuery,
|
||||
elasticsearchStructuredFilter,
|
||||
flattenDocumentFieldPathTree,
|
||||
formatDocumentQueryInput,
|
||||
searchDocumentFieldPathTree,
|
||||
type DocumentFilterRule,
|
||||
} from "../../apps/desktop/src/lib/app/documentStoreProvider.ts";
|
||||
|
||||
@@ -47,12 +53,164 @@ test("providers build store-specific query previews", () => {
|
||||
|
||||
test("builds reusable document filter conditions", () => {
|
||||
assert.deepEqual(buildDocumentFilterCondition(rule({})), { city: "长治" });
|
||||
assert.deepEqual(buildDocumentFilterCondition(rule({ mode: "not-like", rawValue: "test" })), {
|
||||
city: { $not: { $regex: "test", $options: "i" } },
|
||||
assert.deepEqual(buildDocumentFilterCondition(rule({ mode: "like", rawValue: "a.b[0]*" })), {
|
||||
city: { $regex: "a\\.b\\[0\\]\\*", $options: "i" },
|
||||
});
|
||||
assert.deepEqual(buildDocumentFilterCondition(rule({ mode: "not-like", rawValue: "test?" })), {
|
||||
city: { $not: { $regex: "test\\?", $options: "i" } },
|
||||
});
|
||||
assert.deepEqual(buildDocumentFilterCondition(rule({ mode: "is-not-null", rawValue: "" })), { city: { $ne: null } });
|
||||
});
|
||||
|
||||
test("preserves Extended JSON types for MongoDB structured filters", () => {
|
||||
assert.deepEqual(
|
||||
buildDocumentFilterCondition(rule({ fieldName: "profile.ownerId", rawValue: "507f1f77bcf86cd799439011" }), {
|
||||
kind: "mongodb",
|
||||
sampleValue: { $oid: "507f1f77bcf86cd799439011" },
|
||||
}),
|
||||
{ "profile.ownerId": { $oid: "507f1f77bcf86cd799439011" } },
|
||||
);
|
||||
assert.deepEqual(
|
||||
buildDocumentFilterCondition(rule({ fieldName: "profile.createdAt", rawValue: "2026-07-13T00:00:00Z" }), {
|
||||
kind: "mongodb",
|
||||
sampleValue: { $date: "2025-01-01T00:00:00Z" },
|
||||
}),
|
||||
{ "profile.createdAt": { $date: "2026-07-13T00:00:00Z" } },
|
||||
);
|
||||
assert.deepEqual(
|
||||
buildDocumentFilterCondition(rule({ fieldName: "ownerIds", rawValue: "507f1f77bcf86cd799439013" }), {
|
||||
kind: "mongodb",
|
||||
sampleValue: [{ $oid: "507f1f77bcf86cd799439011" }, null, { $oid: "507f1f77bcf86cd799439012" }],
|
||||
}),
|
||||
{ ownerIds: { $oid: "507f1f77bcf86cd799439013" } },
|
||||
);
|
||||
assert.deepEqual(
|
||||
buildDocumentFilterCondition(rule({ fieldName: "eventDates", rawValue: "2026-07-13T00:00:00Z" }), {
|
||||
kind: "mongodb",
|
||||
sampleValue: [{ $date: "2025-01-01T00:00:00Z" }, { $date: { $numberLong: "1752364800000" } }],
|
||||
}),
|
||||
{ eventDates: { $date: "2026-07-13T00:00:00Z" } },
|
||||
);
|
||||
assert.deepEqual(
|
||||
buildDocumentFilterCondition(rule({ fieldName: "mixed", rawValue: "507f1f77bcf86cd799439013" }), {
|
||||
kind: "mongodb",
|
||||
sampleValue: [{ $oid: "507f1f77bcf86cd799439011" }, "plain-string"],
|
||||
}),
|
||||
{ mixed: "507f1f77bcf86cd799439013" },
|
||||
);
|
||||
});
|
||||
|
||||
test("extracts nested document field paths for structured filters", () => {
|
||||
assert.deepEqual(
|
||||
documentFieldPathOptionsFromDocuments([
|
||||
{ _id: "1", profile: { city: "上海", address: { zip: 200000 } }, tags: ["a"] },
|
||||
{ _id: "2", status: "active", profile: { city: "北京" }, audit: [{ by: "ops" }] },
|
||||
]),
|
||||
["_id", "profile", "profile.city", "profile.address", "profile.address.zip", "tags", "status", "audit", "audit.by"],
|
||||
);
|
||||
});
|
||||
|
||||
test("treats BSON Extended JSON wrappers as scalar document fields", () => {
|
||||
const tree = documentFieldPathTreeFromDocuments([
|
||||
{
|
||||
_id: { $oid: "507f1f77bcf86cd799439011" },
|
||||
ownerId: { $oid: "507f1f77bcf86cd799439012" },
|
||||
createdAt: { $date: { $numberLong: "1752364800000" } },
|
||||
sequence: { $numberLong: "9007199254740993" },
|
||||
encoded: { $binary: { base64: "AQID", subType: "00" }, $type: "00" },
|
||||
profile: { name: "Ada" },
|
||||
},
|
||||
]);
|
||||
const flattened = flattenDocumentFieldPathTree(tree);
|
||||
|
||||
assert.deepEqual(
|
||||
flattened.map((node) => node.path),
|
||||
["_id", "ownerId", "createdAt", "sequence", "encoded", "profile", "profile.name"],
|
||||
);
|
||||
assert.equal(flattened.find((node) => node.path === "ownerId")?.kind, "scalar");
|
||||
assert.equal(flattened.find((node) => node.path === "createdAt")?.kind, "scalar");
|
||||
assert.equal(flattened.find((node) => node.path === "sequence")?.kind, "scalar");
|
||||
assert.equal(flattened.find((node) => node.path === "encoded")?.kind, "scalar");
|
||||
});
|
||||
|
||||
test("builds hierarchical document field path tree for array objects", () => {
|
||||
const tree = documentFieldPathTreeFromDocuments([{ _id: "1", orders: [{ sku: "A", qty: 2 }], batches: [[{ lot: "L-1" }]], tags: ["a"], profile: { address: { zip: 1 } } }]);
|
||||
const flattened = flattenDocumentFieldPathTree(tree);
|
||||
const orders = tree.find((node) => node.path === "orders");
|
||||
const batches = tree.find((node) => node.path === "batches");
|
||||
const sku = flattened.find((node) => node.path === "orders.sku");
|
||||
|
||||
assert.equal(orders?.kind, "array-object");
|
||||
assert.equal(batches?.kind, "array-object");
|
||||
assert.equal(orders?.label, "orders[]");
|
||||
assert.equal(sku?.path, "orders.sku");
|
||||
assert.equal(sku?.displayPath, "orders[] > sku");
|
||||
assert.deepEqual(sku?.sampleValue, "A");
|
||||
assert.equal(arrayObjectAncestorPathForDocumentField(tree, "orders.sku"), "orders");
|
||||
assert.equal(arrayObjectAncestorPathForDocumentField(tree, "profile.address.zip"), null);
|
||||
assert.deepEqual(
|
||||
flattened.map((node) => node.path),
|
||||
["_id", "orders", "orders.sku", "orders.qty", "batches", "batches.lot", "tags", "profile", "profile.address", "profile.address.zip"],
|
||||
);
|
||||
});
|
||||
|
||||
test("excludes BSON Extended JSON wrappers from array document field paths", () => {
|
||||
const tree = documentFieldPathTreeFromDocuments([
|
||||
{
|
||||
_id: "1",
|
||||
timestamps: [{ $date: { $numberLong: "1752364800000" } }, { $date: { $numberLong: "1752451200000" } }],
|
||||
ownerIds: [{ $oid: "507f1f77bcf86cd799439011" }],
|
||||
sequences: [{ $numberLong: "9007199254740993" }],
|
||||
nestedDates: [[{ $date: { $numberLong: "1752364800000" } }]],
|
||||
events: [{ at: { $date: { $numberLong: "1752364800000" } }, by: "ops" }, { $date: { $numberLong: "1752451200000" } }],
|
||||
},
|
||||
]);
|
||||
const flattened = flattenDocumentFieldPathTree(tree);
|
||||
|
||||
assert.deepEqual(
|
||||
flattened.map((node) => node.path),
|
||||
["_id", "timestamps", "ownerIds", "sequences", "nestedDates", "events", "events.at", "events.by"],
|
||||
);
|
||||
assert.equal(flattened.find((node) => node.path === "timestamps")?.kind, "array");
|
||||
assert.equal(flattened.find((node) => node.path === "ownerIds")?.kind, "array");
|
||||
assert.equal(flattened.find((node) => node.path === "sequences")?.kind, "array");
|
||||
assert.equal(flattened.find((node) => node.path === "nestedDates")?.kind, "array");
|
||||
assert.equal(flattened.find((node) => node.path === "events")?.kind, "array-object");
|
||||
assert.equal(flattened.find((node) => node.path === "events.at")?.kind, "scalar");
|
||||
});
|
||||
|
||||
test("searches nested document field paths", () => {
|
||||
const tree = documentFieldPathTreeFromDocuments([{ profile: { address: { zip: 200000 }, city: "Shanghai" }, orders: [{ sku: "A" }] }]);
|
||||
assert.deepEqual(
|
||||
searchDocumentFieldPathTree(tree, "address").map((node) => node.path),
|
||||
["profile.address", "profile.address.zip"],
|
||||
);
|
||||
assert.deepEqual(
|
||||
searchDocumentFieldPathTree(tree, "orders[] > sku").map((node) => node.path),
|
||||
["orders.sku"],
|
||||
);
|
||||
});
|
||||
|
||||
test("uses elemMatch only for AND conditions on the same array object", () => {
|
||||
const conditions = [{ "orders.sku": "A" }, { "orders.qty": 2 }];
|
||||
const rules = [rule({ fieldName: "orders.sku" }), rule({ fieldName: "orders.qty", rawValue: "2", conjunction: "AND" })];
|
||||
assert.deepEqual(combineDocumentFilterConditions(conditions, rules, ["orders", "orders"]), {
|
||||
orders: { $elemMatch: { $and: [{ sku: "A" }, { qty: 2 }] } },
|
||||
});
|
||||
assert.deepEqual(combineDocumentFilterConditions([{ status: "active" }, ...conditions], [rule({ fieldName: "status" }), ...rules], [null, "orders", "orders"]), {
|
||||
$and: [{ status: "active" }, { orders: { $elemMatch: { $and: [{ sku: "A" }, { qty: 2 }] } } }],
|
||||
});
|
||||
assert.deepEqual(combineDocumentFilterConditions(conditions, rules, ["orders", "lineItems"]), {
|
||||
$and: conditions,
|
||||
});
|
||||
assert.deepEqual(combineDocumentFilterConditions(conditions, [rules[0], rule({ fieldName: "orders.qty", rawValue: "2", conjunction: "OR" })], ["orders", "orders"]), {
|
||||
$or: conditions,
|
||||
});
|
||||
});
|
||||
|
||||
test("formats document query object input", () => {
|
||||
assert.equal(formatDocumentQueryInput('{profile:{city:"上海"},status:"active"}', "mongodb"), ["{", ' "profile": {', ' "city": "上海"', " },", ' "status": "active"', "}"].join("\n"));
|
||||
});
|
||||
test("preserves MongoDB int64 document filter values", () => {
|
||||
const id = "2048938405781032962";
|
||||
const firstUnsafeInteger = "9007199254740993";
|
||||
|
||||
Reference in new issue
Block a user