|
2 | 2 |
|
3 | 3 | import { describe, it, expect } from "vitest"; |
4 | 4 | import { Database } from "./database.js"; |
| 5 | +import { Entity } from "../entity/entity.js"; |
| 6 | +import { FractionalIndex } from "../../schema/fractional-index/fractional-index.js"; |
5 | 7 |
|
6 | 8 | // ============================================================================ |
7 | 9 | // Catalogue pattern coverage — each describe corresponds to one of the 12 |
@@ -1472,3 +1474,78 @@ describe("registry maintenance", () => { |
1472 | 1474 | expect([...ext.indexes.byName.find({ name: "alice" })].sort()).toEqual([e1, e2].sort()); |
1473 | 1475 | }); |
1474 | 1476 | }); |
| 1477 | + |
| 1478 | +describe("complex sort index — orderedChildOf (nested parent + fractional-index order)", () => { |
| 1479 | + // orderedChildOf bundles the parent entity reference and the fractional-index |
| 1480 | + // sort key in a single object component. The index uses a computed slot map |
| 1481 | + // to extract the group key and a custom compare that drills into the nested |
| 1482 | + // object for the order field — matching the README pattern: |
| 1483 | + // key: (f) => f.parent, order: { by: ["foo"], compare: (a, b) => a.foo.order < b.foo.order ? -1 : 1 } |
| 1484 | + const plugin = () => Database.Plugin.create({ |
| 1485 | + components: { |
| 1486 | + orderedChildOf: { |
| 1487 | + type: "object", |
| 1488 | + properties: { |
| 1489 | + parent: Entity.schema, |
| 1490 | + order: FractionalIndex.schema, |
| 1491 | + }, |
| 1492 | + required: ["parent", "order"], |
| 1493 | + }, |
| 1494 | + }, |
| 1495 | + archetypes: { Child: ["orderedChildOf"] }, |
| 1496 | + indexes: { |
| 1497 | + orderedChildrenOf: { |
| 1498 | + key: { parent: (c) => c.orderedChildOf!.parent }, |
| 1499 | + components: ["orderedChildOf"], |
| 1500 | + order: { |
| 1501 | + by: ["orderedChildOf"], |
| 1502 | + compare: ( |
| 1503 | + a: { orderedChildOf: { parent: number; order: string } }, |
| 1504 | + b: { orderedChildOf: { parent: number; order: string } }, |
| 1505 | + ) => a.orderedChildOf.order < b.orderedChildOf.order ? -1 : 1, |
| 1506 | + }, |
| 1507 | + }, |
| 1508 | + }, |
| 1509 | + transactions: { |
| 1510 | + add: (t, args: { parent: number; order: string }) => |
| 1511 | + t.archetypes.Child.insert({ orderedChildOf: args }), |
| 1512 | + reorder: (t, args: { entity: number; parent: number; order: string }) => |
| 1513 | + t.update(args.entity, { orderedChildOf: { parent: args.parent, order: args.order } }), |
| 1514 | + delete: (t, e: number) => t.delete(e), |
| 1515 | + }, |
| 1516 | + }); |
| 1517 | + |
| 1518 | + it("returns children sorted by fractional index within the same parent", () => { |
| 1519 | + const db = Database.create(plugin()); |
| 1520 | + const c = db.transactions.add({ parent: 1, order: "a2" }); |
| 1521 | + const a = db.transactions.add({ parent: 1, order: "a0" }); |
| 1522 | + const b = db.transactions.add({ parent: 1, order: "a1" }); |
| 1523 | + expect(db.indexes.orderedChildrenOf.find({ parent: 1 })).toEqual([a, b, c]); |
| 1524 | + }); |
| 1525 | + |
| 1526 | + it("children of different parents are in independent buckets", () => { |
| 1527 | + const db = Database.create(plugin()); |
| 1528 | + const child1 = db.transactions.add({ parent: 1, order: "a0" }); |
| 1529 | + const child2 = db.transactions.add({ parent: 2, order: "a0" }); |
| 1530 | + expect(db.indexes.orderedChildrenOf.find({ parent: 1 })).toEqual([child1]); |
| 1531 | + expect(db.indexes.orderedChildrenOf.find({ parent: 2 })).toEqual([child2]); |
| 1532 | + }); |
| 1533 | + |
| 1534 | + it("reordering an entity repositions it in the sorted result", () => { |
| 1535 | + const db = Database.create(plugin()); |
| 1536 | + const a = db.transactions.add({ parent: 1, order: "a0" }); |
| 1537 | + const b = db.transactions.add({ parent: 1, order: "a1" }); |
| 1538 | + const c = db.transactions.add({ parent: 1, order: "a2" }); |
| 1539 | + db.transactions.reorder({ entity: a, parent: 1, order: "a3" }); |
| 1540 | + expect(db.indexes.orderedChildrenOf.find({ parent: 1 })).toEqual([b, c, a]); |
| 1541 | + }); |
| 1542 | + |
| 1543 | + it("delete removes the entity from its sorted bucket", () => { |
| 1544 | + const db = Database.create(plugin()); |
| 1545 | + const a = db.transactions.add({ parent: 1, order: "a0" }); |
| 1546 | + const b = db.transactions.add({ parent: 1, order: "a1" }); |
| 1547 | + const c = db.transactions.add({ parent: 1, order: "a2" }); |
| 1548 | + db.transactions.delete(b); |
| 1549 | + expect(db.indexes.orderedChildrenOf.find({ parent: 1 })).toEqual([a, c]); |
| 1550 | + }); |
| 1551 | +}); |
0 commit comments