@cloudbitmaps/roaring 0.3.0 → 0.4.0
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/dist/bin/compact-segments.js +1 -1
- package/dist/bin/export-segments.js +1 -1
- package/dist/{chunk-HRS3Q6CJ.js → chunk-TXSBMJBE.js} +132 -21
- package/dist/chunk-TXSBMJBE.js.map +1 -0
- package/dist/index.cjs +123 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -19
- package/dist/index.d.ts +99 -19
- package/dist/index.js +123 -12
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-HRS3Q6CJ.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { bulkLoadCrbmGeneration as bulkLoadCrbmGeneration$1, compactSegment as compactSegment$1, runCompactionCycle as runCompactionCycle$1, runExport as runExport$1, CodecBitmap, CodecInterface, IColdDriver, ColdChunkSource, IWarmDriver, IRegistryDriver, IKeystore, Clock, Rng, RetryPolicy, IMetricsSink,
|
|
1
|
+
import { bulkLoadCrbmGeneration as bulkLoadCrbmGeneration$1, compactSegment as compactSegment$1, runCompactionCycle as runCompactionCycle$1, runExport as runExport$1, CodecBitmap, CodecInterface, BudgetOption, IColdDriver, ColdChunkSource, IWarmDriver, IRegistryDriver, IKeystore, Clock, Rng, RetryPolicy, IMetricsSink, SegmentEngine, SegmentRef, PricingProfile, Workload, Topology, CostReport, IAuditSink, CompactionOptions, CompactionResult, ConsistencyReport, ExportSink, ExportOptions, ExportManifest, EstimateInput } from '@cloudbitmaps/core';
|
|
2
2
|
export * from '@cloudbitmaps/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -21,7 +21,15 @@ type BulkLoad = typeof bulkLoadCrbmGeneration$1;
|
|
|
21
21
|
type CompactSegment = typeof compactSegment$1;
|
|
22
22
|
type RunCompactionCycle = typeof runCompactionCycle$1;
|
|
23
23
|
type RunExport = typeof runExport$1;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* {@link coreBulkLoad} with the roaring codec **and a real clock** pre-bound.
|
|
26
|
+
*
|
|
27
|
+
* The clock is what makes a large load **cooperative**: bulk-load yields the event loop periodically instead of
|
|
28
|
+
* stalling the process for its whole duration (measured at 819 ms for 1M ids before this). `core/` cannot
|
|
29
|
+
* default it — it is timer-free by lint, which is precisely why waiting goes through the `Clock` seam — so the
|
|
30
|
+
* flavor package supplies it, exactly as it supplies the codec. A caller who passes their own clock (a virtual
|
|
31
|
+
* one in a simulation, say) keeps it.
|
|
32
|
+
*/
|
|
25
33
|
declare const bulkLoadCrbmGeneration: BulkLoad;
|
|
26
34
|
/** {@link coreCompactSegment} with the roaring codec pre-bound. */
|
|
27
35
|
declare const compactSegment: CompactSegment;
|
|
@@ -191,9 +199,18 @@ interface CloudRoaringOptions {
|
|
|
191
199
|
*/
|
|
192
200
|
readonly warmReadConsistency?: 'strong' | 'eventual';
|
|
193
201
|
/**
|
|
194
|
-
* Max Warm chunk writes in flight per `addMany`/`removeMany` (the bounded flusher). Default **
|
|
195
|
-
*
|
|
196
|
-
*
|
|
202
|
+
* Max Warm chunk writes in flight per `addMany`/`removeMany` (the bounded flusher). Default **4**
|
|
203
|
+
* (`DEFAULT_WRITE_CONCURRENCY`); set it to `1` for strictly serial writes.
|
|
204
|
+
*
|
|
205
|
+
* Distinct chunks are independent OCC rows, so fanning them out cannot make them conflict with each other.
|
|
206
|
+
* The bound exists for the *backend*: a provisioned-capacity store answers a burst by throttling, and 4 stays
|
|
207
|
+
* comfortably inside what the transient-retry path absorbs (measured 8x below the first observed failure).
|
|
208
|
+
* Raise it if your backend is provisioned for the fan-out — on-demand DynamoDB, or a connection pool sized to
|
|
209
|
+
* match. A pool *smaller* than this needs no action: `pg` and `mysql2` both queue rather than reject, so the
|
|
210
|
+
* flusher simply degrades toward serial.
|
|
211
|
+
*
|
|
212
|
+
* As before, a mid-flush failure can leave a **partial result**; `addMany`/`removeMany` are not atomic. What
|
|
213
|
+
* concurrency changes is only how much of the batch may already have landed when the first error surfaces.
|
|
197
214
|
*/
|
|
198
215
|
readonly writeConcurrency?: number;
|
|
199
216
|
/**
|
|
@@ -402,6 +419,29 @@ declare class CloudRoaring {
|
|
|
402
419
|
* applied one chunk at a time, so if a later chunk fails (e.g. {@link WriteConflictError} after retries)
|
|
403
420
|
* earlier chunks are already applied. Within a single chunk the update is atomic.
|
|
404
421
|
*/
|
|
422
|
+
/** Options common to every chunk-aligned combine (`intersect` / `union` / `andNot`). */
|
|
423
|
+
interface BaseCombineOptions {
|
|
424
|
+
/** Max chunk keys resolved concurrently — bounds the Cold footprint. A positive integer. */
|
|
425
|
+
readonly concurrency?: number;
|
|
426
|
+
/** Override the store's per-op denial-of-wallet budget for this call (`false` lifts it). */
|
|
427
|
+
readonly budget?: BudgetOption;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* A combine that can also subtract.
|
|
431
|
+
*
|
|
432
|
+
* `exclude` is why suppression does not need an intermediate segment: applied here it folds into the same
|
|
433
|
+
* chunk-aligned pass, and each exclude is read **only at the keys that survived** — so a large global opt-out
|
|
434
|
+
* list costs reads proportional to the audience, not to itself.
|
|
435
|
+
*/
|
|
436
|
+
interface CombineOptions extends BaseCombineOptions {
|
|
437
|
+
/** Segments whose ids are subtracted from the result. */
|
|
438
|
+
readonly exclude?: Segment[];
|
|
439
|
+
}
|
|
440
|
+
/** {@link CombineOptions} plus the write batching used by the `*Into` variants. */
|
|
441
|
+
interface CombineIntoOptions extends CombineOptions {
|
|
442
|
+
/** Ids buffered per `addMany` while materializing into `dest`. */
|
|
443
|
+
readonly batchSize?: number;
|
|
444
|
+
}
|
|
405
445
|
declare class Segment {
|
|
406
446
|
private readonly engine;
|
|
407
447
|
private readonly ref;
|
|
@@ -416,12 +456,24 @@ declare class Segment {
|
|
|
416
456
|
private timed;
|
|
417
457
|
/** Add an id (integer in `[0, 2^32)`). Throws {@link ValidationError} on a bad id. */
|
|
418
458
|
add(id: number): Promise<void>;
|
|
419
|
-
/**
|
|
420
|
-
|
|
459
|
+
/**
|
|
460
|
+
* Add many ids, from a **sync or async** iterable. Not atomic across chunks — see the class note.
|
|
461
|
+
*
|
|
462
|
+
* An async source means a database cursor streams straight in — `addMany(athenaCursor())` rather than
|
|
463
|
+
* hand-batching `page → addMany(page)`. Ids are grouped by chunk and each chunk is written **exactly once**,
|
|
464
|
+
* however long the stream: pending ids are held compressed, not buffered-and-flushed, so a long stream costs
|
|
465
|
+
* no more backend writes than a short one. Peak memory is the roaring representation of the ids you pass.
|
|
466
|
+
*
|
|
467
|
+
* **For a very large set, reach for `bulkLoadCrbmGeneration` instead.** `addMany` expresses a *delta* and
|
|
468
|
+
* writes one warm row per touched chunk — around 61,000 of them for a set spread across the whole id space.
|
|
469
|
+
* Bulk-load *replaces* the segment with one immutable Cold object. See the guide's cost comparison; picking
|
|
470
|
+
* the wrong one of these is the most expensive mistake available in this library.
|
|
471
|
+
*/
|
|
472
|
+
addMany(ids: Iterable<number> | AsyncIterable<number>): Promise<void>;
|
|
421
473
|
/** Remove an id (integer in `[0, 2^32)`). Throws {@link ValidationError} on a bad id. */
|
|
422
474
|
remove(id: number): Promise<void>;
|
|
423
|
-
/** Remove many ids. Not atomic across chunks — see
|
|
424
|
-
removeMany(ids: Iterable<number>): Promise<void>;
|
|
475
|
+
/** Remove many ids, from a **sync or async** iterable. Not atomic across chunks — see {@link addMany}. */
|
|
476
|
+
removeMany(ids: Iterable<number> | AsyncIterable<number>): Promise<void>;
|
|
425
477
|
has(id: number): Promise<boolean>;
|
|
426
478
|
count(): Promise<number>;
|
|
427
479
|
iterate(): AsyncIterable<number>;
|
|
@@ -434,16 +486,44 @@ declare class Segment {
|
|
|
434
486
|
* up front, so total memory also carries each operand's Warm size — negligible under Topology-A.) Pass
|
|
435
487
|
* `budget` to override the store's per-op denial-of-wallet budget for this call (or `false` to lift it).
|
|
436
488
|
*/
|
|
437
|
-
intersect(others: Segment[], options?: {
|
|
438
|
-
concurrency?: number;
|
|
439
|
-
budget?: BudgetOption;
|
|
440
|
-
}): AsyncIterable<number>;
|
|
441
489
|
/**
|
|
442
|
-
*
|
|
443
|
-
*
|
|
490
|
+
* Map the facade's `Segment` handles in `exclude` down to the plain refs `core` takes. A method rather than
|
|
491
|
+
* a module function because `ref` is class-private — the encapsulation is worth more than the free function.
|
|
444
492
|
*/
|
|
445
|
-
|
|
446
|
-
|
|
493
|
+
private refsIn;
|
|
494
|
+
intersect(others: Segment[], options?: CombineOptions): AsyncIterable<number>;
|
|
495
|
+
/**
|
|
496
|
+
* Materialize `this ∩ others…` (minus `exclude`) **into** `dest` (added, not replaced). Streaming +
|
|
497
|
+
* bounded-memory, but **not atomic** across chunks — see {@link addMany}.
|
|
498
|
+
*/
|
|
499
|
+
intersectInto(dest: Segment, others: Segment[], options?: CombineIntoOptions): Promise<void>;
|
|
500
|
+
/**
|
|
501
|
+
* `this ∪ others…`, minus `options.exclude` — streamed ascending.
|
|
502
|
+
*
|
|
503
|
+
* **The one composite read with no chunk-skipping,** and that is inherent to union rather than a limitation
|
|
504
|
+
* here: an id in *any* operand belongs to the result, so every chunk of every operand must be read.
|
|
505
|
+
* `intersect` prunes any key missing from any operand; union has nothing to prune. It is charged against the
|
|
506
|
+
* same per-op budget, so a wide union is refused rather than quietly billed — pass `budget` to raise it
|
|
507
|
+
* deliberately. If you find yourself unioning the same segments on every read, materializing the combined
|
|
508
|
+
* segment once (`unionInto`, or a bulk-load rebuild) is the cheaper shape.
|
|
509
|
+
*/
|
|
510
|
+
union(others: Segment[], options?: CombineOptions): AsyncIterable<number>;
|
|
511
|
+
/** Materialize `this ∪ others…` (minus `exclude`) **into** `dest`. **Not atomic** — see {@link addMany}. */
|
|
512
|
+
unionInto(dest: Segment, others: Segment[], options?: CombineIntoOptions): Promise<void>;
|
|
513
|
+
/**
|
|
514
|
+
* `this \ (excludes…)` — streamed ascending. Suppression on its own.
|
|
515
|
+
*
|
|
516
|
+
* Reads every chunk of `this` (any of them may survive the subtraction) but each exclude **only where it
|
|
517
|
+
* overlaps this segment**, so the cost tracks the segment being filtered rather than the size of the
|
|
518
|
+
* suppression list. Subtracting a 61,000-chunk global opt-out list from a narrow audience costs a handful of
|
|
519
|
+
* reads, not 61,000.
|
|
520
|
+
*
|
|
521
|
+
* To filter the *result of an intersection*, do not chain — pass `exclude` to {@link intersect} instead, so
|
|
522
|
+
* the suppression folds into the same pass rather than materializing an intermediate segment first.
|
|
523
|
+
*/
|
|
524
|
+
andNot(excludes: Segment[], options?: BaseCombineOptions): AsyncIterable<number>;
|
|
525
|
+
/** Materialize `this \ (excludes…)` **into** `dest`. **Not atomic** — see {@link addMany}. */
|
|
526
|
+
andNotInto(dest: Segment, excludes: Segment[], options?: BaseCombineOptions & {
|
|
447
527
|
batchSize?: number;
|
|
448
528
|
}): Promise<void>;
|
|
449
529
|
/**
|
|
@@ -459,6 +539,6 @@ declare class Segment {
|
|
|
459
539
|
}
|
|
460
540
|
|
|
461
541
|
/** Package version marker. Kept in sync with package.json at release. */
|
|
462
|
-
declare const VERSION = "0.
|
|
542
|
+
declare const VERSION = "0.4.0";
|
|
463
543
|
|
|
464
|
-
export { CloudRoaring, type CloudRoaringOptions, type EraseSubjectResult, SafeBitmap, Segment, type SegmentOptions, type SubjectErasureEntry, type SubjectReport, type SubjectSegmentRef, VERSION, bulkLoadCrbmGeneration, compactSegment, roaringCodec, runCompactionCycle, runExport };
|
|
544
|
+
export { type BaseCombineOptions, CloudRoaring, type CloudRoaringOptions, type CombineIntoOptions, type CombineOptions, type EraseSubjectResult, SafeBitmap, Segment, type SegmentOptions, type SubjectErasureEntry, type SubjectReport, type SubjectSegmentRef, VERSION, bulkLoadCrbmGeneration, compactSegment, roaringCodec, runCompactionCycle, runExport };
|
package/dist/index.js
CHANGED
|
@@ -92,12 +92,8 @@ var roaringCodec = {
|
|
|
92
92
|
fromValues: (values) => SafeBitmap.fromValues(values),
|
|
93
93
|
safeDeserialize: (bytes, maxBytes) => SafeBitmap.safeDeserialize(bytes, maxBytes)
|
|
94
94
|
};
|
|
95
|
-
var bulkLoadCrbmGeneration = (driver, key, ids, options = {}) => bulkLoadCrbmGeneration$1(driver, key, ids, { ...options, codec: options.codec ?? roaringCodec });
|
|
96
|
-
var compactSegment = (ref, deps, options) => compactSegment$1(ref, { ...deps, codec: deps.codec ?? roaringCodec }, options);
|
|
97
|
-
var runCompactionCycle = (deps, options) => runCompactionCycle$1({ ...deps, codec: deps.codec ?? roaringCodec }, options);
|
|
98
|
-
var runExport = (reader, registry, sink, options = {}) => runExport$1(reader, registry, sink, { ...options, codec: options.codec ?? roaringCodec });
|
|
99
95
|
|
|
100
|
-
// src/
|
|
96
|
+
// src/system-clock.ts
|
|
101
97
|
var SystemClock = class {
|
|
102
98
|
now() {
|
|
103
99
|
return Date.now();
|
|
@@ -108,7 +104,43 @@ var SystemClock = class {
|
|
|
108
104
|
setTimeout(resolve, ms);
|
|
109
105
|
});
|
|
110
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* `setImmediate`, and it has to be — this is the one place where the choice of primitive is the whole feature.
|
|
109
|
+
*
|
|
110
|
+
* A long CPU-bound loop in `core/` (bulk-load is the one that matters) periodically hands the loop back so a
|
|
111
|
+
* co-resident HTTP server keeps answering. Measured over a 61,035-chunk load, yielding every 1,024 chunks:
|
|
112
|
+
*
|
|
113
|
+
* ```text
|
|
114
|
+
* no yield 568 ms wall 568.0 ms worst event-loop gap
|
|
115
|
+
* sleep(0) 555 ms wall 555.2 ms ← a microtask. Yields NOTHING.
|
|
116
|
+
* setImmediate 569 ms wall 13.8 ms ← free, and 41x less starvation
|
|
117
|
+
* setTimeout(1) 625 ms wall 15.0 ms ← +10% wall for no extra relief
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* `sleep(0)` resolving on a microtask is correct for its own contract and useless for this: microtasks drain
|
|
121
|
+
* before the loop ever advances a phase, so `await sleep(0)` in a tight loop never lets a single pending I/O
|
|
122
|
+
* callback run. `setImmediate` fires in the check phase *after* pending I/O, which is exactly the ordering a
|
|
123
|
+
* server wants — inbound requests are serviced before the loop resumes chewing.
|
|
124
|
+
*
|
|
125
|
+
* A timer is not ref'd here (unlike {@link sleep}): a yield expresses "someone else may go first", never
|
|
126
|
+
* "there is outstanding work" — and `setImmediate` cannot outlive the turn that scheduled it anyway.
|
|
127
|
+
*/
|
|
128
|
+
yieldNow() {
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
setImmediate(resolve);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
111
133
|
};
|
|
134
|
+
var bulkLoadCrbmGeneration = (driver, key, ids, options = {}) => bulkLoadCrbmGeneration$1(driver, key, ids, {
|
|
135
|
+
...options,
|
|
136
|
+
codec: options.codec ?? roaringCodec,
|
|
137
|
+
clock: options.clock ?? new SystemClock()
|
|
138
|
+
});
|
|
139
|
+
var compactSegment = (ref, deps, options) => compactSegment$1(ref, { ...deps, codec: deps.codec ?? roaringCodec }, options);
|
|
140
|
+
var runCompactionCycle = (deps, options) => runCompactionCycle$1({ ...deps, codec: deps.codec ?? roaringCodec }, options);
|
|
141
|
+
var runExport = (reader, registry, sink, options = {}) => runExport$1(reader, registry, sink, { ...options, codec: options.codec ?? roaringCodec });
|
|
142
|
+
|
|
143
|
+
// src/index.ts
|
|
112
144
|
var SystemRng = class {
|
|
113
145
|
next() {
|
|
114
146
|
return Math.random();
|
|
@@ -486,7 +518,19 @@ var Segment = class {
|
|
|
486
518
|
add(id) {
|
|
487
519
|
return this.timed("add", () => this.engine.add(this.ref, id));
|
|
488
520
|
}
|
|
489
|
-
/**
|
|
521
|
+
/**
|
|
522
|
+
* Add many ids, from a **sync or async** iterable. Not atomic across chunks — see the class note.
|
|
523
|
+
*
|
|
524
|
+
* An async source means a database cursor streams straight in — `addMany(athenaCursor())` rather than
|
|
525
|
+
* hand-batching `page → addMany(page)`. Ids are grouped by chunk and each chunk is written **exactly once**,
|
|
526
|
+
* however long the stream: pending ids are held compressed, not buffered-and-flushed, so a long stream costs
|
|
527
|
+
* no more backend writes than a short one. Peak memory is the roaring representation of the ids you pass.
|
|
528
|
+
*
|
|
529
|
+
* **For a very large set, reach for `bulkLoadCrbmGeneration` instead.** `addMany` expresses a *delta* and
|
|
530
|
+
* writes one warm row per touched chunk — around 61,000 of them for a set spread across the whole id space.
|
|
531
|
+
* Bulk-load *replaces* the segment with one immutable Cold object. See the guide's cost comparison; picking
|
|
532
|
+
* the wrong one of these is the most expensive mistake available in this library.
|
|
533
|
+
*/
|
|
490
534
|
addMany(ids) {
|
|
491
535
|
return this.timed("addMany", () => this.engine.addMany(this.ref, ids));
|
|
492
536
|
}
|
|
@@ -494,7 +538,7 @@ var Segment = class {
|
|
|
494
538
|
remove(id) {
|
|
495
539
|
return this.timed("remove", () => this.engine.remove(this.ref, id));
|
|
496
540
|
}
|
|
497
|
-
/** Remove many ids. Not atomic across chunks — see
|
|
541
|
+
/** Remove many ids, from a **sync or async** iterable. Not atomic across chunks — see {@link addMany}. */
|
|
498
542
|
removeMany(ids) {
|
|
499
543
|
return this.timed("removeMany", () => this.engine.removeMany(this.ref, ids));
|
|
500
544
|
}
|
|
@@ -516,17 +560,84 @@ var Segment = class {
|
|
|
516
560
|
* up front, so total memory also carries each operand's Warm size — negligible under Topology-A.) Pass
|
|
517
561
|
* `budget` to override the store's per-op denial-of-wallet budget for this call (or `false` to lift it).
|
|
518
562
|
*/
|
|
563
|
+
/**
|
|
564
|
+
* Map the facade's `Segment` handles in `exclude` down to the plain refs `core` takes. A method rather than
|
|
565
|
+
* a module function because `ref` is class-private — the encapsulation is worth more than the free function.
|
|
566
|
+
*/
|
|
567
|
+
refsIn(options) {
|
|
568
|
+
if (options === void 0) return void 0;
|
|
569
|
+
const { exclude, ...rest } = options;
|
|
570
|
+
return exclude === void 0 ? rest : { ...rest, exclude: exclude.map((o) => o.ref) };
|
|
571
|
+
}
|
|
519
572
|
intersect(others, options) {
|
|
520
|
-
return this.engine.intersect([this.ref, ...others.map((o) => o.ref)], options);
|
|
573
|
+
return this.engine.intersect([this.ref, ...others.map((o) => o.ref)], this.refsIn(options));
|
|
521
574
|
}
|
|
522
575
|
/**
|
|
523
|
-
* Materialize `this ∩ others…` **into** `dest` (added, not replaced). Streaming +
|
|
524
|
-
* **not atomic** across chunks — see {@link addMany}.
|
|
576
|
+
* Materialize `this ∩ others…` (minus `exclude`) **into** `dest` (added, not replaced). Streaming +
|
|
577
|
+
* bounded-memory, but **not atomic** across chunks — see {@link addMany}.
|
|
525
578
|
*/
|
|
526
579
|
intersectInto(dest, others, options) {
|
|
527
580
|
return this.timed(
|
|
528
581
|
"intersectInto",
|
|
529
|
-
() => this.engine.intersectInto(
|
|
582
|
+
() => this.engine.intersectInto(
|
|
583
|
+
dest.ref,
|
|
584
|
+
[this.ref, ...others.map((o) => o.ref)],
|
|
585
|
+
this.refsIn(options)
|
|
586
|
+
)
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* `this ∪ others…`, minus `options.exclude` — streamed ascending.
|
|
591
|
+
*
|
|
592
|
+
* **The one composite read with no chunk-skipping,** and that is inherent to union rather than a limitation
|
|
593
|
+
* here: an id in *any* operand belongs to the result, so every chunk of every operand must be read.
|
|
594
|
+
* `intersect` prunes any key missing from any operand; union has nothing to prune. It is charged against the
|
|
595
|
+
* same per-op budget, so a wide union is refused rather than quietly billed — pass `budget` to raise it
|
|
596
|
+
* deliberately. If you find yourself unioning the same segments on every read, materializing the combined
|
|
597
|
+
* segment once (`unionInto`, or a bulk-load rebuild) is the cheaper shape.
|
|
598
|
+
*/
|
|
599
|
+
union(others, options) {
|
|
600
|
+
return this.engine.union([this.ref, ...others.map((o) => o.ref)], this.refsIn(options));
|
|
601
|
+
}
|
|
602
|
+
/** Materialize `this ∪ others…` (minus `exclude`) **into** `dest`. **Not atomic** — see {@link addMany}. */
|
|
603
|
+
unionInto(dest, others, options) {
|
|
604
|
+
return this.timed(
|
|
605
|
+
"unionInto",
|
|
606
|
+
() => this.engine.unionInto(
|
|
607
|
+
dest.ref,
|
|
608
|
+
[this.ref, ...others.map((o) => o.ref)],
|
|
609
|
+
this.refsIn(options)
|
|
610
|
+
)
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* `this \ (excludes…)` — streamed ascending. Suppression on its own.
|
|
615
|
+
*
|
|
616
|
+
* Reads every chunk of `this` (any of them may survive the subtraction) but each exclude **only where it
|
|
617
|
+
* overlaps this segment**, so the cost tracks the segment being filtered rather than the size of the
|
|
618
|
+
* suppression list. Subtracting a 61,000-chunk global opt-out list from a narrow audience costs a handful of
|
|
619
|
+
* reads, not 61,000.
|
|
620
|
+
*
|
|
621
|
+
* To filter the *result of an intersection*, do not chain — pass `exclude` to {@link intersect} instead, so
|
|
622
|
+
* the suppression folds into the same pass rather than materializing an intermediate segment first.
|
|
623
|
+
*/
|
|
624
|
+
andNot(excludes, options) {
|
|
625
|
+
return this.engine.andNot(
|
|
626
|
+
this.ref,
|
|
627
|
+
excludes.map((o) => o.ref),
|
|
628
|
+
options
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
/** Materialize `this \ (excludes…)` **into** `dest`. **Not atomic** — see {@link addMany}. */
|
|
632
|
+
andNotInto(dest, excludes, options) {
|
|
633
|
+
return this.timed(
|
|
634
|
+
"andNotInto",
|
|
635
|
+
() => this.engine.andNotInto(
|
|
636
|
+
dest.ref,
|
|
637
|
+
this.ref,
|
|
638
|
+
excludes.map((o) => o.ref),
|
|
639
|
+
options
|
|
640
|
+
)
|
|
530
641
|
);
|
|
531
642
|
}
|
|
532
643
|
/**
|
|
@@ -547,7 +658,7 @@ var Segment = class {
|
|
|
547
658
|
});
|
|
548
659
|
}
|
|
549
660
|
};
|
|
550
|
-
var VERSION = "0.
|
|
661
|
+
var VERSION = "0.4.0";
|
|
551
662
|
|
|
552
663
|
export { CloudRoaring, SafeBitmap, Segment, VERSION, bulkLoadCrbmGeneration, compactSegment, roaringCodec, runCompactionCycle, runExport };
|
|
553
664
|
//# sourceMappingURL=index.js.map
|