UNPKG

@langchain/community

Version:
1 lines • 20.6 kB
{"version":3,"file":"opensearch.cjs","names":["VectorStore","embeddings: EmbeddingsInterface","args: OpenSearchClientArgs","documents: Document[]","vectors: number[][]","options?: { ids?: string[] }","document: Record<string, any>","query: number[]","k: number","filter?: OpenSearchFilter | undefined","search: RequestParams.Search","hit: any","Document","texts: string[]","metadatas: object[] | object","docs: Document[]","dbConfig: OpenSearchClientArgs","dimension: number","filter: OpenSearchFilter | undefined","err: unknown","errors"],"sources":["../../src/vectorstores/opensearch.ts"],"sourcesContent":["import { Client, RequestParams, errors } from \"@opensearch-project/opensearch\";\nimport * as uuid from \"uuid\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\n\ntype OpenSearchEngine = \"nmslib\" | \"hnsw\";\ntype OpenSearchSpaceType = \"l2\" | \"cosinesimil\" | \"ip\";\n\n/**\n * Interface defining the options for vector search in OpenSearch. It\n * includes the engine type, space type, and parameters for the HNSW\n * algorithm.\n */\ninterface VectorSearchOptions {\n readonly engine?: OpenSearchEngine;\n readonly spaceType?: OpenSearchSpaceType;\n readonly m?: number;\n readonly efConstruction?: number;\n readonly efSearch?: number;\n readonly numberOfShards?: number;\n readonly numberOfReplicas?: number;\n}\n\n/**\n * Interface defining the arguments required to create an instance of the\n * OpenSearchVectorStore class. It includes the OpenSearch client, index\n * name, and vector search options.\n */\nexport interface OpenSearchClientArgs {\n readonly client: Client;\n readonly vectorFieldName?: string;\n readonly textFieldName?: string;\n readonly metadataFieldName?: string;\n readonly service?: \"es\" | \"aoss\";\n readonly indexName?: string;\n\n readonly vectorSearchOptions?: VectorSearchOptions;\n}\n\n/**\n * Type alias for an object. It's used to define filters for OpenSearch\n * queries.\n */\ntype OpenSearchFilter = {\n [key: string]: FilterTypeValue | (string | number)[] | string | number;\n};\n\n/**\n * FilterTypeValue for OpenSearch queries.\n */\ninterface FilterTypeValue {\n exists?: boolean;\n fuzzy?: string;\n ids?: string[];\n prefix?: string;\n gte?: number;\n gt?: number;\n lte?: number;\n lt?: number;\n regexp?: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n terms_set?: Record<string, any>;\n wildcard?: string;\n}\n\n/**\n * Class that provides a wrapper around the OpenSearch service for vector\n * search. It provides methods for adding documents and vectors to the\n * OpenSearch index, searching for similar vectors, and managing the\n * OpenSearch index.\n */\nexport class OpenSearchVectorStore extends VectorStore {\n declare FilterType: OpenSearchFilter;\n\n private readonly client: Client;\n\n private readonly indexName: string;\n\n // if true, use the Amazon OpenSearch Serverless service instead of es\n private readonly isAoss: boolean;\n\n private readonly engine: OpenSearchEngine;\n\n private readonly spaceType: OpenSearchSpaceType;\n\n private readonly efConstruction: number;\n\n private readonly efSearch: number;\n\n private readonly numberOfShards: number;\n\n private readonly numberOfReplicas: number;\n\n private readonly m: number;\n\n private readonly vectorFieldName: string;\n\n private readonly textFieldName: string;\n\n private readonly metadataFieldName: string;\n\n _vectorstoreType(): string {\n return \"opensearch\";\n }\n\n constructor(embeddings: EmbeddingsInterface, args: OpenSearchClientArgs) {\n super(embeddings, args);\n\n this.spaceType = args.vectorSearchOptions?.spaceType ?? \"l2\";\n this.engine = args.vectorSearchOptions?.engine ?? \"nmslib\";\n this.m = args.vectorSearchOptions?.m ?? 16;\n this.efConstruction = args.vectorSearchOptions?.efConstruction ?? 512;\n this.efSearch = args.vectorSearchOptions?.efSearch ?? 512;\n this.numberOfShards = args.vectorSearchOptions?.numberOfShards ?? 5;\n this.numberOfReplicas = args.vectorSearchOptions?.numberOfReplicas ?? 1;\n this.vectorFieldName = args.vectorFieldName ?? \"embedding\";\n this.textFieldName = args.textFieldName ?? \"text\";\n this.metadataFieldName = args.metadataFieldName ?? \"metadata\";\n\n this.client = args.client;\n this.indexName = args.indexName ?? \"documents\";\n this.isAoss = (args.service ?? \"es\") === \"aoss\";\n }\n\n /**\n * Method to add documents to the OpenSearch index. It first converts the\n * documents to vectors using the embeddings, then adds the vectors to the\n * index.\n * @param documents The documents to be added to the OpenSearch index.\n * @returns Promise resolving to void.\n */\n async addDocuments(documents: Document[]): Promise<void> {\n const texts = documents.map(({ pageContent }) => pageContent);\n return this.addVectors(\n await this.embeddings.embedDocuments(texts),\n documents\n );\n }\n\n /**\n * Method to add vectors to the OpenSearch index. It ensures the index\n * exists, then adds the vectors and associated documents to the index.\n * @param vectors The vectors to be added to the OpenSearch index.\n * @param documents The documents associated with the vectors.\n * @param options Optional parameter that can contain the IDs for the documents.\n * @returns Promise resolving to void.\n */\n async addVectors(\n vectors: number[][],\n documents: Document[],\n options?: { ids?: string[] }\n ): Promise<void> {\n await this.ensureIndexExists(\n vectors[0].length,\n this.engine,\n this.spaceType,\n this.efSearch,\n this.efConstruction,\n this.numberOfShards,\n this.numberOfReplicas,\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 // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const document: Record<string, any> = [\n {\n index: {\n _index: this.indexName,\n _id: documentIds[idx],\n },\n },\n {\n [this.vectorFieldName]: embedding,\n [this.textFieldName]: documents[idx].pageContent,\n [this.metadataFieldName]: documents[idx].metadata,\n },\n ];\n\n // aoss does not support document id\n if (this.isAoss) {\n delete document[0].index?._id;\n }\n\n return document;\n });\n await this.client.bulk({ body: operations });\n\n // aoss does not support refresh\n if (!this.isAoss) {\n await this.client.indices.refresh({ index: this.indexName });\n }\n }\n\n /**\n * Method to perform a similarity search on the OpenSearch index using a\n * query vector. It returns the k most similar documents and their scores.\n * @param query The query vector.\n * @param k The number of similar documents to return.\n * @param filter Optional filter for the OpenSearch query.\n * @returns Promise resolving to an array of tuples, each containing a Document and its score.\n */\n async similaritySearchVectorWithScore(\n query: number[],\n k: number,\n filter?: OpenSearchFilter | undefined\n ): Promise<[Document, number][]> {\n const search: RequestParams.Search = {\n index: this.indexName,\n body: {\n query: {\n bool: {\n filter: { bool: this.buildMetadataTerms(filter) },\n must: [\n {\n knn: {\n [this.vectorFieldName]: { vector: query, k },\n },\n },\n ],\n },\n },\n size: k,\n },\n };\n\n const { body } = await this.client.search(search);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return body.hits.hits.map((hit: any) => [\n new Document({\n pageContent: hit._source[this.textFieldName],\n metadata: hit._source[this.metadataFieldName],\n id: hit._id,\n }),\n hit._score,\n ]);\n }\n\n /**\n * Static method to create a new OpenSearchVectorStore from an array of\n * texts, their metadata, embeddings, and OpenSearch client arguments.\n * @param texts The texts to be converted into documents and added to the OpenSearch index.\n * @param metadatas The metadata associated with the texts. Can be an array of objects or a single object.\n * @param embeddings The embeddings used to convert the texts into vectors.\n * @param args The OpenSearch client arguments.\n * @returns Promise resolving to a new instance of OpenSearchVectorStore.\n */\n static fromTexts(\n texts: string[],\n metadatas: object[] | object,\n embeddings: EmbeddingsInterface,\n args: OpenSearchClientArgs\n ): Promise<OpenSearchVectorStore> {\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 OpenSearchVectorStore.fromDocuments(documents, embeddings, args);\n }\n\n /**\n * Static method to create a new OpenSearchVectorStore from an array of\n * Documents, embeddings, and OpenSearch client arguments.\n * @param docs The documents to be added to the OpenSearch index.\n * @param embeddings The embeddings used to convert the documents into vectors.\n * @param dbConfig The OpenSearch client arguments.\n * @returns Promise resolving to a new instance of OpenSearchVectorStore.\n */\n static async fromDocuments(\n docs: Document[],\n embeddings: EmbeddingsInterface,\n dbConfig: OpenSearchClientArgs\n ): Promise<OpenSearchVectorStore> {\n const store = new OpenSearchVectorStore(embeddings, dbConfig);\n await store.addDocuments(docs).then(() => store);\n return store;\n }\n\n /**\n * Static method to create a new OpenSearchVectorStore from an existing\n * OpenSearch index, embeddings, and OpenSearch client arguments.\n * @param embeddings The embeddings used to convert the documents into vectors.\n * @param dbConfig The OpenSearch client arguments.\n * @returns Promise resolving to a new instance of OpenSearchVectorStore.\n */\n static async fromExistingIndex(\n embeddings: EmbeddingsInterface,\n dbConfig: OpenSearchClientArgs\n ): Promise<OpenSearchVectorStore> {\n const store = new OpenSearchVectorStore(embeddings, dbConfig);\n await store.client.cat.indices({ index: store.indexName });\n return store;\n }\n\n private async ensureIndexExists(\n dimension: number,\n engine = \"nmslib\",\n spaceType = \"l2\",\n efSearch = 512,\n efConstruction = 512,\n numberOfShards = 5,\n numberOfReplicas = 1,\n m = 16\n ): Promise<void> {\n const body = {\n settings: {\n index: {\n number_of_shards: numberOfShards,\n number_of_replicas: numberOfReplicas,\n knn: true,\n \"knn.algo_param.ef_search\": efSearch,\n },\n },\n mappings: {\n dynamic_templates: [\n {\n // map all metadata properties to be keyword\n [`${this.metadataFieldName}.*`]: {\n match_mapping_type: \"string\",\n mapping: { type: \"keyword\" },\n },\n },\n {\n [`${this.metadataFieldName}.loc`]: {\n match_mapping_type: \"object\",\n mapping: { type: \"object\" },\n },\n },\n ],\n properties: {\n [this.textFieldName]: { type: \"text\" },\n [this.metadataFieldName]: { type: \"object\" },\n [this.vectorFieldName]: {\n type: \"knn_vector\",\n dimension,\n method: {\n name: \"hnsw\",\n engine,\n space_type: spaceType,\n parameters: { ef_construction: efConstruction, m },\n },\n },\n },\n },\n };\n\n const indexExists = await this.doesIndexExist();\n if (indexExists) return;\n\n await this.client.indices.create({ index: this.indexName, body });\n }\n\n /**\n * Builds metadata terms for OpenSearch queries.\n *\n * This function takes a filter object and constructs an array of query terms\n * compatible with OpenSearch 2.x. It supports a variety of query types including\n * term, terms, terms_set, ids, range, prefix, exists, fuzzy, wildcard, and regexp.\n * Reference: https://opensearch.org/docs/latest/query-dsl/term/index/\n *\n * @param {Filter | null} filter - The filter object used to construct query terms.\n * Each key represents a field, and the value specifies the type of query and its parameters.\n *\n * @returns {Array<Record<string, any>>} An array of OpenSearch query terms.\n *\n * @example\n * // Example filter:\n * const filter = {\n * status: { \"exists\": true },\n * age: { \"gte\": 30, \"lte\": 40 },\n * tags: [\"tag1\", \"tag2\"],\n * description: { \"wildcard\": \"*test*\" },\n *\n * };\n *\n * // Resulting query terms:\n * const queryTerms = buildMetadataTerms(filter);\n * // queryTerms would be an array of OpenSearch query objects.\n */\n buildMetadataTerms(filter: OpenSearchFilter | undefined): object {\n if (!filter) return {};\n const must = [];\n const must_not = [];\n for (const [key, value] of Object.entries(filter)) {\n const metadataKey = `${this.metadataFieldName}.${key}`;\n if (value) {\n if (typeof value === \"object\" && !Array.isArray(value)) {\n if (\"exists\" in value) {\n if (value.exists) {\n must.push({ exists: { field: metadataKey } });\n } else {\n must_not.push({ exists: { field: metadataKey } });\n }\n } else if (\"fuzzy\" in value) {\n must.push({ fuzzy: { [metadataKey]: value.fuzzy } });\n } else if (\"ids\" in value) {\n must.push({ ids: { values: value.ids } });\n } else if (\"prefix\" in value) {\n must.push({ prefix: { [metadataKey]: value.prefix } });\n } else if (\n \"gte\" in value ||\n \"gt\" in value ||\n \"lte\" in value ||\n \"lt\" in value\n ) {\n must.push({ range: { [metadataKey]: value } });\n } else if (\"regexp\" in value) {\n must.push({ regexp: { [metadataKey]: value.regexp } });\n } else if (\"terms_set\" in value) {\n must.push({ terms_set: { [metadataKey]: value.terms_set } });\n } else if (\"wildcard\" in value) {\n must.push({ wildcard: { [metadataKey]: value.wildcard } });\n }\n } else {\n const aggregatorKey = Array.isArray(value) ? \"terms\" : \"term\";\n must.push({ [aggregatorKey]: { [metadataKey]: value } });\n }\n }\n }\n return { must, must_not };\n }\n\n /**\n * Method to check if the OpenSearch index exists.\n * @returns Promise resolving to a boolean indicating whether the index exists.\n */\n async doesIndexExist(): Promise<boolean> {\n try {\n await this.client.cat.indices({ index: this.indexName });\n return true;\n } catch (err: unknown) {\n // eslint-disable-next-line no-instanceof/no-instanceof\n if (err instanceof errors.ResponseError && err.statusCode === 404) {\n return false;\n }\n throw err;\n }\n }\n\n /**\n * Method to delete the OpenSearch index if it exists.\n * @returns Promise resolving to void.\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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"}