{"version":3,"file":"functional.cjs","names":["BaseTranslator","Operators","Comparators","castValue","isFilterEmpty"],"sources":["../../src/structured_query/functional.ts"],"sourcesContent":["import { Document } from \"../documents/document.js\";\nimport {\n  Comparator,\n  Comparators,\n  Comparison,\n  Operation,\n  Operator,\n  Operators,\n  StructuredQuery,\n} from \"./ir.js\";\nimport { BaseTranslator } from \"./base.js\";\nimport { castValue, isFilterEmpty } from \"./utils.js\";\n\n/**\n * A type alias for an object that maps comparison operators to string or\n * number values. This is used in the comparison functions to determine\n * the result of a comparison operation.\n */\ntype ValueType = {\n  eq: string | number | boolean;\n  ne: string | number | boolean;\n  lt: string | number;\n  lte: string | number;\n  gt: string | number;\n  gte: string | number;\n};\n\n/**\n * A type alias for a function that takes a `Document` as an argument and\n * returns a boolean. This function is used as a filter for documents.\n */\nexport type FunctionFilter = (document: Document) => boolean;\n\n/**\n * A class that extends `BaseTranslator` to translate structured queries\n * into functional filters.\n * @example\n * ```typescript\n * const functionalTranslator = new FunctionalTranslator();\n * const relevantDocuments = await functionalTranslator.getRelevantDocuments(\n *   \"Which movies are rated higher than 8.5?\",\n * );\n * ```\n */\nexport class FunctionalTranslator extends BaseTranslator {\n  declare VisitOperationOutput: FunctionFilter;\n\n  declare VisitComparisonOutput: FunctionFilter;\n\n  declare VisitStructuredQueryOutput:\n    | { filter: FunctionFilter }\n    | { [k: string]: never };\n\n  allowedOperators: Operator[] = [Operators.and, Operators.or];\n\n  allowedComparators: Comparator[] = [\n    Comparators.eq,\n    Comparators.ne,\n    Comparators.gt,\n    Comparators.gte,\n    Comparators.lt,\n    Comparators.lte,\n  ];\n\n  formatFunction(): string {\n    throw new Error(\"Not implemented\");\n  }\n\n  /**\n   * Returns the allowed comparators for a given data type.\n   * @param input The input value to get the allowed comparators for.\n   * @returns An array of allowed comparators for the input data type.\n   */\n  getAllowedComparatorsForType(inputType: string): Comparator[] {\n    switch (inputType) {\n      case \"string\": {\n        return [\n          Comparators.eq,\n          Comparators.ne,\n          Comparators.gt,\n          Comparators.gte,\n          Comparators.lt,\n          Comparators.lte,\n        ];\n      }\n      case \"number\": {\n        return [\n          Comparators.eq,\n          Comparators.ne,\n          Comparators.gt,\n          Comparators.gte,\n          Comparators.lt,\n          Comparators.lte,\n        ];\n      }\n      case \"boolean\": {\n        return [Comparators.eq, Comparators.ne];\n      }\n      default: {\n        throw new Error(`Unsupported data type: ${inputType}`);\n      }\n    }\n  }\n\n  /**\n   * Returns a function that performs a comparison based on the provided\n   * comparator.\n   * @param comparator The comparator to base the comparison function on.\n   * @returns A function that takes two arguments and returns a boolean based on the comparison.\n   */\n  getComparatorFunction<C extends Comparator>(\n    comparator: Comparator\n  ): (a: string | number, b: ValueType[C]) => boolean {\n    switch (comparator) {\n      case Comparators.eq: {\n        return (a: string | number, b: ValueType[C]) => a === b;\n      }\n      case Comparators.ne: {\n        return (a: string | number, b: ValueType[C]) => a !== b;\n      }\n      case Comparators.gt: {\n        return (a: string | number, b: ValueType[C]) => a > b;\n      }\n      case Comparators.gte: {\n        return (a: string | number, b: ValueType[C]) => a >= b;\n      }\n      case Comparators.lt: {\n        return (a: string | number, b: ValueType[C]) => a < b;\n      }\n      case Comparators.lte: {\n        return (a: string | number, b: ValueType[C]) => a <= b;\n      }\n      default: {\n        throw new Error(\"Unknown comparator\");\n      }\n    }\n  }\n\n  /**\n   * Returns a function that performs an operation based on the provided\n   * operator.\n   * @param operator The operator to base the operation function on.\n   * @returns A function that takes two boolean arguments and returns a boolean based on the operation.\n   */\n  getOperatorFunction(operator: Operator): (a: boolean, b: boolean) => boolean {\n    switch (operator) {\n      case Operators.and: {\n        return (a, b) => a && b;\n      }\n      case Operators.or: {\n        return (a, b) => a || b;\n      }\n      default: {\n        throw new Error(\"Unknown operator\");\n      }\n    }\n  }\n\n  /**\n   * Visits the operation part of a structured query and translates it into\n   * a functional filter.\n   * @param operation The operation part of a structured query.\n   * @returns A function that takes a `Document` as an argument and returns a boolean based on the operation.\n   */\n  visitOperation(operation: Operation): this[\"VisitOperationOutput\"] {\n    const { operator, args } = operation;\n    if (this.allowedOperators.includes(operator)) {\n      const operatorFunction = this.getOperatorFunction(operator);\n      return (document: Document) => {\n        if (!args) {\n          return true;\n        }\n\n        return args.reduce((acc, arg) => {\n          const result = arg.accept(this);\n          if (typeof result === \"function\") {\n            return operatorFunction(acc, result(document));\n          } else {\n            throw new Error(\"Filter is not a function\");\n          }\n        }, true);\n      };\n    } else {\n      throw new Error(\"Operator not allowed\");\n    }\n  }\n\n  /**\n   * Visits the comparison part of a structured query and translates it into\n   * a functional filter.\n   * @param comparison The comparison part of a structured query.\n   * @returns A function that takes a `Document` as an argument and returns a boolean based on the comparison.\n   */\n  visitComparison(\n    comparison: Comparison<string | number | boolean>\n  ): this[\"VisitComparisonOutput\"] {\n    const { comparator, attribute, value } = comparison;\n    const undefinedTrue = [Comparators.ne];\n    if (this.allowedComparators.includes(comparator)) {\n      if (\n        !this.getAllowedComparatorsForType(typeof value).includes(comparator)\n      ) {\n        throw new Error(\n          `'${comparator}' comparator not allowed to be used with ${typeof value}`\n        );\n      }\n      const comparatorFunction = this.getComparatorFunction(comparator);\n      return (document: Document) => {\n        const documentValue = document.metadata[attribute];\n        if (documentValue === undefined) {\n          if (undefinedTrue.includes(comparator)) {\n            return true;\n          }\n          return false;\n        }\n        return comparatorFunction(documentValue, castValue(value));\n      };\n    } else {\n      throw new Error(\"Comparator not allowed\");\n    }\n  }\n\n  /**\n   * Visits a structured query and translates it into a functional filter.\n   * @param query The structured query to translate.\n   * @returns An object containing a `filter` property, which is a function that takes a `Document` as an argument and returns a boolean based on the structured query.\n   */\n  visitStructuredQuery(\n    query: StructuredQuery\n  ): this[\"VisitStructuredQueryOutput\"] {\n    if (!query.filter) {\n      return {};\n    }\n    const filterFunction = query.filter?.accept(this);\n    if (typeof filterFunction !== \"function\") {\n      throw new Error(\"Structured query filter is not a function\");\n    }\n    return { filter: filterFunction as FunctionFilter };\n  }\n\n  /**\n   * Merges two filters into one, based on the specified merge type.\n   * @param defaultFilter The default filter function.\n   * @param generatedFilter The generated filter function.\n   * @param mergeType The type of merge to perform. Can be 'and', 'or', or 'replace'. Default is 'and'.\n   * @returns A function that takes a `Document` as an argument and returns a boolean based on the merged filters, or `undefined` if both filters are empty.\n   */\n  mergeFilters(\n    defaultFilter: FunctionFilter,\n    generatedFilter: FunctionFilter,\n    mergeType = \"and\"\n  ): FunctionFilter | undefined {\n    if (isFilterEmpty(defaultFilter) && isFilterEmpty(generatedFilter)) {\n      return undefined;\n    }\n    if (isFilterEmpty(defaultFilter) || mergeType === \"replace\") {\n      if (isFilterEmpty(generatedFilter)) {\n        return undefined;\n      }\n      return generatedFilter;\n    }\n    if (isFilterEmpty(generatedFilter)) {\n      if (mergeType === \"and\") {\n        return undefined;\n      }\n      return defaultFilter;\n    }\n\n    if (mergeType === \"and\") {\n      return (document: Document) =>\n        defaultFilter(document) && generatedFilter(document);\n    } else if (mergeType === \"or\") {\n      return (document: Document) =>\n        defaultFilter(document) || generatedFilter(document);\n    } else {\n      throw new Error(\"Unknown merge type\");\n    }\n  }\n}\n"],"mappings":"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"}