Skip to content

Commit 101c0bf

Browse files
committed
⚡ InvertedIndexedMap - reduce unnecessary posting
1 parent 3bf2815 commit 101c0bf

File tree

1 file changed

+34
-30
lines changed

1 file changed

+34
-30
lines changed

src/lang.ts

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,16 @@ const EmptySet: IndexSet = {
522522
[Symbol.iterator]: function* () {},
523523
};
524524

525+
const LenIndexSet: (len: number) => IndexSet = (len) => ({
526+
has: (index) => index < len,
527+
size: len,
528+
[Symbol.iterator]: function* () {
529+
for (let i = 0; i < len; i++) {
530+
yield i;
531+
}
532+
},
533+
});
534+
525535
/**
526536
* Converts a sorted array of numbers into an IndexSet data structure.
527537
* sortedArr shouldn't have duplicate elements.
@@ -662,19 +672,6 @@ export class InvertedIndexMap<R extends Record<keyof R, unknown>> {
662672
return this.data.length;
663673
}
664674

665-
private allIndexSet(): IndexSet {
666-
const data = this.data;
667-
return {
668-
has: (index) => index < data.length,
669-
size: data.length,
670-
[Symbol.iterator]: function* () {
671-
for (let i = 0; i < data.length; i++) {
672-
yield i;
673-
}
674-
},
675-
};
676-
}
677-
678675
private updateFieldOrder() {
679676
if (!this.fieldOrderStale) {
680677
return;
@@ -691,36 +688,43 @@ export class InvertedIndexMap<R extends Record<keyof R, unknown>> {
691688
const key = this.keyfn(record);
692689
let i = this.primaryIdx.get(key);
693690
if (i != null) {
694-
// For updates, remove the old posting for each field.
695691
const exstRecord = this.data[i];
692+
// Only update indices where values have changed
696693
for (const { field } of this.fieldOrder) {
697-
const v = exstRecord[field];
694+
const oldVal = exstRecord[field];
695+
const newVal = record[field];
696+
if (oldVal === newVal) {
697+
continue;
698+
}
698699
const idx = this.invIdxes[field]!;
699-
const posting = idx.get(v);
700+
// Remove old posting
701+
const posting = idx.get(oldVal);
700702
if (posting) {
701-
// Remove i from the sorted array (linear scan)
702703
const pos = posting.indexOf(i);
703704
if (pos !== -1) {
704705
posting.splice(pos, 1);
705706
}
706707
}
708+
// Add new posting
709+
if (!idx.has(newVal)) {
710+
idx.set(newVal, []);
711+
}
712+
idx.get(newVal)!.push(i);
707713
}
708714
} else {
709715
i = this.data.length;
710716
this.primaryIdx.set(key, i);
711-
}
712-
this.data[i] = record;
713-
// For each indexed field, add the new index.
714-
for (const { field } of this.fieldOrder) {
715-
const v = record[field];
716-
const idx = this.invIdxes[field]!;
717-
if (!idx.has(v)) {
718-
// For new posting lists, initialize with an empty array.
719-
idx.set(v, []);
717+
// For new records, just add to indices
718+
for (const { field } of this.fieldOrder) {
719+
const v = record[field];
720+
const idx = this.invIdxes[field]!;
721+
if (!idx.has(v)) {
722+
idx.set(v, []);
723+
}
724+
idx.get(v)!.push(i);
720725
}
721-
// Because indices only increase, push preserves sorted order.
722-
idx.get(v)!.push(i);
723726
}
727+
this.data[i] = record;
724728
this.fieldOrderStale = true;
725729
}
726730

@@ -755,14 +759,14 @@ export class InvertedIndexMap<R extends Record<keyof R, unknown>> {
755759
queryIndexSet(q: Partial<R>): IndexSet {
756760
const qfields = Object.keys(q) as (keyof R)[];
757761
if (qfields.length === 0) {
758-
return this.allIndexSet();
762+
return LenIndexSet(this.data.length);
759763
}
760764
if (qfields.length === 1) {
761765
const qfield = qfields[0];
762766
const idx = this.invIdxes[qfield];
763767
const qv = q[qfield];
764768
if (!idx || qv === undefined) {
765-
return this.allIndexSet();
769+
return LenIndexSet(this.data.length);
766770
}
767771
const posting = idx.get(qv);
768772
return posting ? sortedArrToIndexSet(posting) : EmptySet;

0 commit comments

Comments
 (0)