@tanstack/db-ivm 0.1.13 → 0.1.15
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/README.md +3 -3
- package/dist/cjs/d2.cjs.map +1 -1
- package/dist/cjs/graph.cjs.map +1 -1
- package/dist/cjs/hashing/hash.cjs.map +1 -1
- package/dist/cjs/index.cjs +6 -3
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/indexes.cjs +1 -1
- package/dist/cjs/indexes.cjs.map +1 -1
- package/dist/cjs/multiset.cjs.map +1 -1
- package/dist/cjs/operators/concat.cjs.map +1 -1
- package/dist/cjs/operators/consolidate.cjs.map +1 -1
- package/dist/cjs/operators/count.cjs.map +1 -1
- package/dist/cjs/operators/debug.cjs.map +1 -1
- package/dist/cjs/operators/distinct.cjs.map +1 -1
- package/dist/cjs/operators/filter.cjs.map +1 -1
- package/dist/cjs/operators/filterBy.cjs.map +1 -1
- package/dist/cjs/operators/groupBy.cjs +2 -1
- package/dist/cjs/operators/groupBy.cjs.map +1 -1
- package/dist/cjs/operators/groupedTopKWithFractionalIndex.cjs +116 -0
- package/dist/cjs/operators/groupedTopKWithFractionalIndex.cjs.map +1 -0
- package/dist/cjs/operators/groupedTopKWithFractionalIndex.d.cts +50 -0
- package/dist/cjs/operators/index.d.cts +1 -0
- package/dist/cjs/operators/join.cjs.map +1 -1
- package/dist/cjs/operators/keying.cjs.map +1 -1
- package/dist/cjs/operators/map.cjs.map +1 -1
- package/dist/cjs/operators/negate.cjs.map +1 -1
- package/dist/cjs/operators/orderBy.cjs.map +1 -1
- package/dist/cjs/operators/output.cjs.map +1 -1
- package/dist/cjs/operators/pipe.cjs.map +1 -1
- package/dist/cjs/operators/reduce.cjs.map +1 -1
- package/dist/cjs/operators/tap.cjs.map +1 -1
- package/dist/cjs/operators/topK.cjs.map +1 -1
- package/dist/cjs/operators/topKArray.cjs +137 -0
- package/dist/cjs/operators/topKArray.cjs.map +1 -0
- package/dist/cjs/operators/topKArray.d.cts +59 -0
- package/dist/cjs/operators/topKState.cjs +73 -0
- package/dist/cjs/operators/topKState.cjs.map +1 -0
- package/dist/cjs/operators/topKState.d.cts +35 -0
- package/dist/cjs/operators/topKWithFractionalIndex.cjs +17 -208
- package/dist/cjs/operators/topKWithFractionalIndex.cjs.map +1 -1
- package/dist/cjs/operators/topKWithFractionalIndex.d.cts +4 -43
- package/dist/cjs/operators/topKWithFractionalIndexBTree.d.cts +5 -4
- package/dist/cjs/utils.cjs +21 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +11 -0
- package/dist/esm/d2.js.map +1 -1
- package/dist/esm/graph.js.map +1 -1
- package/dist/esm/hashing/hash.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +7 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes.js +1 -1
- package/dist/esm/indexes.js.map +1 -1
- package/dist/esm/multiset.js.map +1 -1
- package/dist/esm/operators/concat.js.map +1 -1
- package/dist/esm/operators/consolidate.js.map +1 -1
- package/dist/esm/operators/count.js.map +1 -1
- package/dist/esm/operators/debug.js.map +1 -1
- package/dist/esm/operators/distinct.js.map +1 -1
- package/dist/esm/operators/filter.js.map +1 -1
- package/dist/esm/operators/filterBy.js.map +1 -1
- package/dist/esm/operators/groupBy.js +2 -1
- package/dist/esm/operators/groupBy.js.map +1 -1
- package/dist/esm/operators/groupedTopKWithFractionalIndex.d.ts +50 -0
- package/dist/esm/operators/groupedTopKWithFractionalIndex.js +116 -0
- package/dist/esm/operators/groupedTopKWithFractionalIndex.js.map +1 -0
- package/dist/esm/operators/index.d.ts +1 -0
- package/dist/esm/operators/join.js.map +1 -1
- package/dist/esm/operators/keying.js.map +1 -1
- package/dist/esm/operators/map.js.map +1 -1
- package/dist/esm/operators/negate.js.map +1 -1
- package/dist/esm/operators/orderBy.js.map +1 -1
- package/dist/esm/operators/output.js.map +1 -1
- package/dist/esm/operators/pipe.js.map +1 -1
- package/dist/esm/operators/reduce.js.map +1 -1
- package/dist/esm/operators/tap.js.map +1 -1
- package/dist/esm/operators/topK.js.map +1 -1
- package/dist/esm/operators/topKArray.d.ts +59 -0
- package/dist/esm/operators/topKArray.js +137 -0
- package/dist/esm/operators/topKArray.js.map +1 -0
- package/dist/esm/operators/topKState.d.ts +35 -0
- package/dist/esm/operators/topKState.js +73 -0
- package/dist/esm/operators/topKState.js.map +1 -0
- package/dist/esm/operators/topKWithFractionalIndex.d.ts +4 -43
- package/dist/esm/operators/topKWithFractionalIndex.js +16 -207
- package/dist/esm/operators/topKWithFractionalIndex.js.map +1 -1
- package/dist/esm/operators/topKWithFractionalIndexBTree.d.ts +5 -4
- package/dist/esm/utils.d.ts +11 -0
- package/dist/esm/utils.js +22 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +29 -29
- package/src/d2.ts +4 -4
- package/src/graph.ts +6 -6
- package/src/hashing/hash.ts +3 -3
- package/src/hashing/index.ts +2 -2
- package/src/index.ts +5 -4
- package/src/indexes.ts +8 -8
- package/src/multiset.ts +5 -5
- package/src/operators/concat.ts +6 -6
- package/src/operators/consolidate.ts +6 -6
- package/src/operators/count.ts +9 -9
- package/src/operators/debug.ts +8 -8
- package/src/operators/distinct.ts +11 -11
- package/src/operators/filter.ts +8 -8
- package/src/operators/filterBy.ts +7 -7
- package/src/operators/groupBy.ts +24 -23
- package/src/operators/groupedTopKWithFractionalIndex.ts +192 -0
- package/src/operators/index.ts +20 -19
- package/src/operators/join.ts +23 -23
- package/src/operators/keying.ts +4 -4
- package/src/operators/map.ts +8 -8
- package/src/operators/negate.ts +6 -6
- package/src/operators/orderBy.ts +26 -26
- package/src/operators/orderByBTree.ts +7 -7
- package/src/operators/output.ts +9 -9
- package/src/operators/pipe.ts +1 -1
- package/src/operators/reduce.ts +9 -9
- package/src/operators/tap.ts +8 -8
- package/src/operators/topK.ts +8 -8
- package/src/operators/topKArray.ts +255 -0
- package/src/operators/topKState.ts +111 -0
- package/src/operators/topKWithFractionalIndex.ts +36 -381
- package/src/operators/topKWithFractionalIndexBTree.ts +21 -30
- package/src/types.ts +22 -22
- package/src/utils.ts +36 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orderBy.js","sources":["../../../src/operators/orderBy.ts"],"sourcesContent":["import { topK, topKWithIndex } from
|
|
1
|
+
{"version":3,"file":"orderBy.js","sources":["../../../src/operators/orderBy.ts"],"sourcesContent":["import { topK, topKWithIndex } from './topK.js'\nimport { topKWithFractionalIndex } from './topKWithFractionalIndex.js'\nimport { map } from './map.js'\nimport { innerJoin } from './join.js'\nimport { consolidate } from './consolidate.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\n\nexport interface OrderByOptions<Ve> {\n comparator?: (a: Ve, b: Ve) => number\n limit?: number\n offset?: number\n}\n\ntype OrderByWithFractionalIndexOptions<Ve> = OrderByOptions<Ve> & {\n setSizeCallback?: (getSize: () => number) => void\n setWindowFn?: (\n windowFn: (options: { offset?: number; limit?: number }) => void,\n ) => void\n}\n\n/**\n * Orders the elements and limits the number of results, with optional offset\n * This requires a keyed stream, and uses the `topK` operator to order all the elements.\n *\n * @param valueExtractor - A function that extracts the value to order by from the element\n * @param options - An optional object containing comparator, limit and offset properties\n * @returns A piped operator that orders the elements and limits the number of results\n */\nexport function orderBy<T extends KeyValue<unknown, unknown>, Ve = unknown>(\n valueExtractor: (\n value: T extends KeyValue<unknown, infer V> ? V : never,\n ) => Ve,\n options?: OrderByOptions<Ve>,\n) {\n const limit = options?.limit ?? Infinity\n const offset = options?.offset ?? 0\n const comparator =\n options?.comparator ??\n ((a, b) => {\n // Default to JS like ordering\n if (a === b) return 0\n if (a < b) return -1\n return 1\n })\n\n return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {\n type KeyType = T extends KeyValue<infer K, unknown> ? K : never\n\n return stream.pipe(\n map(\n ([key, value]) =>\n [\n null,\n [\n key,\n valueExtractor(\n value as T extends KeyValue<unknown, infer V> ? V : never,\n ),\n ],\n ] as KeyValue<null, [KeyType, Ve]>,\n ),\n topK((a, b) => comparator(a[1], b[1]), { limit, offset }),\n map(([_, [key]]) => [key, null] as KeyValue<KeyType, null>),\n innerJoin(stream),\n map(([key, value]) => {\n return [key, value[1]] as T\n }),\n consolidate(),\n )\n }\n}\n\n/**\n * Orders the elements and limits the number of results, with optional offset and\n * annotates the value with the index.\n * This requires a keyed stream, and uses the `topKWithIndex` operator to order all the elements.\n *\n * @param valueExtractor - A function that extracts the value to order by from the element\n * @param options - An optional object containing comparator, limit and offset properties\n * @returns A piped operator that orders the elements and limits the number of results\n */\nexport function orderByWithIndex<\n T extends KeyValue<unknown, unknown>,\n Ve = unknown,\n>(\n valueExtractor: (\n value: T extends KeyValue<unknown, infer V> ? V : never,\n ) => Ve,\n options?: OrderByOptions<Ve>,\n) {\n const limit = options?.limit ?? Infinity\n const offset = options?.offset ?? 0\n const comparator =\n options?.comparator ??\n ((a, b) => {\n // Default to JS like ordering\n if (a === b) return 0\n if (a < b) return -1\n return 1\n })\n\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<\n KeyValue<\n T extends KeyValue<infer K, unknown> ? K : never,\n [T extends KeyValue<unknown, infer V> ? V : never, number]\n >\n > => {\n type KeyType = T extends KeyValue<infer K, unknown> ? K : never\n type ValueType = T extends KeyValue<unknown, infer V> ? V : never\n\n return stream.pipe(\n map(\n ([key, value]) =>\n [\n null,\n [\n key,\n valueExtractor(\n value as T extends KeyValue<unknown, infer V> ? V : never,\n ),\n ],\n ] as KeyValue<null, [KeyType, Ve]>,\n ),\n topKWithIndex((a, b) => comparator(a[1], b[1]), { limit, offset }),\n map(([_, [[key], index]]) => [key, index] as KeyValue<KeyType, number>),\n innerJoin(stream),\n map(([key, [index, value]]) => {\n return [key, [value, index]] as KeyValue<KeyType, [ValueType, number]>\n }),\n consolidate(),\n )\n }\n}\n\nexport function orderByWithFractionalIndexBase<\n T extends KeyValue<unknown, unknown>,\n Ve = unknown,\n>(\n topKFunction: typeof topKWithFractionalIndex,\n valueExtractor: (\n value: T extends KeyValue<unknown, infer V> ? V : never,\n ) => Ve,\n options?: OrderByWithFractionalIndexOptions<Ve>,\n) {\n type KeyType = T extends KeyValue<infer K, unknown> ? K : never\n type ValueType = T extends KeyValue<unknown, infer V> ? V : never\n\n const limit = options?.limit ?? Infinity\n const offset = options?.offset ?? 0\n const setSizeCallback = options?.setSizeCallback\n const setWindowFn = options?.setWindowFn\n const comparator =\n options?.comparator ??\n ((a, b) => {\n // Default to JS like ordering\n if (a === b) return 0\n if (a < b) return -1\n return 1\n })\n\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<[KeyType, [ValueType, string]]> => {\n return stream.pipe(\n topKFunction(\n (a: ValueType, b: ValueType) =>\n comparator(valueExtractor(a), valueExtractor(b)),\n {\n limit,\n offset,\n setSizeCallback,\n setWindowFn,\n },\n ),\n consolidate(),\n )\n }\n}\n\n/**\n * Orders the elements and limits the number of results, with optional offset and\n * annotates the value with a fractional index.\n * This requires a keyed stream, and uses the `topKWithFractionalIndex` operator to order all the elements.\n *\n * @param valueExtractor - A function that extracts the value to order by from the element\n * @param options - An optional object containing comparator, limit and offset properties\n * @returns A piped operator that orders the elements and limits the number of results\n */\nexport function orderByWithFractionalIndex<\n T extends KeyValue<unknown, unknown>,\n Ve = unknown,\n>(\n valueExtractor: (\n value: T extends KeyValue<unknown, infer V> ? V : never,\n ) => Ve,\n options?: OrderByWithFractionalIndexOptions<Ve>,\n) {\n return orderByWithFractionalIndexBase(\n topKWithFractionalIndex,\n valueExtractor,\n options,\n )\n}\n"],"names":[],"mappings":";;;;;AA4BO,SAAS,QACd,gBAGA,SACA;AACA,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,aACJ,SAAS,eACR,CAAC,GAAG,MAAM;AAET,QAAI,MAAM,EAAG,QAAO;AACpB,QAAI,IAAI,EAAG,QAAO;AAClB,WAAO;AAAA,EACT;AAEF,SAAO,CAAC,WAAiD;AAGvD,WAAO,OAAO;AAAA,MACZ;AAAA,QACE,CAAC,CAAC,KAAK,KAAK,MACV;AAAA,UACE;AAAA,UACA;AAAA,YACE;AAAA,YACA;AAAA,cACE;AAAA,YAAA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MAEJ,KAAK,CAAC,GAAG,MAAM,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,QAAQ;AAAA,MACxD,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,CAA4B;AAAA,MAC1D,UAAU,MAAM;AAAA,MAChB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACpB,eAAO,CAAC,KAAK,MAAM,CAAC,CAAC;AAAA,MACvB,CAAC;AAAA,MACD,YAAA;AAAA,IAAY;AAAA,EAEhB;AACF;AAWO,SAAS,iBAId,gBAGA,SACA;AACA,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,aACJ,SAAS,eACR,CAAC,GAAG,MAAM;AAET,QAAI,MAAM,EAAG,QAAO;AACpB,QAAI,IAAI,EAAG,QAAO;AAClB,WAAO;AAAA,EACT;AAEF,SAAO,CACL,WAMG;AAIH,WAAO,OAAO;AAAA,MACZ;AAAA,QACE,CAAC,CAAC,KAAK,KAAK,MACV;AAAA,UACE;AAAA,UACA;AAAA,YACE;AAAA,YACA;AAAA,cACE;AAAA,YAAA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MAEJ,cAAc,CAAC,GAAG,MAAM,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,QAAQ;AAAA,MACjE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,KAAK,CAA8B;AAAA,MACtE,UAAU,MAAM;AAAA,MAChB,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,MAAM;AAC7B,eAAO,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC;AAAA,MAC7B,CAAC;AAAA,MACD,YAAA;AAAA,IAAY;AAAA,EAEhB;AACF;AAEO,SAAS,+BAId,cACA,gBAGA,SACA;AAIA,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,kBAAkB,SAAS;AACjC,QAAM,cAAc,SAAS;AAC7B,QAAM,aACJ,SAAS,eACR,CAAC,GAAG,MAAM;AAET,QAAI,MAAM,EAAG,QAAO;AACpB,QAAI,IAAI,EAAG,QAAO;AAClB,WAAO;AAAA,EACT;AAEF,SAAO,CACL,WACmD;AACnD,WAAO,OAAO;AAAA,MACZ;AAAA,QACE,CAAC,GAAc,MACb,WAAW,eAAe,CAAC,GAAG,eAAe,CAAC,CAAC;AAAA,QACjD;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA;AAAA,MACF;AAAA,MAEF,YAAA;AAAA,IAAY;AAAA,EAEhB;AACF;AAWO,SAAS,2BAId,gBAGA,SACA;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.js","sources":["../../../src/operators/output.ts"],"sourcesContent":["import { DifferenceStreamWriter, UnaryOperator } from
|
|
1
|
+
{"version":3,"file":"output.js","sources":["../../../src/operators/output.ts"],"sourcesContent":["import { DifferenceStreamWriter, UnaryOperator } from '../graph.js'\nimport { StreamBuilder } from '../d2.js'\nimport type { IStreamBuilder, PipedOperator } from '../types.js'\nimport type { DifferenceStreamReader } from '../graph.js'\nimport type { MultiSet } from '../multiset.js'\n\n/**\n * Operator that outputs the messages in the stream\n */\nexport class OutputOperator<T> extends UnaryOperator<T> {\n #fn: (data: MultiSet<T>) => void\n\n constructor(\n id: number,\n inputA: DifferenceStreamReader<T>,\n outputWriter: DifferenceStreamWriter<T>,\n fn: (data: MultiSet<T>) => void,\n ) {\n super(id, inputA, outputWriter)\n this.#fn = fn\n }\n\n run(): void {\n for (const message of this.inputMessages()) {\n this.#fn(message)\n this.output.sendData(message)\n }\n }\n}\n\n/**\n * Outputs the messages in the stream\n * @param fn - The function to call with each message\n */\nexport function output<T>(\n fn: (data: MultiSet<T>) => void,\n): PipedOperator<T, T> {\n return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {\n const outputStream = new StreamBuilder<T>(\n stream.graph,\n new DifferenceStreamWriter<T>(),\n )\n const operator = new OutputOperator<T>(\n stream.graph.getNextOperatorId(),\n stream.connectReader(),\n outputStream.writer,\n fn,\n )\n stream.graph.addOperator(operator)\n return outputStream\n }\n}\n"],"names":[],"mappings":";;AASO,MAAM,uBAA0B,cAAiB;AAAA,EACtD;AAAA,EAEA,YACE,IACA,QACA,cACA,IACA;AACA,UAAM,IAAI,QAAQ,YAAY;AAC9B,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,MAAY;AACV,eAAW,WAAW,KAAK,iBAAiB;AAC1C,WAAK,IAAI,OAAO;AAChB,WAAK,OAAO,SAAS,OAAO;AAAA,IAC9B;AAAA,EACF;AACF;AAMO,SAAS,OACd,IACqB;AACrB,SAAO,CAAC,WAAiD;AACvD,UAAM,eAAe,IAAI;AAAA,MACvB,OAAO;AAAA,MACP,IAAI,uBAAA;AAAA,IAA0B;AAEhC,UAAM,WAAW,IAAI;AAAA,MACnB,OAAO,MAAM,kBAAA;AAAA,MACb,OAAO,cAAA;AAAA,MACP,aAAa;AAAA,MACb;AAAA,IAAA;AAEF,WAAO,MAAM,YAAY,QAAQ;AACjC,WAAO;AAAA,EACT;AACF;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipe.js","sources":["../../../src/operators/pipe.ts"],"sourcesContent":["import type { IStreamBuilder, PipedOperator } from
|
|
1
|
+
{"version":3,"file":"pipe.js","sources":["../../../src/operators/pipe.ts"],"sourcesContent":["import type { IStreamBuilder, PipedOperator } from '../types.js'\n\n// Don't judge, this is the only way to type this function.\n// rxjs has very similar code to type its pipe function\n// https://github.com/ReactiveX/rxjs/blob/master/packages/rxjs/src/internal/util/pipe.ts\n// We go to 20 operators deep, because surely that's enough for anyone...\n// A user can always split the pipe into multiple pipes to get around this.\nexport function pipe<T, O>(o1: PipedOperator<T, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, O>): PipedOperator<T, O>\n// prettier-ignore\nexport function pipe<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, T20>, o20: PipedOperator<T20, O>): PipedOperator<T, O>\n\n/**\n * Creates a new stream by piping the input stream through a series of operators\n */\nexport function pipe<T>(...operators: Array<PipedOperator<any, any>>) {\n return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {\n return stream.pipe(...operators)\n }\n}\n"],"names":[],"mappings":"AAkDO,SAAS,QAAW,WAA2C;AACpE,SAAO,CAAC,WAAiD;AACvD,WAAO,OAAO,KAAK,GAAG,SAAS;AAAA,EACjC;AACF;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reduce.js","sources":["../../../src/operators/reduce.ts"],"sourcesContent":["import { DifferenceStreamWriter, UnaryOperator } from
|
|
1
|
+
{"version":3,"file":"reduce.js","sources":["../../../src/operators/reduce.ts"],"sourcesContent":["import { DifferenceStreamWriter, UnaryOperator } from '../graph.js'\nimport { StreamBuilder } from '../d2.js'\nimport { MultiSet } from '../multiset.js'\nimport { Index } from '../indexes.js'\nimport type { DifferenceStreamReader } from '../graph.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\n\n/**\n * Base operator for reduction operations (version-free)\n */\nexport class ReduceOperator<K, V1, V2> extends UnaryOperator<[K, V1], [K, V2]> {\n #index = new Index<K, V1>()\n #indexOut = new Index<K, V2>()\n #f: (values: Array<[V1, number]>) => Array<[V2, number]>\n\n constructor(\n id: number,\n inputA: DifferenceStreamReader<[K, V1]>,\n output: DifferenceStreamWriter<[K, V2]>,\n f: (values: Array<[V1, number]>) => Array<[V2, number]>,\n ) {\n super(id, inputA, output)\n this.#f = f\n }\n\n run(): void {\n // Collect all input messages and update the index\n const keysTodo = new Set<K>()\n for (const message of this.inputMessages()) {\n for (const [item, multiplicity] of message.getInner()) {\n const [key, value] = item\n this.#index.addValue(key, [value, multiplicity])\n keysTodo.add(key)\n }\n }\n\n // For each key, compute the reduction and delta\n const result: Array<[[K, V2], number]> = []\n for (const key of keysTodo) {\n const curr = this.#index.get(key)\n const currOut = this.#indexOut.get(key)\n const out = this.#f(curr)\n\n // Create maps for current and previous outputs using values directly as keys\n const newOutputMap = new Map<V2, number>()\n const oldOutputMap = new Map<V2, number>()\n\n // Process new output\n for (const [value, multiplicity] of out) {\n const existing = newOutputMap.get(value) ?? 0\n newOutputMap.set(value, existing + multiplicity)\n }\n\n // Process previous output\n for (const [value, multiplicity] of currOut) {\n const existing = oldOutputMap.get(value) ?? 0\n oldOutputMap.set(value, existing + multiplicity)\n }\n\n // First, emit removals for old values that are no longer present\n for (const [value, multiplicity] of oldOutputMap) {\n if (!newOutputMap.has(value)) {\n // Remove the old value entirely\n result.push([[key, value], -multiplicity])\n this.#indexOut.addValue(key, [value, -multiplicity])\n }\n }\n\n // Then, emit additions for new values that are not present in old\n for (const [value, multiplicity] of newOutputMap) {\n if (!oldOutputMap.has(value)) {\n // Add the new value only if it has non-zero multiplicity\n if (multiplicity !== 0) {\n result.push([[key, value], multiplicity])\n this.#indexOut.addValue(key, [value, multiplicity])\n }\n }\n }\n\n // Finally, emit multiplicity changes for values that were present and are still present\n for (const [value, newMultiplicity] of newOutputMap) {\n const oldMultiplicity = oldOutputMap.get(value)\n if (oldMultiplicity !== undefined) {\n const delta = newMultiplicity - oldMultiplicity\n // Only emit actual changes, i.e. non-zero deltas\n if (delta !== 0) {\n result.push([[key, value], delta])\n this.#indexOut.addValue(key, [value, delta])\n }\n }\n }\n }\n\n if (result.length > 0) {\n this.output.sendData(new MultiSet(result))\n }\n }\n}\n\n/**\n * Reduces the elements in the stream by key (version-free)\n */\nexport function reduce<\n KType extends T extends KeyValue<infer K, infer _V> ? K : never,\n V1Type extends T extends KeyValue<KType, infer V> ? V : never,\n R,\n T,\n>(f: (values: Array<[V1Type, number]>) => Array<[R, number]>) {\n return (stream: IStreamBuilder<T>): IStreamBuilder<KeyValue<KType, R>> => {\n const output = new StreamBuilder<KeyValue<KType, R>>(\n stream.graph,\n new DifferenceStreamWriter<KeyValue<KType, R>>(),\n )\n const operator = new ReduceOperator<KType, V1Type, R>(\n stream.graph.getNextOperatorId(),\n stream.connectReader() as DifferenceStreamReader<KeyValue<KType, V1Type>>,\n output.writer,\n f,\n )\n stream.graph.addOperator(operator)\n return output\n }\n}\n"],"names":[],"mappings":";;;;AAUO,MAAM,uBAAkC,cAAgC;AAAA,EAC7E,SAAS,IAAI,MAAA;AAAA,EACb,YAAY,IAAI,MAAA;AAAA,EAChB;AAAA,EAEA,YACE,IACA,QACA,QACA,GACA;AACA,UAAM,IAAI,QAAQ,MAAM;AACxB,SAAK,KAAK;AAAA,EACZ;AAAA,EAEA,MAAY;AAEV,UAAM,+BAAe,IAAA;AACrB,eAAW,WAAW,KAAK,iBAAiB;AAC1C,iBAAW,CAAC,MAAM,YAAY,KAAK,QAAQ,YAAY;AACrD,cAAM,CAAC,KAAK,KAAK,IAAI;AACrB,aAAK,OAAO,SAAS,KAAK,CAAC,OAAO,YAAY,CAAC;AAC/C,iBAAS,IAAI,GAAG;AAAA,MAClB;AAAA,IACF;AAGA,UAAM,SAAmC,CAAA;AACzC,eAAW,OAAO,UAAU;AAC1B,YAAM,OAAO,KAAK,OAAO,IAAI,GAAG;AAChC,YAAM,UAAU,KAAK,UAAU,IAAI,GAAG;AACtC,YAAM,MAAM,KAAK,GAAG,IAAI;AAGxB,YAAM,mCAAmB,IAAA;AACzB,YAAM,mCAAmB,IAAA;AAGzB,iBAAW,CAAC,OAAO,YAAY,KAAK,KAAK;AACvC,cAAM,WAAW,aAAa,IAAI,KAAK,KAAK;AAC5C,qBAAa,IAAI,OAAO,WAAW,YAAY;AAAA,MACjD;AAGA,iBAAW,CAAC,OAAO,YAAY,KAAK,SAAS;AAC3C,cAAM,WAAW,aAAa,IAAI,KAAK,KAAK;AAC5C,qBAAa,IAAI,OAAO,WAAW,YAAY;AAAA,MACjD;AAGA,iBAAW,CAAC,OAAO,YAAY,KAAK,cAAc;AAChD,YAAI,CAAC,aAAa,IAAI,KAAK,GAAG;AAE5B,iBAAO,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,YAAY,CAAC;AACzC,eAAK,UAAU,SAAS,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC;AAAA,QACrD;AAAA,MACF;AAGA,iBAAW,CAAC,OAAO,YAAY,KAAK,cAAc;AAChD,YAAI,CAAC,aAAa,IAAI,KAAK,GAAG;AAE5B,cAAI,iBAAiB,GAAG;AACtB,mBAAO,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,YAAY,CAAC;AACxC,iBAAK,UAAU,SAAS,KAAK,CAAC,OAAO,YAAY,CAAC;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,CAAC,OAAO,eAAe,KAAK,cAAc;AACnD,cAAM,kBAAkB,aAAa,IAAI,KAAK;AAC9C,YAAI,oBAAoB,QAAW;AACjC,gBAAM,QAAQ,kBAAkB;AAEhC,cAAI,UAAU,GAAG;AACf,mBAAO,KAAK,CAAC,CAAC,KAAK,KAAK,GAAG,KAAK,CAAC;AACjC,iBAAK,UAAU,SAAS,KAAK,CAAC,OAAO,KAAK,CAAC;AAAA,UAC7C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,GAAG;AACrB,WAAK,OAAO,SAAS,IAAI,SAAS,MAAM,CAAC;AAAA,IAC3C;AAAA,EACF;AACF;AAKO,SAAS,OAKd,GAA4D;AAC5D,SAAO,CAAC,WAAkE;AACxE,UAAM,SAAS,IAAI;AAAA,MACjB,OAAO;AAAA,MACP,IAAI,uBAAA;AAAA,IAA2C;AAEjD,UAAM,WAAW,IAAI;AAAA,MACnB,OAAO,MAAM,kBAAA;AAAA,MACb,OAAO,cAAA;AAAA,MACP,OAAO;AAAA,MACP;AAAA,IAAA;AAEF,WAAO,MAAM,YAAY,QAAQ;AACjC,WAAO;AAAA,EACT;AACF;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tap.js","sources":["../../../src/operators/tap.ts"],"sourcesContent":["import { DifferenceStreamWriter, LinearUnaryOperator } from
|
|
1
|
+
{"version":3,"file":"tap.js","sources":["../../../src/operators/tap.ts"],"sourcesContent":["import { DifferenceStreamWriter, LinearUnaryOperator } from '../graph.js'\nimport { StreamBuilder } from '../d2.js'\nimport type { IStreamBuilder, PipedOperator } from '../types.js'\nimport type { DifferenceStreamReader } from '../graph.js'\nimport type { MultiSet } from '../multiset.js'\n\n/**\n * Operator that applies a function to each multi-set in the input stream\n */\nexport class TapOperator<T> extends LinearUnaryOperator<T, T> {\n #f: (data: MultiSet<T>) => void\n\n constructor(\n id: number,\n inputA: DifferenceStreamReader<T>,\n output: DifferenceStreamWriter<T>,\n f: (data: MultiSet<T>) => void,\n ) {\n super(id, inputA, output)\n this.#f = f\n }\n\n inner(collection: MultiSet<T>): MultiSet<T> {\n this.#f(collection)\n return collection\n }\n}\n\n/**\n * Invokes a function for each multi-set in the input stream.\n * This operator doesn't modify the stream and is used to perform side effects.\n * @param f - The function to invoke on each multi-set\n * @returns The input stream\n */\nexport function tap<T>(f: (data: MultiSet<T>) => void): PipedOperator<T, T> {\n return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {\n const output = new StreamBuilder<T>(\n stream.graph,\n new DifferenceStreamWriter<T>(),\n )\n const operator = new TapOperator<T>(\n stream.graph.getNextOperatorId(),\n stream.connectReader(),\n output.writer,\n f,\n )\n stream.graph.addOperator(operator)\n return output\n }\n}\n"],"names":[],"mappings":";;AASO,MAAM,oBAAuB,oBAA0B;AAAA,EAC5D;AAAA,EAEA,YACE,IACA,QACA,QACA,GACA;AACA,UAAM,IAAI,QAAQ,MAAM;AACxB,SAAK,KAAK;AAAA,EACZ;AAAA,EAEA,MAAM,YAAsC;AAC1C,SAAK,GAAG,UAAU;AAClB,WAAO;AAAA,EACT;AACF;AAQO,SAAS,IAAO,GAAqD;AAC1E,SAAO,CAAC,WAAiD;AACvD,UAAM,SAAS,IAAI;AAAA,MACjB,OAAO;AAAA,MACP,IAAI,uBAAA;AAAA,IAA0B;AAEhC,UAAM,WAAW,IAAI;AAAA,MACnB,OAAO,MAAM,kBAAA;AAAA,MACb,OAAO,cAAA;AAAA,MACP,OAAO;AAAA,MACP;AAAA,IAAA;AAEF,WAAO,MAAM,YAAY,QAAQ;AACjC,WAAO;AAAA,EACT;AACF;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topK.js","sources":["../../../src/operators/topK.ts"],"sourcesContent":["import { MultiSet } from
|
|
1
|
+
{"version":3,"file":"topK.js","sources":["../../../src/operators/topK.ts"],"sourcesContent":["import { MultiSet } from '../multiset.js'\nimport { reduce } from './reduce.js'\nimport type { IStreamBuilder, KeyValue, PipedOperator } from '../types'\n\ninterface TopKOptions {\n limit?: number\n offset?: number\n}\n\n/**\n * Limits the number of results based on a comparator, with optional offset.\n * This works on a keyed stream, where the key is the first element of the tuple\n * The ordering is withing a key group, i.e. elements are sorted within a key group\n * and the limit + offset is applied to that sorted group.\n * To order the entire stream, key by the same value for all elements such as null.\n *\n * @param comparator - A function that compares two elements\n * @param options - An optional object containing limit and offset properties\n * @returns A piped operator that limits the number of results\n */\nexport function topK<\n KType extends T extends KeyValue<infer K, infer _V> ? K : never,\n V1Type extends T extends KeyValue<KType, infer V> ? V : never,\n T,\n>(\n comparator: (a: V1Type, b: V1Type) => number,\n options?: TopKOptions,\n): PipedOperator<T, T> {\n const limit = options?.limit ?? Infinity\n const offset = options?.offset ?? 0\n\n return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {\n const reduced = stream.pipe(\n reduce((values) => {\n // `values` is a list of tuples, first element is the value, second is the multiplicity\n const consolidated = new MultiSet(values).consolidate()\n const sortedValues = consolidated\n .getInner()\n .sort((a, b) => comparator(a[0] as V1Type, b[0] as V1Type))\n return sortedValues.slice(offset, offset + limit)\n }),\n )\n return reduced as IStreamBuilder<T>\n }\n}\n\n/**\n * Limits the number of results based on a comparator, with optional offset.\n * This works on a keyed stream, where the key is the first element of the tuple\n * The ordering is withing a key group, i.e. elements are sorted within a key group\n * and the limit + offset is applied to that sorted group.\n * To order the entire stream, key by the same value for all elements such as null.\n * Adds the index of the element to the result as [key, [value, index]]\n *\n * @param comparator - A function that compares two elements\n * @param options - An optional object containing limit and offset properties\n * @returns A piped operator that orders the elements and limits the number of results\n */\nexport function topKWithIndex<\n KType extends T extends KeyValue<infer K, infer _V> ? K : never,\n V1Type extends T extends KeyValue<KType, infer V> ? V : never,\n T,\n>(\n comparator: (a: V1Type, b: V1Type) => number,\n options?: TopKOptions,\n): PipedOperator<T, KeyValue<KType, [V1Type, number]>> {\n const limit = options?.limit ?? Infinity\n const offset = options?.offset ?? 0\n\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<KeyValue<KType, [V1Type, number]>> => {\n const reduced = stream.pipe(\n reduce<KType, V1Type, [V1Type, number], T>((values) => {\n // `values` is a list of tuples, first element is the value, second is the multiplicity\n const consolidated = new MultiSet(values).consolidate()\n let i = offset\n const sortedValues = consolidated\n .getInner()\n .sort((a, b) => comparator(a[0], b[0]))\n .slice(offset, offset + limit)\n .map(([value, multiplicity]): [[V1Type, number], number] => [\n [value, i++],\n multiplicity,\n ])\n return sortedValues\n }),\n )\n return reduced\n }\n}\n"],"names":[],"mappings":";;AAoBO,SAAS,KAKd,YACA,SACqB;AACrB,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,SAAS,SAAS,UAAU;AAElC,SAAO,CAAC,WAAiD;AACvD,UAAM,UAAU,OAAO;AAAA,MACrB,OAAO,CAAC,WAAW;AAEjB,cAAM,eAAe,IAAI,SAAS,MAAM,EAAE,YAAA;AAC1C,cAAM,eAAe,aAClB,SAAA,EACA,KAAK,CAAC,GAAG,MAAM,WAAW,EAAE,CAAC,GAAa,EAAE,CAAC,CAAW,CAAC;AAC5D,eAAO,aAAa,MAAM,QAAQ,SAAS,KAAK;AAAA,MAClD,CAAC;AAAA,IAAA;AAEH,WAAO;AAAA,EACT;AACF;AAcO,SAAS,cAKd,YACA,SACqD;AACrD,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,SAAS,SAAS,UAAU;AAElC,SAAO,CACL,WACsD;AACtD,UAAM,UAAU,OAAO;AAAA,MACrB,OAA2C,CAAC,WAAW;AAErD,cAAM,eAAe,IAAI,SAAS,MAAM,EAAE,YAAA;AAC1C,YAAI,IAAI;AACR,cAAM,eAAe,aAClB,SAAA,EACA,KAAK,CAAC,GAAG,MAAM,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EACrC,MAAM,QAAQ,SAAS,KAAK,EAC5B,IAAI,CAAC,CAAC,OAAO,YAAY,MAAkC;AAAA,UAC1D,CAAC,OAAO,GAAG;AAAA,UACX;AAAA,QAAA,CACD;AACH,eAAO;AAAA,MACT,CAAC;AAAA,IAAA;AAEH,WAAO;AAAA,EACT;AACF;"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type FractionalIndex = string;
|
|
2
|
+
export type IndexedValue<V> = [V, FractionalIndex];
|
|
3
|
+
export declare function indexedValue<V>(value: V, index: FractionalIndex): IndexedValue<V>;
|
|
4
|
+
export declare function getValue<V>(indexedVal: IndexedValue<V>): V;
|
|
5
|
+
export declare function getIndex<V>(indexedVal: IndexedValue<V>): FractionalIndex;
|
|
6
|
+
/**
|
|
7
|
+
* Creates a comparator for [key, value] tuples that first compares values,
|
|
8
|
+
* then uses the row key as a stable tie-breaker.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createKeyedComparator<K extends string | number, T>(comparator: (a: T, b: T) => number): (a: [K, T], b: [K, T]) => number;
|
|
11
|
+
export type TopKChanges<V> = {
|
|
12
|
+
/** Indicates which element moves into the topK (if any) */
|
|
13
|
+
moveIn: IndexedValue<V> | null;
|
|
14
|
+
/** Indicates which element moves out of the topK (if any) */
|
|
15
|
+
moveOut: IndexedValue<V> | null;
|
|
16
|
+
};
|
|
17
|
+
export type TopKMoveChanges<V> = {
|
|
18
|
+
/** Flag that marks whether there were any changes to the topK */
|
|
19
|
+
changes: boolean;
|
|
20
|
+
/** Indicates which elements move into the topK (if any) */
|
|
21
|
+
moveIns: Array<IndexedValue<V>>;
|
|
22
|
+
/** Indicates which elements move out of the topK (if any) */
|
|
23
|
+
moveOuts: Array<IndexedValue<V>>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A topK data structure that supports insertions and deletions
|
|
27
|
+
* and returns changes to the topK.
|
|
28
|
+
*/
|
|
29
|
+
export interface TopK<V> {
|
|
30
|
+
size: number;
|
|
31
|
+
insert: (value: V) => TopKChanges<V>;
|
|
32
|
+
delete: (value: V) => TopKChanges<V>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Implementation of a topK data structure.
|
|
36
|
+
* Uses a sorted array internally to store the values and keeps a topK window over that array.
|
|
37
|
+
* Inserts and deletes are O(n) operations because worst case an element is inserted/deleted
|
|
38
|
+
* at the start of the array which causes all the elements to shift to the right/left.
|
|
39
|
+
*/
|
|
40
|
+
export declare class TopKArray<V> implements TopK<V> {
|
|
41
|
+
#private;
|
|
42
|
+
constructor(offset: number, limit: number, comparator: (a: V, b: V) => number);
|
|
43
|
+
get size(): number;
|
|
44
|
+
/**
|
|
45
|
+
* Moves the topK window
|
|
46
|
+
*/
|
|
47
|
+
move({ offset, limit, }: {
|
|
48
|
+
offset?: number;
|
|
49
|
+
limit?: number;
|
|
50
|
+
}): TopKMoveChanges<V>;
|
|
51
|
+
insert(value: V): TopKChanges<V>;
|
|
52
|
+
/**
|
|
53
|
+
* Deletes a value that may or may not be in the topK.
|
|
54
|
+
* IMPORTANT: this assumes that the value is present in the collection
|
|
55
|
+
* if it's not the case it will remove the element
|
|
56
|
+
* that is on the position where the provided `value` would be.
|
|
57
|
+
*/
|
|
58
|
+
delete(value: V): TopKChanges<V>;
|
|
59
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { generateKeyBetween } from "fractional-indexing";
|
|
2
|
+
import { compareKeys, diffHalfOpen, binarySearch } from "../utils.js";
|
|
3
|
+
function indexedValue(value, index) {
|
|
4
|
+
return [value, index];
|
|
5
|
+
}
|
|
6
|
+
function getValue(indexedVal) {
|
|
7
|
+
return indexedVal[0];
|
|
8
|
+
}
|
|
9
|
+
function getIndex(indexedVal) {
|
|
10
|
+
return indexedVal[1];
|
|
11
|
+
}
|
|
12
|
+
function createKeyedComparator(comparator) {
|
|
13
|
+
return ([aKey, aVal], [bKey, bVal]) => {
|
|
14
|
+
const valueComparison = comparator(aVal, bVal);
|
|
15
|
+
if (valueComparison !== 0) {
|
|
16
|
+
return valueComparison;
|
|
17
|
+
}
|
|
18
|
+
return compareKeys(aKey, bKey);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
class TopKArray {
|
|
22
|
+
#sortedValues = [];
|
|
23
|
+
#comparator;
|
|
24
|
+
#topKStart;
|
|
25
|
+
#topKEnd;
|
|
26
|
+
constructor(offset, limit, comparator) {
|
|
27
|
+
this.#topKStart = offset;
|
|
28
|
+
this.#topKEnd = offset + limit;
|
|
29
|
+
this.#comparator = comparator;
|
|
30
|
+
}
|
|
31
|
+
get size() {
|
|
32
|
+
const offset = this.#topKStart;
|
|
33
|
+
const limit = this.#topKEnd - this.#topKStart;
|
|
34
|
+
const available = this.#sortedValues.length - offset;
|
|
35
|
+
return Math.max(0, Math.min(limit, available));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Moves the topK window
|
|
39
|
+
*/
|
|
40
|
+
move({
|
|
41
|
+
offset,
|
|
42
|
+
limit
|
|
43
|
+
}) {
|
|
44
|
+
const oldOffset = this.#topKStart;
|
|
45
|
+
const oldLimit = this.#topKEnd - this.#topKStart;
|
|
46
|
+
const oldRange = [
|
|
47
|
+
this.#topKStart,
|
|
48
|
+
this.#topKEnd === Infinity ? this.#topKStart + this.size : this.#topKEnd
|
|
49
|
+
];
|
|
50
|
+
this.#topKStart = offset ?? oldOffset;
|
|
51
|
+
this.#topKEnd = this.#topKStart + (limit ?? oldLimit);
|
|
52
|
+
const newRange = [
|
|
53
|
+
this.#topKStart,
|
|
54
|
+
this.#topKEnd === Infinity ? Math.max(this.#topKStart + this.size, oldRange[1]) : this.#topKEnd
|
|
55
|
+
];
|
|
56
|
+
const { onlyInA, onlyInB } = diffHalfOpen(oldRange, newRange);
|
|
57
|
+
const moveIns = [];
|
|
58
|
+
onlyInB.forEach((index) => {
|
|
59
|
+
const value = this.#sortedValues[index];
|
|
60
|
+
if (value) {
|
|
61
|
+
moveIns.push(value);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const moveOuts = [];
|
|
65
|
+
onlyInA.forEach((index) => {
|
|
66
|
+
const value = this.#sortedValues[index];
|
|
67
|
+
if (value) {
|
|
68
|
+
moveOuts.push(value);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return { moveIns, moveOuts, changes: onlyInA.length + onlyInB.length > 0 };
|
|
72
|
+
}
|
|
73
|
+
insert(value) {
|
|
74
|
+
const result = { moveIn: null, moveOut: null };
|
|
75
|
+
const index = this.#findIndex(value);
|
|
76
|
+
const indexBefore = index === 0 ? null : getIndex(this.#sortedValues[index - 1]);
|
|
77
|
+
const indexAfter = index === this.#sortedValues.length ? null : getIndex(this.#sortedValues[index]);
|
|
78
|
+
const fractionalIndex = generateKeyBetween(indexBefore, indexAfter);
|
|
79
|
+
const val = indexedValue(value, fractionalIndex);
|
|
80
|
+
this.#sortedValues.splice(index, 0, val);
|
|
81
|
+
if (index < this.#topKEnd) {
|
|
82
|
+
const moveInIndex = Math.max(index, this.#topKStart);
|
|
83
|
+
if (moveInIndex < this.#sortedValues.length) {
|
|
84
|
+
result.moveIn = this.#sortedValues[moveInIndex];
|
|
85
|
+
if (this.#topKEnd < this.#sortedValues.length) {
|
|
86
|
+
result.moveOut = this.#sortedValues[this.#topKEnd];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Deletes a value that may or may not be in the topK.
|
|
94
|
+
* IMPORTANT: this assumes that the value is present in the collection
|
|
95
|
+
* if it's not the case it will remove the element
|
|
96
|
+
* that is on the position where the provided `value` would be.
|
|
97
|
+
*/
|
|
98
|
+
delete(value) {
|
|
99
|
+
const result = { moveIn: null, moveOut: null };
|
|
100
|
+
const index = this.#findIndex(value);
|
|
101
|
+
const [removedElem] = this.#sortedValues.splice(index, 1);
|
|
102
|
+
if (index < this.#topKEnd) {
|
|
103
|
+
result.moveOut = removedElem;
|
|
104
|
+
if (index < this.#topKStart) {
|
|
105
|
+
const moveOutIndex = this.#topKStart - 1;
|
|
106
|
+
if (moveOutIndex < this.#sortedValues.length) {
|
|
107
|
+
result.moveOut = this.#sortedValues[moveOutIndex];
|
|
108
|
+
} else {
|
|
109
|
+
result.moveOut = null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const moveInIndex = this.#topKEnd - 1;
|
|
113
|
+
if (moveInIndex < this.#sortedValues.length) {
|
|
114
|
+
result.moveIn = this.#sortedValues[moveInIndex];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
// TODO: see if there is a way to refactor the code for insert and delete in the topK above
|
|
120
|
+
// because they are very similar, one is shifting the topK window to the left and the other is shifting it to the right
|
|
121
|
+
// so i have the feeling there is a common pattern here and we can implement both cases using that pattern
|
|
122
|
+
#findIndex(value) {
|
|
123
|
+
return binarySearch(
|
|
124
|
+
this.#sortedValues,
|
|
125
|
+
indexedValue(value, ``),
|
|
126
|
+
(a, b) => this.#comparator(getValue(a), getValue(b))
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
TopKArray,
|
|
132
|
+
createKeyedComparator,
|
|
133
|
+
getIndex,
|
|
134
|
+
getValue,
|
|
135
|
+
indexedValue
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=topKArray.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topKArray.js","sources":["../../../src/operators/topKArray.ts"],"sourcesContent":["import { generateKeyBetween } from 'fractional-indexing'\nimport { binarySearch, compareKeys, diffHalfOpen } from '../utils.js'\nimport type { HRange } from '../utils.js'\n\n// Abstraction for fractionally indexed values\nexport type FractionalIndex = string\nexport type IndexedValue<V> = [V, FractionalIndex]\n\nexport function indexedValue<V>(\n value: V,\n index: FractionalIndex,\n): IndexedValue<V> {\n return [value, index]\n}\n\nexport function getValue<V>(indexedVal: IndexedValue<V>): V {\n return indexedVal[0]\n}\n\nexport function getIndex<V>(indexedVal: IndexedValue<V>): FractionalIndex {\n return indexedVal[1]\n}\n\n/**\n * Creates a comparator for [key, value] tuples that first compares values,\n * then uses the row key as a stable tie-breaker.\n */\nexport function createKeyedComparator<K extends string | number, T>(\n comparator: (a: T, b: T) => number,\n): (a: [K, T], b: [K, T]) => number {\n return ([aKey, aVal], [bKey, bVal]) => {\n // First compare on the value\n const valueComparison = comparator(aVal, bVal)\n if (valueComparison !== 0) {\n return valueComparison\n }\n // If the values are equal, use the row key as tie-breaker\n // This provides stable, deterministic ordering since keys are string | number\n return compareKeys(aKey, bKey)\n }\n}\n\nexport type TopKChanges<V> = {\n /** Indicates which element moves into the topK (if any) */\n moveIn: IndexedValue<V> | null\n /** Indicates which element moves out of the topK (if any) */\n moveOut: IndexedValue<V> | null\n}\n\nexport type TopKMoveChanges<V> = {\n /** Flag that marks whether there were any changes to the topK */\n changes: boolean\n /** Indicates which elements move into the topK (if any) */\n moveIns: Array<IndexedValue<V>>\n /** Indicates which elements move out of the topK (if any) */\n moveOuts: Array<IndexedValue<V>>\n}\n\n/**\n * A topK data structure that supports insertions and deletions\n * and returns changes to the topK.\n */\nexport interface TopK<V> {\n size: number\n insert: (value: V) => TopKChanges<V>\n delete: (value: V) => TopKChanges<V>\n}\n\n/**\n * Implementation of a topK data structure.\n * Uses a sorted array internally to store the values and keeps a topK window over that array.\n * Inserts and deletes are O(n) operations because worst case an element is inserted/deleted\n * at the start of the array which causes all the elements to shift to the right/left.\n */\nexport class TopKArray<V> implements TopK<V> {\n #sortedValues: Array<IndexedValue<V>> = []\n #comparator: (a: V, b: V) => number\n #topKStart: number\n #topKEnd: number\n\n constructor(\n offset: number,\n limit: number,\n comparator: (a: V, b: V) => number,\n ) {\n this.#topKStart = offset\n this.#topKEnd = offset + limit\n this.#comparator = comparator\n }\n\n get size(): number {\n const offset = this.#topKStart\n const limit = this.#topKEnd - this.#topKStart\n const available = this.#sortedValues.length - offset\n return Math.max(0, Math.min(limit, available))\n }\n\n /**\n * Moves the topK window\n */\n move({\n offset,\n limit,\n }: {\n offset?: number\n limit?: number\n }): TopKMoveChanges<V> {\n const oldOffset = this.#topKStart\n const oldLimit = this.#topKEnd - this.#topKStart\n\n // `this.#topKEnd` can be `Infinity` if it has no limit\n // but `diffHalfOpen` expects a finite range\n // so we restrict it to the size of the topK if topKEnd is infinite\n const oldRange: HRange = [\n this.#topKStart,\n this.#topKEnd === Infinity ? this.#topKStart + this.size : this.#topKEnd,\n ]\n\n this.#topKStart = offset ?? oldOffset\n this.#topKEnd = this.#topKStart + (limit ?? oldLimit) // can be `Infinity` if limit is `Infinity`\n\n // Also handle `Infinity` in the newRange\n const newRange: HRange = [\n this.#topKStart,\n this.#topKEnd === Infinity\n ? Math.max(this.#topKStart + this.size, oldRange[1]) // since the new limit is Infinity we need to take everything (so we need to take the biggest (finite) topKEnd)\n : this.#topKEnd,\n ]\n const { onlyInA, onlyInB } = diffHalfOpen(oldRange, newRange)\n\n const moveIns: Array<IndexedValue<V>> = []\n onlyInB.forEach((index) => {\n const value = this.#sortedValues[index]\n if (value) {\n moveIns.push(value)\n }\n })\n\n const moveOuts: Array<IndexedValue<V>> = []\n onlyInA.forEach((index) => {\n const value = this.#sortedValues[index]\n if (value) {\n moveOuts.push(value)\n }\n })\n\n // It could be that there are changes (i.e. moveIns or moveOuts)\n // but that the collection is lazy so we don't have the data yet that needs to move in/out\n // so `moveIns` and `moveOuts` will be empty but `changes` will be true\n // this will tell the caller that it needs to run the graph to load more data\n return { moveIns, moveOuts, changes: onlyInA.length + onlyInB.length > 0 }\n }\n\n insert(value: V): TopKChanges<V> {\n const result: TopKChanges<V> = { moveIn: null, moveOut: null }\n\n // Lookup insert position\n const index = this.#findIndex(value)\n // Generate fractional index based on the fractional indices of the elements before and after it\n const indexBefore =\n index === 0 ? null : getIndex(this.#sortedValues[index - 1]!)\n const indexAfter =\n index === this.#sortedValues.length\n ? null\n : getIndex(this.#sortedValues[index]!)\n const fractionalIndex = generateKeyBetween(indexBefore, indexAfter)\n\n // Insert the value at the correct position\n const val = indexedValue(value, fractionalIndex)\n // Splice is O(n) where n = all elements in the collection (i.e. n >= k) !\n this.#sortedValues.splice(index, 0, val)\n\n // Check if the topK changed\n if (index < this.#topKEnd) {\n // The inserted element is either before the top K or within the top K\n // If it is before the top K then it moves the element that was right before the topK into the topK\n // If it is within the top K then the inserted element moves into the top K\n // In both cases the last element of the old top K now moves out of the top K\n const moveInIndex = Math.max(index, this.#topKStart)\n if (moveInIndex < this.#sortedValues.length) {\n // We actually have a topK\n // because in some cases there may not be enough elements in the array to reach the start of the topK\n // e.g. [1, 2, 3] with K = 2 and offset = 3 does not have a topK\n result.moveIn = this.#sortedValues[moveInIndex]!\n\n // We need to remove the element that falls out of the top K\n // The element that falls out of the top K has shifted one to the right\n // because of the element we inserted, so we find it at index topKEnd\n if (this.#topKEnd < this.#sortedValues.length) {\n result.moveOut = this.#sortedValues[this.#topKEnd]!\n }\n }\n }\n\n return result\n }\n\n /**\n * Deletes a value that may or may not be in the topK.\n * IMPORTANT: this assumes that the value is present in the collection\n * if it's not the case it will remove the element\n * that is on the position where the provided `value` would be.\n */\n delete(value: V): TopKChanges<V> {\n const result: TopKChanges<V> = { moveIn: null, moveOut: null }\n\n // Lookup delete position\n const index = this.#findIndex(value)\n // Remove the value at that position\n const [removedElem] = this.#sortedValues.splice(index, 1)\n\n // Check if the topK changed\n if (index < this.#topKEnd) {\n // The removed element is either before the top K or within the top K\n // If it is before the top K then the first element of the topK moves out of the topK\n // If it is within the top K then the removed element moves out of the topK\n result.moveOut = removedElem!\n if (index < this.#topKStart) {\n // The removed element is before the topK\n // so actually, the first element of the topK moves out of the topK\n // and not the element that we removed\n // The first element of the topK is now at index topKStart - 1\n // since we removed an element before the topK\n const moveOutIndex = this.#topKStart - 1\n if (moveOutIndex < this.#sortedValues.length) {\n result.moveOut = this.#sortedValues[moveOutIndex]!\n } else {\n // No value is moving out of the topK\n // because there are no elements in the topK\n result.moveOut = null\n }\n }\n\n // Since we removed an element that was before or in the topK\n // the first element after the topK moved one position to the left\n // and thus falls into the topK now\n const moveInIndex = this.#topKEnd - 1\n if (moveInIndex < this.#sortedValues.length) {\n result.moveIn = this.#sortedValues[moveInIndex]!\n }\n }\n\n return result\n }\n\n // TODO: see if there is a way to refactor the code for insert and delete in the topK above\n // because they are very similar, one is shifting the topK window to the left and the other is shifting it to the right\n // so i have the feeling there is a common pattern here and we can implement both cases using that pattern\n\n #findIndex(value: V): number {\n return binarySearch(this.#sortedValues, indexedValue(value, ``), (a, b) =>\n this.#comparator(getValue(a), getValue(b)),\n )\n }\n}\n"],"names":[],"mappings":";;AAQO,SAAS,aACd,OACA,OACiB;AACjB,SAAO,CAAC,OAAO,KAAK;AACtB;AAEO,SAAS,SAAY,YAAgC;AAC1D,SAAO,WAAW,CAAC;AACrB;AAEO,SAAS,SAAY,YAA8C;AACxE,SAAO,WAAW,CAAC;AACrB;AAMO,SAAS,sBACd,YACkC;AAClC,SAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,MAAM;AAErC,UAAM,kBAAkB,WAAW,MAAM,IAAI;AAC7C,QAAI,oBAAoB,GAAG;AACzB,aAAO;AAAA,IACT;AAGA,WAAO,YAAY,MAAM,IAAI;AAAA,EAC/B;AACF;AAkCO,MAAM,UAAgC;AAAA,EAC3C,gBAAwC,CAAA;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YACE,QACA,OACA,YACA;AACA,SAAK,aAAa;AAClB,SAAK,WAAW,SAAS;AACzB,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,IAAI,OAAe;AACjB,UAAM,SAAS,KAAK;AACpB,UAAM,QAAQ,KAAK,WAAW,KAAK;AACnC,UAAM,YAAY,KAAK,cAAc,SAAS;AAC9C,WAAO,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,SAAS,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK;AAAA,IACH;AAAA,IACA;AAAA,EAAA,GAIqB;AACrB,UAAM,YAAY,KAAK;AACvB,UAAM,WAAW,KAAK,WAAW,KAAK;AAKtC,UAAM,WAAmB;AAAA,MACvB,KAAK;AAAA,MACL,KAAK,aAAa,WAAW,KAAK,aAAa,KAAK,OAAO,KAAK;AAAA,IAAA;AAGlE,SAAK,aAAa,UAAU;AAC5B,SAAK,WAAW,KAAK,cAAc,SAAS;AAG5C,UAAM,WAAmB;AAAA,MACvB,KAAK;AAAA,MACL,KAAK,aAAa,WACd,KAAK,IAAI,KAAK,aAAa,KAAK,MAAM,SAAS,CAAC,CAAC,IACjD,KAAK;AAAA,IAAA;AAEX,UAAM,EAAE,SAAS,QAAA,IAAY,aAAa,UAAU,QAAQ;AAE5D,UAAM,UAAkC,CAAA;AACxC,YAAQ,QAAQ,CAAC,UAAU;AACzB,YAAM,QAAQ,KAAK,cAAc,KAAK;AACtC,UAAI,OAAO;AACT,gBAAQ,KAAK,KAAK;AAAA,MACpB;AAAA,IACF,CAAC;AAED,UAAM,WAAmC,CAAA;AACzC,YAAQ,QAAQ,CAAC,UAAU;AACzB,YAAM,QAAQ,KAAK,cAAc,KAAK;AACtC,UAAI,OAAO;AACT,iBAAS,KAAK,KAAK;AAAA,MACrB;AAAA,IACF,CAAC;AAMD,WAAO,EAAE,SAAS,UAAU,SAAS,QAAQ,SAAS,QAAQ,SAAS,EAAA;AAAA,EACzE;AAAA,EAEA,OAAO,OAA0B;AAC/B,UAAM,SAAyB,EAAE,QAAQ,MAAM,SAAS,KAAA;AAGxD,UAAM,QAAQ,KAAK,WAAW,KAAK;AAEnC,UAAM,cACJ,UAAU,IAAI,OAAO,SAAS,KAAK,cAAc,QAAQ,CAAC,CAAE;AAC9D,UAAM,aACJ,UAAU,KAAK,cAAc,SACzB,OACA,SAAS,KAAK,cAAc,KAAK,CAAE;AACzC,UAAM,kBAAkB,mBAAmB,aAAa,UAAU;AAGlE,UAAM,MAAM,aAAa,OAAO,eAAe;AAE/C,SAAK,cAAc,OAAO,OAAO,GAAG,GAAG;AAGvC,QAAI,QAAQ,KAAK,UAAU;AAKzB,YAAM,cAAc,KAAK,IAAI,OAAO,KAAK,UAAU;AACnD,UAAI,cAAc,KAAK,cAAc,QAAQ;AAI3C,eAAO,SAAS,KAAK,cAAc,WAAW;AAK9C,YAAI,KAAK,WAAW,KAAK,cAAc,QAAQ;AAC7C,iBAAO,UAAU,KAAK,cAAc,KAAK,QAAQ;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,OAA0B;AAC/B,UAAM,SAAyB,EAAE,QAAQ,MAAM,SAAS,KAAA;AAGxD,UAAM,QAAQ,KAAK,WAAW,KAAK;AAEnC,UAAM,CAAC,WAAW,IAAI,KAAK,cAAc,OAAO,OAAO,CAAC;AAGxD,QAAI,QAAQ,KAAK,UAAU;AAIzB,aAAO,UAAU;AACjB,UAAI,QAAQ,KAAK,YAAY;AAM3B,cAAM,eAAe,KAAK,aAAa;AACvC,YAAI,eAAe,KAAK,cAAc,QAAQ;AAC5C,iBAAO,UAAU,KAAK,cAAc,YAAY;AAAA,QAClD,OAAO;AAGL,iBAAO,UAAU;AAAA,QACnB;AAAA,MACF;AAKA,YAAM,cAAc,KAAK,WAAW;AACpC,UAAI,cAAc,KAAK,cAAc,QAAQ;AAC3C,eAAO,SAAS,KAAK,cAAc,WAAW;AAAA,MAChD;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,OAAkB;AAC3B,WAAO;AAAA,MAAa,KAAK;AAAA,MAAe,aAAa,OAAO,EAAE;AAAA,MAAG,CAAC,GAAG,MACnE,KAAK,YAAY,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC;AAAA,IAAA;AAAA,EAE7C;AACF;"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { IndexedValue, TopK, TopKChanges, TopKMoveChanges } from './topKArray.js';
|
|
2
|
+
/**
|
|
3
|
+
* Helper class that manages the state for a single topK window.
|
|
4
|
+
* Encapsulates the multiplicity tracking and topK data structure,
|
|
5
|
+
* providing a clean interface for processing elements and moving the window.
|
|
6
|
+
*
|
|
7
|
+
* This class is used by both TopKWithFractionalIndexOperator (single instance)
|
|
8
|
+
* and GroupedTopKWithFractionalIndexOperator (one instance per group).
|
|
9
|
+
*/
|
|
10
|
+
export declare class TopKState<K extends string | number, T> {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(topK: TopK<[K, T]>);
|
|
13
|
+
get size(): number;
|
|
14
|
+
get isEmpty(): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Process an element update (insert or delete based on multiplicity change).
|
|
17
|
+
* Returns the changes to the topK window.
|
|
18
|
+
*/
|
|
19
|
+
processElement(key: K, value: T, multiplicity: number): TopKChanges<[K, T]>;
|
|
20
|
+
/**
|
|
21
|
+
* Move the topK window. Only works with TopKArray implementation.
|
|
22
|
+
*/
|
|
23
|
+
move(options: {
|
|
24
|
+
offset?: number;
|
|
25
|
+
limit?: number;
|
|
26
|
+
}): TopKMoveChanges<[K, T]>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Handles a moveIn change by adding it to the result array.
|
|
30
|
+
*/
|
|
31
|
+
export declare function handleMoveIn<K extends string | number, T>(moveIn: IndexedValue<[K, T]> | null, result: Array<[[K, IndexedValue<T>], number]>): void;
|
|
32
|
+
/**
|
|
33
|
+
* Handles a moveOut change by adding it to the result array.
|
|
34
|
+
*/
|
|
35
|
+
export declare function handleMoveOut<K extends string | number, T>(moveOut: IndexedValue<[K, T]> | null, result: Array<[[K, IndexedValue<T>], number]>): void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { TopKArray } from "./topKArray.js";
|
|
2
|
+
class TopKState {
|
|
3
|
+
#multiplicities = /* @__PURE__ */ new Map();
|
|
4
|
+
#topK;
|
|
5
|
+
constructor(topK) {
|
|
6
|
+
this.#topK = topK;
|
|
7
|
+
}
|
|
8
|
+
get size() {
|
|
9
|
+
return this.#topK.size;
|
|
10
|
+
}
|
|
11
|
+
get isEmpty() {
|
|
12
|
+
return this.#multiplicities.size === 0 && this.#topK.size === 0;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Process an element update (insert or delete based on multiplicity change).
|
|
16
|
+
* Returns the changes to the topK window.
|
|
17
|
+
*/
|
|
18
|
+
processElement(key, value, multiplicity) {
|
|
19
|
+
const { oldMultiplicity, newMultiplicity } = this.#updateMultiplicity(
|
|
20
|
+
key,
|
|
21
|
+
multiplicity
|
|
22
|
+
);
|
|
23
|
+
if (oldMultiplicity <= 0 && newMultiplicity > 0) {
|
|
24
|
+
return this.#topK.insert([key, value]);
|
|
25
|
+
} else if (oldMultiplicity > 0 && newMultiplicity <= 0) {
|
|
26
|
+
return this.#topK.delete([key, value]);
|
|
27
|
+
}
|
|
28
|
+
return { moveIn: null, moveOut: null };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Move the topK window. Only works with TopKArray implementation.
|
|
32
|
+
*/
|
|
33
|
+
move(options) {
|
|
34
|
+
if (!(this.#topK instanceof TopKArray)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Cannot move B+-tree implementation of TopK with fractional index`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return this.#topK.move(options);
|
|
40
|
+
}
|
|
41
|
+
#updateMultiplicity(key, multiplicity) {
|
|
42
|
+
if (multiplicity === 0) {
|
|
43
|
+
const current = this.#multiplicities.get(key) ?? 0;
|
|
44
|
+
return { oldMultiplicity: current, newMultiplicity: current };
|
|
45
|
+
}
|
|
46
|
+
const oldMultiplicity = this.#multiplicities.get(key) ?? 0;
|
|
47
|
+
const newMultiplicity = oldMultiplicity + multiplicity;
|
|
48
|
+
if (newMultiplicity === 0) {
|
|
49
|
+
this.#multiplicities.delete(key);
|
|
50
|
+
} else {
|
|
51
|
+
this.#multiplicities.set(key, newMultiplicity);
|
|
52
|
+
}
|
|
53
|
+
return { oldMultiplicity, newMultiplicity };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function handleMoveIn(moveIn, result) {
|
|
57
|
+
if (moveIn) {
|
|
58
|
+
const [[key, value], index] = moveIn;
|
|
59
|
+
result.push([[key, [value, index]], 1]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function handleMoveOut(moveOut, result) {
|
|
63
|
+
if (moveOut) {
|
|
64
|
+
const [[key, value], index] = moveOut;
|
|
65
|
+
result.push([[key, [value, index]], -1]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
TopKState,
|
|
70
|
+
handleMoveIn,
|
|
71
|
+
handleMoveOut
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=topKState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topKState.js","sources":["../../../src/operators/topKState.ts"],"sourcesContent":["import { TopKArray } from './topKArray.js'\nimport type {\n IndexedValue,\n TopK,\n TopKChanges,\n TopKMoveChanges,\n} from './topKArray.js'\n\n/**\n * Helper class that manages the state for a single topK window.\n * Encapsulates the multiplicity tracking and topK data structure,\n * providing a clean interface for processing elements and moving the window.\n *\n * This class is used by both TopKWithFractionalIndexOperator (single instance)\n * and GroupedTopKWithFractionalIndexOperator (one instance per group).\n */\nexport class TopKState<K extends string | number, T> {\n #multiplicities: Map<K, number> = new Map()\n #topK: TopK<[K, T]>\n\n constructor(topK: TopK<[K, T]>) {\n this.#topK = topK\n }\n\n get size(): number {\n return this.#topK.size\n }\n\n get isEmpty(): boolean {\n return this.#multiplicities.size === 0 && this.#topK.size === 0\n }\n\n /**\n * Process an element update (insert or delete based on multiplicity change).\n * Returns the changes to the topK window.\n */\n processElement(key: K, value: T, multiplicity: number): TopKChanges<[K, T]> {\n const { oldMultiplicity, newMultiplicity } = this.#updateMultiplicity(\n key,\n multiplicity,\n )\n\n if (oldMultiplicity <= 0 && newMultiplicity > 0) {\n // The value was invisible but should now be visible\n return this.#topK.insert([key, value])\n } else if (oldMultiplicity > 0 && newMultiplicity <= 0) {\n // The value was visible but should now be invisible\n return this.#topK.delete([key, value])\n }\n // The value was invisible and remains invisible,\n // or was visible and remains visible - no topK change\n return { moveIn: null, moveOut: null }\n }\n\n /**\n * Move the topK window. Only works with TopKArray implementation.\n */\n move(options: { offset?: number; limit?: number }): TopKMoveChanges<[K, T]> {\n if (!(this.#topK instanceof TopKArray)) {\n throw new Error(\n `Cannot move B+-tree implementation of TopK with fractional index`,\n )\n }\n return this.#topK.move(options)\n }\n\n #updateMultiplicity(\n key: K,\n multiplicity: number,\n ): { oldMultiplicity: number; newMultiplicity: number } {\n if (multiplicity === 0) {\n const current = this.#multiplicities.get(key) ?? 0\n return { oldMultiplicity: current, newMultiplicity: current }\n }\n\n const oldMultiplicity = this.#multiplicities.get(key) ?? 0\n const newMultiplicity = oldMultiplicity + multiplicity\n if (newMultiplicity === 0) {\n this.#multiplicities.delete(key)\n } else {\n this.#multiplicities.set(key, newMultiplicity)\n }\n return { oldMultiplicity, newMultiplicity }\n }\n}\n\n/**\n * Handles a moveIn change by adding it to the result array.\n */\nexport function handleMoveIn<K extends string | number, T>(\n moveIn: IndexedValue<[K, T]> | null,\n result: Array<[[K, IndexedValue<T>], number]>,\n): void {\n if (moveIn) {\n const [[key, value], index] = moveIn\n result.push([[key, [value, index]], 1])\n }\n}\n\n/**\n * Handles a moveOut change by adding it to the result array.\n */\nexport function handleMoveOut<K extends string | number, T>(\n moveOut: IndexedValue<[K, T]> | null,\n result: Array<[[K, IndexedValue<T>], number]>,\n): void {\n if (moveOut) {\n const [[key, value], index] = moveOut\n result.push([[key, [value, index]], -1])\n }\n}\n"],"names":[],"mappings":";AAgBO,MAAM,UAAwC;AAAA,EACnD,sCAAsC,IAAA;AAAA,EACtC;AAAA,EAEA,YAAY,MAAoB;AAC9B,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,IAAI,UAAmB;AACrB,WAAO,KAAK,gBAAgB,SAAS,KAAK,KAAK,MAAM,SAAS;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAe,KAAQ,OAAU,cAA2C;AAC1E,UAAM,EAAE,iBAAiB,gBAAA,IAAoB,KAAK;AAAA,MAChD;AAAA,MACA;AAAA,IAAA;AAGF,QAAI,mBAAmB,KAAK,kBAAkB,GAAG;AAE/C,aAAO,KAAK,MAAM,OAAO,CAAC,KAAK,KAAK,CAAC;AAAA,IACvC,WAAW,kBAAkB,KAAK,mBAAmB,GAAG;AAEtD,aAAO,KAAK,MAAM,OAAO,CAAC,KAAK,KAAK,CAAC;AAAA,IACvC;AAGA,WAAO,EAAE,QAAQ,MAAM,SAAS,KAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,SAAuE;AAC1E,QAAI,EAAE,KAAK,iBAAiB,YAAY;AACtC,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AACA,WAAO,KAAK,MAAM,KAAK,OAAO;AAAA,EAChC;AAAA,EAEA,oBACE,KACA,cACsD;AACtD,QAAI,iBAAiB,GAAG;AACtB,YAAM,UAAU,KAAK,gBAAgB,IAAI,GAAG,KAAK;AACjD,aAAO,EAAE,iBAAiB,SAAS,iBAAiB,QAAA;AAAA,IACtD;AAEA,UAAM,kBAAkB,KAAK,gBAAgB,IAAI,GAAG,KAAK;AACzD,UAAM,kBAAkB,kBAAkB;AAC1C,QAAI,oBAAoB,GAAG;AACzB,WAAK,gBAAgB,OAAO,GAAG;AAAA,IACjC,OAAO;AACL,WAAK,gBAAgB,IAAI,KAAK,eAAe;AAAA,IAC/C;AACA,WAAO,EAAE,iBAAiB,gBAAA;AAAA,EAC5B;AACF;AAKO,SAAS,aACd,QACA,QACM;AACN,MAAI,QAAQ;AACV,UAAM,CAAC,CAAC,KAAK,KAAK,GAAG,KAAK,IAAI;AAC9B,WAAO,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC;AAAA,EACxC;AACF;AAKO,SAAS,cACd,SACA,QACM;AACN,MAAI,SAAS;AACX,UAAM,CAAC,CAAC,KAAK,KAAK,GAAG,KAAK,IAAI;AAC9B,WAAO,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,GAAG,EAAE,CAAC;AAAA,EACzC;AACF;"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DifferenceStreamWriter, UnaryOperator, DifferenceStreamReader } from '../graph.js';
|
|
2
|
+
import { IndexedValue, TopK } from './topKArray.js';
|
|
2
3
|
import { PipedOperator } from '../types.js';
|
|
3
4
|
export interface TopKWithFractionalIndexOptions {
|
|
4
5
|
limit?: number;
|
|
@@ -9,44 +10,15 @@ export interface TopKWithFractionalIndexOptions {
|
|
|
9
10
|
limit?: number;
|
|
10
11
|
}) => void) => void;
|
|
11
12
|
}
|
|
12
|
-
export type TopKChanges<V> = {
|
|
13
|
-
/** Indicates which element moves into the topK (if any) */
|
|
14
|
-
moveIn: IndexedValue<V> | null;
|
|
15
|
-
/** Indicates which element moves out of the topK (if any) */
|
|
16
|
-
moveOut: IndexedValue<V> | null;
|
|
17
|
-
};
|
|
18
|
-
export type TopKMoveChanges<V> = {
|
|
19
|
-
/** Flag that marks whether there were any changes to the topK */
|
|
20
|
-
changes: boolean;
|
|
21
|
-
/** Indicates which elements move into the topK (if any) */
|
|
22
|
-
moveIns: Array<IndexedValue<V>>;
|
|
23
|
-
/** Indicates which elements move out of the topK (if any) */
|
|
24
|
-
moveOuts: Array<IndexedValue<V>>;
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* A topK data structure that supports insertions and deletions
|
|
28
|
-
* and returns changes to the topK.
|
|
29
|
-
*/
|
|
30
|
-
export interface TopK<V> {
|
|
31
|
-
size: number;
|
|
32
|
-
insert: (value: V) => TopKChanges<V>;
|
|
33
|
-
delete: (value: V) => TopKChanges<V>;
|
|
34
|
-
}
|
|
35
13
|
/**
|
|
36
14
|
* Operator for fractional indexed topK operations
|
|
37
15
|
* This operator maintains fractional indices for sorted elements
|
|
38
16
|
* and only updates indices when elements move position
|
|
39
17
|
*/
|
|
40
|
-
export declare class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator<[
|
|
41
|
-
K,
|
|
42
|
-
T
|
|
43
|
-
], [
|
|
44
|
-
K,
|
|
45
|
-
IndexedValue<T>
|
|
46
|
-
]> {
|
|
18
|
+
export declare class TopKWithFractionalIndexOperator<K extends string | number, T> extends UnaryOperator<[K, T], [K, IndexedValue<T>]> {
|
|
47
19
|
#private;
|
|
48
20
|
constructor(id: number, inputA: DifferenceStreamReader<[K, T]>, output: DifferenceStreamWriter<[K, IndexedValue<T>]>, comparator: (a: T, b: T) => number, options: TopKWithFractionalIndexOptions);
|
|
49
|
-
protected createTopK(offset: number, limit: number, comparator: (a:
|
|
21
|
+
protected createTopK(offset: number, limit: number, comparator: (a: [K, T], b: [K, T]) => number): TopK<[K, T]>;
|
|
50
22
|
/**
|
|
51
23
|
* Moves the topK window based on the provided offset and limit.
|
|
52
24
|
* Any changes to the topK are sent to the output.
|
|
@@ -57,10 +29,6 @@ export declare class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator
|
|
|
57
29
|
}): void;
|
|
58
30
|
run(): void;
|
|
59
31
|
processElement(key: K, value: T, multiplicity: number, result: Array<[[K, IndexedValue<T>], number]>): void;
|
|
60
|
-
private handleMoveIn;
|
|
61
|
-
private handleMoveOut;
|
|
62
|
-
private getMultiplicity;
|
|
63
|
-
private addKey;
|
|
64
32
|
}
|
|
65
33
|
/**
|
|
66
34
|
* Limits the number of results based on a comparator, with optional offset.
|
|
@@ -72,11 +40,4 @@ export declare class TopKWithFractionalIndexOperator<K, T> extends UnaryOperator
|
|
|
72
40
|
* @param options - An optional object containing limit and offset properties
|
|
73
41
|
* @returns A piped operator that orders the elements and limits the number of results
|
|
74
42
|
*/
|
|
75
|
-
export declare function topKWithFractionalIndex<KType, T>(comparator: (a: T, b: T) => number, options?: TopKWithFractionalIndexOptions): PipedOperator<[KType, T], [KType, IndexedValue<T>]>;
|
|
76
|
-
export type FractionalIndex = string;
|
|
77
|
-
export type IndexedValue<V> = [V, FractionalIndex];
|
|
78
|
-
export declare function indexedValue<V>(value: V, index: FractionalIndex): IndexedValue<V>;
|
|
79
|
-
export declare function getValue<V>(indexedVal: IndexedValue<V>): V;
|
|
80
|
-
export declare function getIndex<V>(indexedVal: IndexedValue<V>): FractionalIndex;
|
|
81
|
-
export type Tag = number;
|
|
82
|
-
export type TaggedValue<K, V> = [K, V, Tag];
|
|
43
|
+
export declare function topKWithFractionalIndex<KType extends string | number, T>(comparator: (a: T, b: T) => number, options?: TopKWithFractionalIndexOptions): PipedOperator<[KType, T], [KType, IndexedValue<T>]>;
|