From cdb2d5dc0331e6028229d2e5a608ea674fb57168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vinicius=20Louren=C3=A7o?= <12551007+H4ad@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:57:05 -0300 Subject: [PATCH] fix: issue with max call stack when search (#437) Co-authored-by: Vincent Baronnet Co-authored-by: Alexandr Pestryakov --- packages/orama/src/components/groups.ts | 8 +++++-- packages/orama/src/components/index.ts | 30 ++++++++++++------------- packages/orama/src/methods/search.ts | 4 ++-- packages/orama/src/trees/avl.ts | 16 ++++++++----- packages/orama/src/utils.ts | 26 +++++++++++++++++++++ 5 files changed, 59 insertions(+), 25 deletions(-) diff --git a/packages/orama/src/components/groups.ts b/packages/orama/src/components/groups.ts index 8e4e98980..58a484127 100644 --- a/packages/orama/src/components/groups.ts +++ b/packages/orama/src/components/groups.ts @@ -1,6 +1,6 @@ import type { Orama, ScalarSearchableValue, TokenScore, GroupByParams, GroupResult, Result, Reduce } from '../types.js' import { createError } from '../errors.js' -import { getNested, intersect } from '../utils.js' +import { getNested, intersect, safeArrayPush } from '../utils.js' import { getDocumentIdFromInternalId } from './internal-document-id-store.js' interface PropertyGroup { @@ -169,7 +169,11 @@ function calculateCombination(arrs: ScalarSearchableValue[][], index = 0): Scala const combinations = [] for (const value of head) { for (const combination of c) { - combinations.push([value, ...combination]) + const result = [value]; + + safeArrayPush(result, combination) + + combinations.push(result) } } diff --git a/packages/orama/src/components/index.ts b/packages/orama/src/components/index.ts index 0402de72e..a5b637278 100644 --- a/packages/orama/src/components/index.ts +++ b/packages/orama/src/components/index.ts @@ -34,7 +34,7 @@ import { Node as RadixNode, removeDocumentByWord as radixRemoveDocument, } from '../trees/radix.js' -import { intersect } from '../utils.js' +import { intersect, safeArrayPush } from '../utils.js'; import { BM25 } from './algorithms.js' import { getInnerType, getVectorSize, isArrayType, isVectorType } from './defaults.js' import { @@ -462,7 +462,7 @@ export async function searchByWhereClause( // Lookup const scoreList = await orama.index.search(context, index, prop, term) - context.indexMap[prop][term].push(...scoreList) + safeArrayPush(context.indexMap[prop][term], scoreList); } const docIds = context.indexMap[prop] diff --git a/packages/orama/src/trees/avl.ts b/packages/orama/src/trees/avl.ts index e7b86f5d0..bcc2a07cc 100644 --- a/packages/orama/src/trees/avl.ts +++ b/packages/orama/src/trees/avl.ts @@ -1,3 +1,5 @@ +import { safeArrayPush } from '../utils.js' + export type Node = { key: K value: V @@ -111,7 +113,7 @@ export function rangeSearch(node: Node, min: K, max: K): V { } if (node.key >= min && node.key <= max) { - result.push(...(node.value as V[])) + safeArrayPush(result, node.value as V[]); } if (node.key < max) { @@ -139,11 +141,11 @@ export function greaterThan(node: Node, key: K, inclusive = false): } if (inclusive && node.key >= key) { - result.push(...(node.value as V[])) + safeArrayPush(result, node.value as V[]); } if (!inclusive && node.key > key) { - result.push(...(node.value as V[])) + safeArrayPush(result, node.value as V[]); } traverse(node.left as Node) @@ -170,11 +172,11 @@ export function lessThan(node: Node, key: K, inclusive = false): V { } if (inclusive && node.key <= key) { - result.push(...(node.value as V[])) + safeArrayPush(result, node.value as V[]); } if (!inclusive && node.key < key) { - result.push(...(node.value as V[])) + safeArrayPush(result, node.value as V[]); } traverse(node.left as Node) @@ -358,6 +360,10 @@ export function remove(root: Node | null, key: K): Node | null export function removeDocument(root: Node, id: V, key: K): void { const node = getNodeByKey(root, key)! + if (!node) { + return + } + if (node.value.length === 1) { remove(root, key) return diff --git a/packages/orama/src/utils.ts b/packages/orama/src/utils.ts index 22e12956a..d1efac4fe 100644 --- a/packages/orama/src/utils.ts +++ b/packages/orama/src/utils.ts @@ -10,6 +10,32 @@ const second = BigInt(1e9) export const isServer = typeof window === 'undefined' +/** + * This value can be increased up to 100_000 + * But i don't know if this value change from nodejs to nodejs + * So I will keep a safer value here. + */ +export const MAX_ARGUMENT_FOR_STACK = 65535; + +/** + * This method is needed to used because of issues like: https://github.com/oramasearch/orama/issues/301 + * that issue is caused because the array that is pushed is huge (>100k) + * + * @example + * ```ts + * safeArrayPush(myArray, [1, 2]) + * ``` + */ +export function safeArrayPush(arr: T[], newArr: T[]): void { + if (newArr.length < MAX_ARGUMENT_FOR_STACK) { + Array.prototype.push.apply(arr, newArr) + } else { + for (let i = 0; i < newArr.length; i += MAX_ARGUMENT_FOR_STACK) { + Array.prototype.push.apply(arr, newArr.slice(i, i + MAX_ARGUMENT_FOR_STACK)) + } + } +} + export function sprintf(template: string, ...args: (string | number)[]): string { return template.replace( /%(?:(?\d+)\$)?(?-?\d*\.?\d*)(?[dfs])/g,