{"version":3,"file":"functional.cjs","names":["BaseTranslator","Operators","Comparators","inputType: string","comparator: Comparator","a: string | number","b: ValueType[C]","operator: Operator","operation: Operation","document: Document","comparison: Comparison<string | number | boolean>","castValue","query: StructuredQuery","defaultFilter: FunctionFilter","generatedFilter: FunctionFilter","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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"}