{"version":3,"file":"cassandra.cjs","names":["VectorStore","CassandraTable","Document"],"sources":["../../src/vectorstores/cassandra.ts"],"sourcesContent":["import { v4 as uuidv4 } from \"uuid\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport {\n  VectorStore,\n  MaxMarginalRelevanceSearchOptions,\n} from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\nimport { maximalMarginalRelevance } from \"@langchain/core/utils/math\";\n\nimport {\n  CassandraClientArgs,\n  Column,\n  Filter,\n  WhereClause,\n  CassandraTableArgs,\n  CassandraTable,\n} from \"../utils/cassandra.js\";\n\nexport type SupportedVectorTypes = \"cosine\" | \"dot_product\" | \"euclidean\";\n\nexport interface CassandraLibArgs\n  extends\n    CassandraClientArgs,\n    Omit<CassandraTableArgs, \"nonKeyColumns\" | \"keyspace\"> {\n  // keyspace is optional on CassandraClientArgs, but mandatory on CassandraTableArgs; we make it mandatory here\n  keyspace: string;\n  vectorType?: SupportedVectorTypes;\n  dimensions: number;\n  metadataColumns?: Column[];\n  nonKeyColumns?: Column | Column[];\n}\n\n/**\n * Class for interacting with the Cassandra database. It extends the\n * VectorStore class and provides methods for adding vectors and\n * documents, searching for similar vectors, and creating instances from\n * texts or documents.\n */\nexport class CassandraStore extends VectorStore {\n  declare FilterType: WhereClause;\n\n  private readonly table: CassandraTable;\n\n  private readonly idColumnAutoName = \"id\";\n\n  private readonly idColumnAutoGenerated: boolean;\n\n  private readonly vectorColumnName = \"vector\";\n\n  private readonly vectorColumn: Column;\n\n  private readonly textColumnName = \"text\";\n\n  private readonly textColumn: Column;\n\n  private readonly metadataColumnDefaultName = \"metadata\";\n\n  private readonly metadataColumns: Column[];\n\n  private readonly similarityColumn: Column;\n\n  private readonly embeddingColumnAlias = \"embedding\";\n\n  _vectorstoreType(): string {\n    return \"cassandra\";\n  }\n\n  private _cleanArgs(\n    args: CassandraLibArgs\n  ): CassandraLibArgs & { metadataColumns: Column[]; nonKeyColumns: Column[] } {\n    const {\n      table,\n      dimensions,\n      primaryKey,\n      nonKeyColumns,\n      indices,\n      metadataColumns,\n      vectorType = \"cosine\",\n    } = args;\n\n    if (!table || !dimensions) {\n      throw new Error(\"Missing required arguments\");\n    }\n\n    // Utility function to ensure the argument is treated as an array\n    function _toArray<T>(value: T | T[]): T[] {\n      return Array.isArray(value) ? value : [value];\n    }\n\n    const indicesArg = indices || [];\n\n    // Use the primary key if provided, else default to a single auto-generated UUID column\n    let primaryKeyArg: Column[];\n    if (primaryKey) {\n      primaryKeyArg = _toArray(primaryKey);\n    } else {\n      primaryKeyArg = [\n        { name: this.idColumnAutoName, type: \"uuid\", partition: true },\n      ];\n    }\n\n    // The combined nonKeyColumns and metadataColumns, de-duped by name\n    const combinedColumns = [\n      ..._toArray(nonKeyColumns || []),\n      ..._toArray(metadataColumns || []),\n    ];\n\n    const deduplicatedColumns = combinedColumns.filter(\n      (col, index, self) => self.findIndex((c) => c.name === col.name) === index\n    );\n\n    const nonKeyColumnsArg: Column[] = [...deduplicatedColumns];\n\n    // If no metadata columns are specified, add a default metadata column consistent with Langchain Python\n    if (nonKeyColumnsArg.length === 0) {\n      nonKeyColumnsArg.push({\n        name: this.metadataColumnDefaultName,\n        type: \"map<text, text>\",\n      });\n      indicesArg.push({\n        name: `idx_${this.metadataColumnDefaultName}_${table}_keys`,\n        value: `(keys(${this.metadataColumnDefaultName}))`,\n      });\n      indicesArg.push({\n        name: `idx_${this.metadataColumnDefaultName}_${table}_entries`,\n        value: `(entries(${this.metadataColumnDefaultName}))`,\n      });\n    }\n\n    const addDefaultNonKeyColumnIfNeeded = (defaultColumn: Column) => {\n      const column = nonKeyColumnsArg.find(\n        (col) => col.name === defaultColumn.name\n      );\n      if (!column) {\n        nonKeyColumnsArg.push(defaultColumn);\n      }\n    };\n\n    addDefaultNonKeyColumnIfNeeded({ name: this.textColumnName, type: \"text\" });\n    addDefaultNonKeyColumnIfNeeded({\n      name: this.vectorColumnName,\n      type: `VECTOR<FLOAT,${dimensions}>`,\n      alias: this.embeddingColumnAlias,\n    });\n\n    // If no index is specified for the vector column, add a default index\n    if (\n      !indicesArg.some((index) =>\n        new RegExp(`\\\\(\\\\s*${this.vectorColumnName.toLowerCase()}\\\\s*\\\\)`).test(\n          index.value.toLowerCase()\n        )\n      )\n    ) {\n      indicesArg.push({\n        name: `idx_${this.vectorColumnName}_${table}`,\n        value: `(${this.vectorColumnName})`,\n        options: `{'similarity_function': '${vectorType.toLowerCase()}'}`,\n      });\n    }\n\n    // Metadata the user will see excludes vector column and text column\n    const metadataColumnsArg = [...primaryKeyArg, ...nonKeyColumnsArg].filter(\n      (column) =>\n        column.name !== this.vectorColumnName &&\n        column.name !== this.textColumnName\n    );\n\n    return {\n      ...args,\n      vectorType,\n      primaryKey: primaryKeyArg,\n      nonKeyColumns: nonKeyColumnsArg,\n      metadataColumns: metadataColumnsArg,\n      indices: indicesArg,\n    };\n  }\n\n  private _getColumnByName(\n    columns: Column | Column[],\n    columnName: string\n  ): Column {\n    const columnsArray = Array.isArray(columns) ? columns : [columns];\n    const column = columnsArray.find((col) => col.name === columnName);\n    if (!column) {\n      throw new Error(`Column ${columnName} not found`);\n    }\n    return column;\n  }\n\n  constructor(embeddings: EmbeddingsInterface, args: CassandraLibArgs) {\n    super(embeddings, args);\n\n    const cleanedArgs = this._cleanArgs(args);\n\n    // This check here to help the compiler understand that nonKeyColumns will always\n    // have values after the _cleanArgs call. It is the cleanest way to handle the fact\n    // that the compiler is not able to make this determination, no matter how hard we try!\n    if (!cleanedArgs.nonKeyColumns || cleanedArgs.nonKeyColumns.length === 0) {\n      throw new Error(\"No non-key columns provided\");\n    }\n\n    this.vectorColumn = this._getColumnByName(\n      cleanedArgs.nonKeyColumns,\n      this.vectorColumnName\n    );\n    this.textColumn = this._getColumnByName(\n      cleanedArgs.nonKeyColumns,\n      this.textColumnName\n    );\n\n    this.similarityColumn = {\n      name: `similarity_${cleanedArgs.vectorType}(${this.vectorColumnName},?)`,\n      alias: \"similarity_score\",\n      type: \"\",\n    };\n    this.idColumnAutoGenerated = !args.primaryKey;\n    this.metadataColumns = cleanedArgs.metadataColumns;\n\n    this.table = new CassandraTable(cleanedArgs);\n  }\n\n  /**\n   * Method to save vectors to the Cassandra database.\n   * @param vectors Vectors to save.\n   * @param documents The documents associated with the vectors.\n   * @returns Promise that resolves when the vectors have been added.\n   */\n  async addVectors(vectors: number[][], documents: Document[]): Promise<void> {\n    if (vectors.length === 0) {\n      return;\n    }\n    // Prepare the values for upsert\n    const values = vectors.map((vector, index) => {\n      const document = documents[index];\n      const docMetadata = document.metadata || {};\n\n      // If idColumnAutoGenerated is true and ID is not provided, generate a UUID\n      if (\n        this.idColumnAutoGenerated &&\n        (docMetadata[this.idColumnAutoName] === undefined ||\n          docMetadata[this.idColumnAutoName] === \"\")\n      ) {\n        docMetadata[this.idColumnAutoName] = uuidv4();\n      }\n\n      // Construct the row\n      const row = [];\n\n      // Add values for each metadata column\n      this.metadataColumns.forEach((col) => {\n        row.push(docMetadata[col.name] || null);\n      });\n\n      // Add the text content and vector\n      row.push(document.pageContent);\n      row.push(new Float32Array(vector));\n\n      return row;\n    });\n\n    const columns = [\n      ...this.metadataColumns,\n      { name: this.textColumnName, type: \"\" },\n      { name: this.vectorColumnName, type: \"\" },\n    ];\n\n    return this.table.upsert(values, columns);\n  }\n\n  getCassandraTable(): CassandraTable {\n    return this.table;\n  }\n\n  /**\n   * Method to add documents to the Cassandra database.\n   * @param documents The documents to add.\n   * @returns Promise that resolves when the documents have been added.\n   */\n  async addDocuments(documents: Document[]): Promise<void> {\n    return this.addVectors(\n      await this.embeddings.embedDocuments(documents.map((d) => d.pageContent)),\n      documents\n    );\n  }\n\n  /**\n   * Helper method to search for vectors that are similar to a given query vector.\n   * @param query The query vector.\n   * @param k The number of similar Documents to return.\n   * @param filter Optional filter to be applied as a WHERE clause.\n   * @param includeEmbedding Whether to include the embedding vectors in the results.\n   * @returns Promise that resolves with an array of tuples, each containing a Document and a score.\n   */\n  async search(\n    query: number[],\n    k: number,\n    filter?: WhereClause,\n    includeEmbedding?: boolean\n  ): Promise<[Document, number][]> {\n    const vectorAsFloat32Array = new Float32Array(query);\n\n    const similarityColumnWithBinds = {\n      ...this.similarityColumn,\n      binds: [vectorAsFloat32Array],\n    };\n\n    const queryCols = [\n      ...this.metadataColumns,\n      this.textColumn,\n      similarityColumnWithBinds,\n    ];\n\n    if (includeEmbedding) {\n      queryCols.push(this.vectorColumn);\n    }\n\n    const orderBy: Filter = {\n      name: this.vectorColumnName,\n      operator: \"ANN OF\",\n      value: [vectorAsFloat32Array],\n    };\n\n    const queryResultSet = await this.table.select(\n      queryCols,\n      filter,\n      [orderBy],\n      k\n    );\n\n    return queryResultSet?.rows.map((row) => {\n      const textContent = row[this.textColumnName];\n      const sanitizedRow = { ...row };\n      delete sanitizedRow[this.textColumnName];\n      delete sanitizedRow.similarity_score;\n\n      Object.keys(sanitizedRow).forEach((key) => {\n        if (sanitizedRow[key] === null) {\n          delete sanitizedRow[key];\n        }\n      });\n\n      return [\n        new Document({ pageContent: textContent, metadata: sanitizedRow }),\n        row.similarity_score,\n      ];\n    });\n  }\n\n  /**\n   * Method to search for vectors that are similar to a given query vector.\n   * @param query The query vector.\n   * @param k The number of similar Documents to return.\n   * @param filter Optional filter to be applied as a WHERE clause.\n   * @returns Promise that resolves with an array of tuples, each containing a Document and a score.\n   */\n  async similaritySearchVectorWithScore(\n    query: number[],\n    k: number,\n    filter?: WhereClause\n  ): Promise<[Document, number][]> {\n    return this.search(query, k, filter, false);\n  }\n\n  /**\n   * Method to search for vectors that are similar to a given query vector, but with\n   * the results selected using the maximal marginal relevance.\n   * @param query The query string.\n   * @param options.k The number of similar Documents to return.\n   * @param options.fetchK=4*k The number of records to fetch before passing to the MMR algorithm.\n   * @param options.lambda=0.5 The degree of diversity among the results between 0 (maximum diversity) and 1 (minimum diversity).\n   * @param options.filter Optional filter to be applied as a WHERE clause.\n   * @returns List of documents selected by maximal marginal relevance.\n   */\n  async maxMarginalRelevanceSearch(\n    query: string,\n    options: MaxMarginalRelevanceSearchOptions<this[\"FilterType\"]>\n  ): Promise<Document[]> {\n    const { k, fetchK = 4 * k, lambda = 0.5, filter } = options;\n\n    const queryEmbedding = await this.embeddings.embedQuery(query);\n\n    const queryResults = await this.search(\n      queryEmbedding,\n      fetchK,\n      filter,\n      true\n    );\n\n    const embeddingList = queryResults.map(\n      (doc) => doc[0].metadata[this.embeddingColumnAlias]\n    );\n\n    const mmrIndexes = maximalMarginalRelevance(\n      queryEmbedding,\n      embeddingList,\n      lambda,\n      k\n    );\n\n    return mmrIndexes.map((idx) => {\n      const doc = queryResults[idx][0];\n      delete doc.metadata[this.embeddingColumnAlias];\n      return doc;\n    });\n  }\n\n  /**\n   * Static method to create an instance of CassandraStore from texts.\n   * @param texts The texts to use.\n   * @param metadatas The metadata associated with the texts.\n   * @param embeddings The embeddings to use.\n   * @param args The arguments for the CassandraStore.\n   * @returns Promise that resolves with a new instance of CassandraStore.\n   */\n  static async fromTexts(\n    texts: string[],\n    metadatas: object | object[],\n    embeddings: EmbeddingsInterface,\n    args: CassandraLibArgs\n  ): Promise<CassandraStore> {\n    const docs: Document[] = [];\n\n    for (let index = 0; index < texts.length; index += 1) {\n      const metadata = Array.isArray(metadatas) ? metadatas[index] : metadatas;\n      const doc = new Document({\n        pageContent: texts[index],\n        metadata,\n      });\n      docs.push(doc);\n    }\n\n    return CassandraStore.fromDocuments(docs, embeddings, args);\n  }\n\n  /**\n   * Static method to create an instance of CassandraStore from documents.\n   * @param docs The documents to use.\n   * @param embeddings The embeddings to use.\n   * @param args The arguments for the CassandraStore.\n   * @returns Promise that resolves with a new instance of CassandraStore.\n   */\n  static async fromDocuments(\n    docs: Document[],\n    embeddings: EmbeddingsInterface,\n    args: CassandraLibArgs\n  ): Promise<CassandraStore> {\n    const instance = new this(embeddings, args);\n    await instance.addDocuments(docs);\n    return instance;\n  }\n\n  /**\n   * Static method to create an instance of CassandraStore from an existing\n   * index.\n   * @param embeddings The embeddings to use.\n   * @param args The arguments for the CassandraStore.\n   * @returns Promise that resolves with a new instance of CassandraStore.\n   */\n  static async fromExistingIndex(\n    embeddings: EmbeddingsInterface,\n    args: CassandraLibArgs\n  ): Promise<CassandraStore> {\n    const instance = new this(embeddings, args);\n    return instance;\n  }\n}\n"],"mappings":"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"}