cry-search 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/UNIVERSE.md CHANGED
@@ -620,6 +620,43 @@ SearchUniverse provides methods to update data and metadata after initial load.
620
620
  └─────────────────────────────────────────────────────────────────────────────┘
621
621
  ```
622
622
 
623
+ ### Sync Collection (Snapshot Diff)
624
+
625
+ ```
626
+ ┌─────────────────────────────────────────────────────────────────────────────┐
627
+ │ universe.syncCollection(collectionName, newItems, options?) │
628
+ ├─────────────────────────────────────────────────────────────────────────────┤
629
+ │ │
630
+ │ Diffs a fresh full snapshot against the current collection state. │
631
+ │ Use for periodic refresh cycles when you don't have a delta. │
632
+ │ │
633
+ │ newItems = [ │
634
+ │ { _id: "s1", name: "Krajnik", address: "Ljubljana" }, ← unchanged │
635
+ │ { _id: "s2", name: "Novak", address: "Celje" }, ← updated │
636
+ │ { _id: "s3", name: "Horvat", address: "Koper" }, ← added │
637
+ │ // s4 from previous state is missing → removed │
638
+ │ ] │
639
+ │ │
640
+ │ Behavior: │
641
+ │ 1. Items in collection but absent from newItems → removed │
642
+ │ (data + metadata + primaryToLinked all cleaned) │
643
+ │ 2. Items in both → tokens compared; metadata.set only if changed │
644
+ │ 3. New items → tokenized, added to metadata + primaryToLinked │
645
+ │ 4. _deleted/_blocked items in newItems → removed from metadata │
646
+ │ (kept in data, same convention as updateSearchMetadata) │
647
+ │ │
648
+ │ Options: │
649
+ │ skipUpdateCheck: true → don't re-tokenize existing items (faster) │
650
+ │ data refs still get updated │
651
+ │ │
652
+ │ Returns: SyncResult │
653
+ │ { added, updated, removed, addedIds[], updatedIds[], removedIds[] } │
654
+ │ │
655
+ │ Single shared tokensMap is used across the whole snapshot for efficiency. │
656
+ │ │
657
+ └─────────────────────────────────────────────────────────────────────────────┘
658
+ ```
659
+
623
660
  ### Clear Universe
624
661
 
625
662
  ```
@@ -1,4 +1,4 @@
1
- import type { Id, NumericSearchMetadata, SearchableData, SearchableObject, UniversalSearchOpts, CollectionConfig, UpdateResult, BatchUpdateResult } from '../types';
1
+ import type { Id, NumericSearchMetadata, SearchableData, SearchableObject, UniversalSearchOpts, CollectionConfig, UpdateResult, BatchUpdateResult, SyncResult, SyncOptions } from '../types';
2
2
  export type { UniversalSearchOpts, CollectionConfig, UpdateResult, BatchUpdateResult } from '../types';
3
3
  /**
4
4
  * SearchUniverse manages multiple collections with their metadata and relationships.
@@ -154,6 +154,34 @@ export declare class SearchUniverse {
154
154
  * @returns Number of items actually removed
155
155
  */
156
156
  removeFromSearchMetadataBatch(collectionName: string, ids: Id[]): number;
157
+ /**
158
+ * Syncs a collection against a fresh snapshot of items.
159
+ *
160
+ * Compares the provided `newItems` to the current collection state and applies
161
+ * the diff: items not in `newItems` are removed, new items are added, and existing
162
+ * items are updated when their tokens change. Items marked `_deleted` or `_blocked`
163
+ * are removed from metadata (and from `primaryToLinked` for linked collections),
164
+ * but remain in `collection.data` for reference — same convention as
165
+ * {@link updateSearchMetadata}.
166
+ *
167
+ * Use this for full refresh cycles (e.g. periodic snapshot from server) when you
168
+ * don't have a delta. For incremental delta updates use
169
+ * {@link updateSearchMetadataBatch} + {@link removeFromSearchMetadataBatch}.
170
+ *
171
+ * @typeParam T - Type of items in the collection
172
+ * @param collectionName - Name of the collection
173
+ * @param newItems - Full new dataset (snapshot)
174
+ * @param options - Optional sync options (e.g. `skipUpdateCheck` to avoid re-tokenizing existing items)
175
+ * @returns SyncResult with counts and IDs of added/updated/removed items
176
+ *
177
+ * @example
178
+ * ```typescript
179
+ * const fresh = await fetchAllProducts();
180
+ * const result = universe.syncCollection('products', fresh);
181
+ * // { added: 3, updated: 12, removed: 1, addedIds: [...], ... }
182
+ * ```
183
+ */
184
+ syncCollection<T extends SearchableObject>(collectionName: string, newItems: T[], options?: SyncOptions): SyncResult;
157
185
  /**
158
186
  * Gets collection or throws an error if not found.
159
187
  *
@@ -1 +1 @@
1
- {"version":3,"file":"SearchUniverse.d.ts","sourceRoot":"","sources":["../../src/common/SearchUniverse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAEhB,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAWlB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAcvG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAiC;IAEpD;;;;;OAKG;IACH,kBAAkB,CAAC,CAAC,SAAS,gBAAgB,EAAE,CAAC,GAAG,OAAO,EACxD,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAM,GAClC,IAAI;IAmBP;;;;;OAKG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IAsC1E;;;;;;;OAOG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,SAAS;IAKlF;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,CAAC,EAAE;IAKjE;;;;;OAKG;IACH,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAKjD;;;;;OAKG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB;IAK1D;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC;IAK9E;;;;;;;OAOG;IACH,YAAY,CAAC,oBAAoB,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE;IAQ/D;;;;;;;OAOG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EACvC,oBAAoB,EAAE,MAAM,EAC5B,SAAS,EAAE,EAAE,GACZ,CAAC,EAAE;IAMN;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,EAC7C,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,CAAC,GACN,YAAY;IAoFf;;;;;;OAMG;IACH,yBAAyB,CAAC,CAAC,SAAS,gBAAgB,EAClD,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,CAAC,EAAE,GACT,iBAAiB;IA0BpB;;;;;;OAMG;IACH,wBAAwB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO;IAmCjE;;;;;;OAMG;IACH,6BAA6B,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,MAAM;IAcxE;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAQ5B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,iBAAiB,CACf,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,mBAAmB,GACzB,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;QAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAC;QAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IA6FlJ;;;;;;;;OAQG;IACH,KAAK,IAAI,IAAI;CAUd"}
1
+ {"version":3,"file":"SearchUniverse.d.ts","sourceRoot":"","sources":["../../src/common/SearchUniverse.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1332,6 +1332,16 @@ function syncLinkedItemsForPrimaryNumeric(linked, primaryId, newLinkedItems, for
1332
1332
  return result;
1333
1333
  }
1334
1334
  // src/common/SearchUniverse.ts
1335
+ function tokensEqual3(a, b) {
1336
+ if (a.length !== b.length)
1337
+ return false;
1338
+ for (let i = 0;i < a.length; i++) {
1339
+ if (a[i] !== b[i])
1340
+ return false;
1341
+ }
1342
+ return true;
1343
+ }
1344
+
1335
1345
  class SearchUniverse {
1336
1346
  collections = new Map;
1337
1347
  registerCollection(name, config = {}) {
@@ -1535,6 +1545,113 @@ class SearchUniverse {
1535
1545
  }
1536
1546
  return removed;
1537
1547
  }
1548
+ syncCollection(collectionName, newItems, options) {
1549
+ const collection = this.getCollectionOrThrow(collectionName);
1550
+ const result = {
1551
+ added: 0,
1552
+ updated: 0,
1553
+ removed: 0,
1554
+ addedIds: [],
1555
+ updatedIds: [],
1556
+ removedIds: []
1557
+ };
1558
+ const skipUpdateCheck = options?.skipUpdateCheck ?? false;
1559
+ const fkGetter = collection.config.linkedTo ? collection.config.linkedTo.foreignKeyGetter : undefined;
1560
+ const newIds = new Set;
1561
+ for (const item of newItems)
1562
+ newIds.add(item._id);
1563
+ const toRemove = [];
1564
+ for (const id of collection.data.keys()) {
1565
+ if (!newIds.has(id))
1566
+ toRemove.push(id);
1567
+ }
1568
+ for (const id of toRemove) {
1569
+ const existing = collection.data.get(id);
1570
+ if (fkGetter && collection.primaryToLinked) {
1571
+ const fk = fkGetter(existing);
1572
+ if (fk) {
1573
+ const list = collection.primaryToLinked.get(fk);
1574
+ if (list) {
1575
+ const idx = list.indexOf(id);
1576
+ if (idx !== -1) {
1577
+ list.splice(idx, 1);
1578
+ if (list.length === 0)
1579
+ collection.primaryToLinked.delete(fk);
1580
+ }
1581
+ }
1582
+ }
1583
+ }
1584
+ const hadMetadata = collection.metadata.has(id);
1585
+ collection.data.delete(id);
1586
+ collection.metadata.delete(id);
1587
+ if (hadMetadata) {
1588
+ result.removed++;
1589
+ result.removedIds.push(id);
1590
+ }
1591
+ }
1592
+ const tokensMap = createTokensMap();
1593
+ try {
1594
+ for (const item of newItems) {
1595
+ const id = item._id;
1596
+ const existing = collection.data.get(id);
1597
+ const hadMetadata = collection.metadata.has(id);
1598
+ const shouldTokenize = !existing || !skipUpdateCheck;
1599
+ let newTokens;
1600
+ if (shouldTokenize) {
1601
+ newTokens = prepareObjectForSearchNumeric(item, collection.config.spec, tokensMap);
1602
+ }
1603
+ if (fkGetter && collection.primaryToLinked) {
1604
+ const newFk = fkGetter(item);
1605
+ const oldFk = existing ? fkGetter(existing) : undefined;
1606
+ const wasInIndex = hadMetadata;
1607
+ const shouldBeInIndex = !!newFk && !item._deleted && !item._blocked;
1608
+ if (oldFk !== newFk || wasInIndex !== shouldBeInIndex) {
1609
+ if (oldFk && wasInIndex) {
1610
+ const list = collection.primaryToLinked.get(oldFk);
1611
+ if (list) {
1612
+ const idx = list.indexOf(id);
1613
+ if (idx !== -1) {
1614
+ list.splice(idx, 1);
1615
+ if (list.length === 0)
1616
+ collection.primaryToLinked.delete(oldFk);
1617
+ }
1618
+ }
1619
+ }
1620
+ if (shouldBeInIndex && newFk) {
1621
+ const list = collection.primaryToLinked.get(newFk) || [];
1622
+ if (!list.includes(id))
1623
+ list.push(id);
1624
+ collection.primaryToLinked.set(newFk, list);
1625
+ }
1626
+ }
1627
+ }
1628
+ collection.data.set(id, item);
1629
+ if (!shouldTokenize)
1630
+ continue;
1631
+ if (newTokens === undefined) {
1632
+ if (hadMetadata) {
1633
+ collection.metadata.delete(id);
1634
+ result.removed++;
1635
+ result.removedIds.push(id);
1636
+ }
1637
+ } else if (!hadMetadata) {
1638
+ collection.metadata.set(id, newTokens);
1639
+ result.added++;
1640
+ result.addedIds.push(id);
1641
+ } else {
1642
+ const existingTokens = collection.metadata.get(id);
1643
+ if (!tokensEqual3(existingTokens, newTokens)) {
1644
+ collection.metadata.set(id, newTokens);
1645
+ result.updated++;
1646
+ result.updatedIds.push(id);
1647
+ }
1648
+ }
1649
+ }
1650
+ } finally {
1651
+ clearTokensMap(tokensMap);
1652
+ }
1653
+ return result;
1654
+ }
1538
1655
  getCollectionOrThrow(name) {
1539
1656
  const collection = this.collections.get(name);
1540
1657
  if (!collection) {
package/dist/index.js CHANGED
@@ -1247,6 +1247,16 @@ function syncLinkedItemsForPrimaryNumeric(linked, primaryId, newLinkedItems, for
1247
1247
  return result;
1248
1248
  }
1249
1249
  // src/common/SearchUniverse.ts
1250
+ function tokensEqual3(a, b) {
1251
+ if (a.length !== b.length)
1252
+ return false;
1253
+ for (let i = 0;i < a.length; i++) {
1254
+ if (a[i] !== b[i])
1255
+ return false;
1256
+ }
1257
+ return true;
1258
+ }
1259
+
1250
1260
  class SearchUniverse {
1251
1261
  collections = new Map;
1252
1262
  registerCollection(name, config = {}) {
@@ -1450,6 +1460,113 @@ class SearchUniverse {
1450
1460
  }
1451
1461
  return removed;
1452
1462
  }
1463
+ syncCollection(collectionName, newItems, options) {
1464
+ const collection = this.getCollectionOrThrow(collectionName);
1465
+ const result = {
1466
+ added: 0,
1467
+ updated: 0,
1468
+ removed: 0,
1469
+ addedIds: [],
1470
+ updatedIds: [],
1471
+ removedIds: []
1472
+ };
1473
+ const skipUpdateCheck = options?.skipUpdateCheck ?? false;
1474
+ const fkGetter = collection.config.linkedTo ? collection.config.linkedTo.foreignKeyGetter : undefined;
1475
+ const newIds = new Set;
1476
+ for (const item of newItems)
1477
+ newIds.add(item._id);
1478
+ const toRemove = [];
1479
+ for (const id of collection.data.keys()) {
1480
+ if (!newIds.has(id))
1481
+ toRemove.push(id);
1482
+ }
1483
+ for (const id of toRemove) {
1484
+ const existing = collection.data.get(id);
1485
+ if (fkGetter && collection.primaryToLinked) {
1486
+ const fk = fkGetter(existing);
1487
+ if (fk) {
1488
+ const list = collection.primaryToLinked.get(fk);
1489
+ if (list) {
1490
+ const idx = list.indexOf(id);
1491
+ if (idx !== -1) {
1492
+ list.splice(idx, 1);
1493
+ if (list.length === 0)
1494
+ collection.primaryToLinked.delete(fk);
1495
+ }
1496
+ }
1497
+ }
1498
+ }
1499
+ const hadMetadata = collection.metadata.has(id);
1500
+ collection.data.delete(id);
1501
+ collection.metadata.delete(id);
1502
+ if (hadMetadata) {
1503
+ result.removed++;
1504
+ result.removedIds.push(id);
1505
+ }
1506
+ }
1507
+ const tokensMap = createTokensMap();
1508
+ try {
1509
+ for (const item of newItems) {
1510
+ const id = item._id;
1511
+ const existing = collection.data.get(id);
1512
+ const hadMetadata = collection.metadata.has(id);
1513
+ const shouldTokenize = !existing || !skipUpdateCheck;
1514
+ let newTokens;
1515
+ if (shouldTokenize) {
1516
+ newTokens = prepareObjectForSearchNumeric(item, collection.config.spec, tokensMap);
1517
+ }
1518
+ if (fkGetter && collection.primaryToLinked) {
1519
+ const newFk = fkGetter(item);
1520
+ const oldFk = existing ? fkGetter(existing) : undefined;
1521
+ const wasInIndex = hadMetadata;
1522
+ const shouldBeInIndex = !!newFk && !item._deleted && !item._blocked;
1523
+ if (oldFk !== newFk || wasInIndex !== shouldBeInIndex) {
1524
+ if (oldFk && wasInIndex) {
1525
+ const list = collection.primaryToLinked.get(oldFk);
1526
+ if (list) {
1527
+ const idx = list.indexOf(id);
1528
+ if (idx !== -1) {
1529
+ list.splice(idx, 1);
1530
+ if (list.length === 0)
1531
+ collection.primaryToLinked.delete(oldFk);
1532
+ }
1533
+ }
1534
+ }
1535
+ if (shouldBeInIndex && newFk) {
1536
+ const list = collection.primaryToLinked.get(newFk) || [];
1537
+ if (!list.includes(id))
1538
+ list.push(id);
1539
+ collection.primaryToLinked.set(newFk, list);
1540
+ }
1541
+ }
1542
+ }
1543
+ collection.data.set(id, item);
1544
+ if (!shouldTokenize)
1545
+ continue;
1546
+ if (newTokens === undefined) {
1547
+ if (hadMetadata) {
1548
+ collection.metadata.delete(id);
1549
+ result.removed++;
1550
+ result.removedIds.push(id);
1551
+ }
1552
+ } else if (!hadMetadata) {
1553
+ collection.metadata.set(id, newTokens);
1554
+ result.added++;
1555
+ result.addedIds.push(id);
1556
+ } else {
1557
+ const existingTokens = collection.metadata.get(id);
1558
+ if (!tokensEqual3(existingTokens, newTokens)) {
1559
+ collection.metadata.set(id, newTokens);
1560
+ result.updated++;
1561
+ result.updatedIds.push(id);
1562
+ }
1563
+ }
1564
+ }
1565
+ } finally {
1566
+ clearTokensMap(tokensMap);
1567
+ }
1568
+ return result;
1569
+ }
1453
1570
  getCollectionOrThrow(name) {
1454
1571
  const collection = this.collections.get(name);
1455
1572
  if (!collection) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cry-search",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "A fast, memory-efficient search library for large datasets with support for tokenized matching, linked collections, and per-field match modes.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",