{"version":3,"file":"elasticsearch.cjs","names":["config: HybridRetrievalStrategyConfig","VectorStore","embeddings: EmbeddingsInterface","args: ElasticClientArgs","documents: Document[]","options?: { ids?: string[] }","vectors: number[][]","query: string","filter?: ElasticFilter","_callbacks?: Callbacks","query: number[]","k: number","hit: any","Document","queryText: string","queryVector: number[]","params: { ids: string[] }","texts: string[]","metadatas: object[] | object","docs: Document[]","dbConfig: ElasticClientArgs","dimension: number","request: estypes.IndicesCreateRequest","result: ElasticMetadataTerms"],"sources":["../../src/vectorstores/elasticsearch.ts"],"sourcesContent":["import * as uuid from \"uuid\";\nimport { Client, estypes } from \"@elastic/elasticsearch\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\nimport type { Callbacks } from \"@langchain/core/callbacks/manager\";\n/**\n * Type representing the k-nearest neighbors (k-NN) engine used in\n * Elasticsearch.\n */\ntype ElasticKnnEngine = \"hnsw\";\n/**\n * Type representing the similarity measure used in Elasticsearch.\n */\ntype ElasticSimilarity = \"l2_norm\" | \"dot_product\" | \"cosine\";\n\n/**\n * Interface defining the options for vector search in Elasticsearch.\n */\ninterface VectorSearchOptions {\n  readonly engine?: ElasticKnnEngine;\n  readonly similarity?: ElasticSimilarity;\n  readonly m?: number;\n  readonly efConstruction?: number;\n  readonly candidates?: number;\n}\n\n/**\n * Configuration options for hybrid retrieval strategy.\n */\nexport interface HybridRetrievalStrategyConfig {\n  rankWindowSize?: number;\n  rankConstant?: number;\n  textField?: string;\n}\n\n/**\n * Hybrid search strategy combining vector and BM25 search using RRF.\n */\nexport class HybridRetrievalStrategy {\n  public readonly rankWindowSize: number;\n  public readonly rankConstant: number;\n  public readonly textField: string;\n\n  constructor(config: HybridRetrievalStrategyConfig = {}) {\n    this.rankWindowSize = config.rankWindowSize ?? 100;\n    this.rankConstant = config.rankConstant ?? 60;\n    this.textField = config.textField ?? \"text\";\n  }\n}\n\n/**\n * Interface defining the arguments required to create an Elasticsearch\n * client.\n */\nexport interface ElasticClientArgs {\n  readonly client: Client;\n  readonly indexName?: string;\n  readonly vectorSearchOptions?: VectorSearchOptions;\n  readonly strategy?: HybridRetrievalStrategy;\n}\n\n/**\n * Type representing a filter object in Elasticsearch.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ElasticFilter = object | { field: string; operator: string; value: any }[];\n\ntype ElasticMetadataTerms = {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  must: { [operator: string]: { [field: string]: any } }[];\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  must_not: { [operator: string]: { [field: string]: any } }[];\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  should?: { [operator: string]: { [field: string]: any } }[];\n  minimum_should_match?: number;\n};\n\n/**\n * Elasticsearch vector store supporting vector and hybrid search.\n *\n * Hybrid search combines kNN vector search with BM25 full-text search\n * using RRF. Enable by passing a `HybridRetrievalStrategy` to the constructor.\n *\n * @example\n * ```typescript\n * // Vector search (default)\n * const vectorStore = new ElasticVectorSearch(embeddings, { client, indexName });\n *\n * // Hybrid search\n * const hybridStore = new ElasticVectorSearch(embeddings, {\n *   client,\n *   indexName,\n *   strategy: new HybridRetrievalStrategy()\n * });\n * ```\n */\nexport class ElasticVectorSearch extends VectorStore {\n  declare FilterType: ElasticFilter;\n\n  private readonly client: Client;\n\n  private readonly indexName: string;\n\n  private readonly engine: ElasticKnnEngine;\n\n  private readonly similarity: ElasticSimilarity;\n\n  private readonly efConstruction: number;\n\n  private readonly m: number;\n\n  private readonly candidates: number;\n\n  private readonly strategy?: HybridRetrievalStrategy;\n\n  private lastQueryText?: string;\n\n  _vectorstoreType(): string {\n    return \"elasticsearch\";\n  }\n\n  constructor(embeddings: EmbeddingsInterface, args: ElasticClientArgs) {\n    super(embeddings, args);\n\n    this.engine = args.vectorSearchOptions?.engine ?? \"hnsw\";\n    this.similarity = args.vectorSearchOptions?.similarity ?? \"l2_norm\";\n    this.m = args.vectorSearchOptions?.m ?? 16;\n    this.efConstruction = args.vectorSearchOptions?.efConstruction ?? 100;\n    this.candidates = args.vectorSearchOptions?.candidates ?? 200;\n    this.strategy = args.strategy;\n\n    const userAgent = this.strategy\n      ? \"langchain-js-vs-hybrid/0.0.1\"\n      : \"langchain-js-vs/0.0.1\";\n\n    this.client = args.client.child({\n      headers: { \"user-agent\": userAgent },\n    });\n    this.indexName = args.indexName ?? \"documents\";\n  }\n\n  /**\n   * Method to add documents to the Elasticsearch database. It first\n   * converts the documents to vectors using the embeddings, then adds the\n   * vectors to the database.\n   * @param documents The documents to add to the database.\n   * @param options Optional parameter that can contain the IDs for the documents.\n   * @returns A promise that resolves with the IDs of the added documents.\n   */\n  async addDocuments(documents: Document[], options?: { ids?: string[] }) {\n    const texts = documents.map(({ pageContent }) => pageContent);\n    return this.addVectors(\n      await this.embeddings.embedDocuments(texts),\n      documents,\n      options\n    );\n  }\n\n  /**\n   * Method to add vectors to the Elasticsearch database. It ensures the\n   * index exists, then adds the vectors and their corresponding documents\n   * to the database.\n   * @param vectors The vectors to add to the database.\n   * @param documents The documents corresponding to the vectors.\n   * @param options Optional parameter that can contain the IDs for the documents.\n   * @returns A promise that resolves with the IDs of the added documents.\n   */\n  async addVectors(\n    vectors: number[][],\n    documents: Document[],\n    options?: { ids?: string[] }\n  ) {\n    await this.ensureIndexExists(\n      vectors[0].length,\n      this.engine,\n      this.similarity,\n      this.efConstruction,\n      this.m\n    );\n    const documentIds =\n      options?.ids ?? Array.from({ length: vectors.length }, () => uuid.v4());\n    const operations = vectors.flatMap((embedding, idx) => [\n      {\n        index: {\n          _id: documentIds[idx],\n          _index: this.indexName,\n        },\n      },\n      {\n        embedding,\n        metadata: documents[idx].metadata,\n        text: documents[idx].pageContent,\n      },\n    ]);\n    const results = await this.client.bulk({ refresh: true, operations });\n    if (results.errors) {\n      const reasons = results.items.map(\n        (result) => result.index?.error?.reason\n      );\n      throw new Error(`Failed to insert documents:\\n${reasons.join(\"\\n\")}`);\n    }\n    return documentIds;\n  }\n\n  async similaritySearch(\n    query: string,\n    k = 4,\n    filter?: ElasticFilter,\n    _callbacks?: Callbacks\n  ): Promise<Document[]> {\n    this.lastQueryText = query;\n    return super.similaritySearch(query, k, filter, _callbacks);\n  }\n\n  /**\n   * Method to perform a similarity search in the Elasticsearch database\n   * using a vector. It returns the k most similar documents along with\n   * their similarity scores.\n   * @param query The query vector.\n   * @param k The number of most similar documents to return.\n   * @param filter Optional filter to apply to the search.\n   * @returns A promise that resolves with an array of tuples, where each tuple contains a Document and its similarity score.\n   */\n  async similaritySearchVectorWithScore(\n    query: number[],\n    k: number,\n    filter?: ElasticFilter\n  ): Promise<[Document, number][]> {\n    if (this.strategy && this.lastQueryText) {\n      return this.hybridSearchVectorWithScore(\n        this.lastQueryText,\n        query,\n        k,\n        filter\n      );\n    }\n\n    const result = await this.client.search({\n      index: this.indexName,\n      size: k,\n      knn: {\n        field: \"embedding\",\n        query_vector: query,\n        filter: { bool: this.buildMetadataTerms(filter) },\n        k,\n        num_candidates: this.candidates,\n      },\n    });\n\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    return result.hits.hits.map((hit: any) => [\n      new Document({\n        pageContent: hit._source.text,\n        metadata: hit._source.metadata,\n      }),\n      hit._score,\n    ]);\n  }\n\n  private async hybridSearchVectorWithScore(\n    queryText: string,\n    queryVector: number[],\n    k: number,\n    filter?: ElasticFilter\n  ): Promise<[Document, number][]> {\n    const metadataTerms = this.buildMetadataTerms(filter);\n    const filterClauses =\n      metadataTerms.must.length > 0 || metadataTerms.must_not.length > 0\n        ? { bool: metadataTerms }\n        : undefined;\n\n    const result = await this.client.search({\n      index: this.indexName,\n      size: k,\n      retriever: {\n        rrf: {\n          retrievers: [\n            {\n              standard: {\n                query: {\n                  match: {\n                    [this.strategy!.textField]: queryText,\n                  },\n                },\n              },\n            },\n            {\n              knn: {\n                field: \"embedding\",\n                query_vector: queryVector,\n                k,\n                num_candidates: this.candidates,\n              },\n            },\n          ],\n          rank_window_size: this.strategy!.rankWindowSize,\n          rank_constant: this.strategy!.rankConstant,\n        },\n      },\n      ...(filterClauses && { query: filterClauses }),\n    });\n\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    return result.hits.hits.map((hit: any) => [\n      new Document({\n        pageContent: hit._source.text,\n        metadata: hit._source.metadata,\n      }),\n      hit._score,\n    ]);\n  }\n\n  /**\n   * Method to delete documents from the Elasticsearch database.\n   * @param params Object containing the IDs of the documents to delete.\n   * @returns A promise that resolves when the deletion is complete.\n   */\n  async delete(params: { ids: string[] }): Promise<void> {\n    const operations = params.ids.map((id) => ({\n      delete: {\n        _id: id,\n        _index: this.indexName,\n      },\n    }));\n    if (operations.length > 0)\n      await this.client.bulk({ refresh: true, operations });\n  }\n\n  /**\n   * Static method to create an ElasticVectorSearch instance from texts. It\n   * creates Document instances from the texts and their corresponding\n   * metadata, then calls the fromDocuments method to create the\n   * ElasticVectorSearch instance.\n   * @param texts The texts to create the ElasticVectorSearch instance from.\n   * @param metadatas The metadata corresponding to the texts.\n   * @param embeddings The embeddings to use for the documents.\n   * @param args The arguments to create the Elasticsearch client.\n   * @returns A promise that resolves with the created ElasticVectorSearch instance.\n   */\n  static fromTexts(\n    texts: string[],\n    metadatas: object[] | object,\n    embeddings: EmbeddingsInterface,\n    args: ElasticClientArgs\n  ): Promise<ElasticVectorSearch> {\n    const documents = texts.map((text, idx) => {\n      const metadata = Array.isArray(metadatas) ? metadatas[idx] : metadatas;\n      return new Document({ pageContent: text, metadata });\n    });\n\n    return ElasticVectorSearch.fromDocuments(documents, embeddings, args);\n  }\n\n  /**\n   * Static method to create an ElasticVectorSearch instance from Document\n   * instances. It adds the documents to the Elasticsearch database, then\n   * returns the ElasticVectorSearch instance.\n   * @param docs The Document instances to create the ElasticVectorSearch instance from.\n   * @param embeddings The embeddings to use for the documents.\n   * @param dbConfig The configuration for the Elasticsearch database.\n   * @returns A promise that resolves with the created ElasticVectorSearch instance.\n   */\n  static async fromDocuments(\n    docs: Document[],\n    embeddings: EmbeddingsInterface,\n    dbConfig: ElasticClientArgs\n  ): Promise<ElasticVectorSearch> {\n    const store = new ElasticVectorSearch(embeddings, dbConfig);\n    await store.addDocuments(docs).then(() => store);\n    return store;\n  }\n\n  /**\n   * Static method to create an ElasticVectorSearch instance from an\n   * existing index in the Elasticsearch database. It checks if the index\n   * exists, then returns the ElasticVectorSearch instance if it does.\n   * @param embeddings The embeddings to use for the documents.\n   * @param dbConfig The configuration for the Elasticsearch database.\n   * @returns A promise that resolves with the created ElasticVectorSearch instance if the index exists, otherwise it throws an error.\n   */\n  static async fromExistingIndex(\n    embeddings: EmbeddingsInterface,\n    dbConfig: ElasticClientArgs\n  ): Promise<ElasticVectorSearch> {\n    const store = new ElasticVectorSearch(embeddings, dbConfig);\n    const exists = await store.doesIndexExist();\n    if (exists) {\n      return store;\n    }\n    throw new Error(`The index ${store.indexName} does not exist.`);\n  }\n\n  private async ensureIndexExists(\n    dimension: number,\n    engine = \"hnsw\",\n    similarity = \"l2_norm\",\n    efConstruction = 100,\n    m = 16\n  ): Promise<void> {\n    const request: estypes.IndicesCreateRequest = {\n      index: this.indexName,\n      mappings: {\n        dynamic_templates: [\n          {\n            // map all metadata properties to be keyword except loc\n            metadata_except_loc: {\n              match_mapping_type: \"*\",\n              match: \"metadata.*\",\n              unmatch: \"metadata.loc\",\n              mapping: { type: \"keyword\" },\n            },\n          },\n        ],\n        properties: {\n          text: { type: \"text\" },\n          metadata: {\n            type: \"object\",\n            properties: {\n              loc: { type: \"object\" }, // explicitly define loc as an object\n            },\n          },\n          embedding: {\n            type: \"dense_vector\",\n            dims: dimension,\n            index: true,\n            similarity,\n            index_options: {\n              type: engine,\n              m,\n              ef_construction: efConstruction,\n            },\n          },\n        },\n      },\n    };\n\n    const indexExists = await this.doesIndexExist();\n    if (indexExists) return;\n\n    await this.client.indices.create(request);\n  }\n\n  private buildMetadataTerms(filter?: ElasticFilter): ElasticMetadataTerms {\n    if (filter == null) return { must: [], must_not: [] };\n    const filters = Array.isArray(filter)\n      ? filter\n      : Object.entries(filter).map(([key, value]) => ({\n          operator: \"term\",\n          field: key,\n          value,\n        }));\n\n    const must = [];\n    const must_not = [];\n    const should = [];\n    for (const condition of filters) {\n      const metadataField = `metadata.${condition.field}`;\n      if (condition.operator === \"exists\") {\n        must.push({\n          [condition.operator]: {\n            field: metadataField,\n          },\n        });\n      } else if (condition.operator === \"not_exists\") {\n        must_not.push({\n          exists: {\n            field: metadataField,\n          },\n        });\n      } else if (condition.operator === \"exclude\") {\n        const toExclude = { [metadataField]: condition.value };\n        must_not.push({\n          ...(Array.isArray(condition.value)\n            ? { terms: toExclude }\n            : { term: toExclude }),\n        });\n      } else if (condition.operator === \"or\") {\n        should.push({\n          term: {\n            [metadataField]: condition.value,\n          },\n        });\n      } else {\n        must.push({\n          [condition.operator]: {\n            [metadataField]: condition.value,\n          },\n        });\n      }\n    }\n    const result: ElasticMetadataTerms = { must, must_not };\n\n    if (should.length > 0) {\n      result.should = should;\n      result.minimum_should_match = 1;\n    }\n    return result;\n  }\n\n  /**\n   * Method to check if an index exists in the Elasticsearch database.\n   * @returns A promise that resolves with a boolean indicating whether the index exists.\n   */\n  async doesIndexExist(): Promise<boolean> {\n    return await this.client.indices.exists({ index: this.indexName });\n  }\n\n  /**\n   * Method to delete an index from the Elasticsearch database if it exists.\n   * @returns A promise that resolves when the deletion is complete.\n   */\n  async deleteIfExists(): Promise<void> {\n    const indexExists = await this.doesIndexExist();\n    if (!indexExists) return;\n\n    await this.client.indices.delete({ index: this.indexName });\n  }\n}\n"],"mappings":"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"}