diff --git a/docs/core_docs/docs/integrations/document_loaders/web_loaders/couchbase.mdx b/docs/core_docs/docs/integrations/document_loaders/web_loaders/couchbase.mdx
index 950a01fa354f..901a81de4b56 100644
--- a/docs/core_docs/docs/integrations/document_loaders/web_loaders/couchbase.mdx
+++ b/docs/core_docs/docs/integrations/document_loaders/web_loaders/couchbase.mdx
@@ -77,7 +77,6 @@ for await (const doc of this.lazyLoad()) {
```
### Specifying Fields with Content and Metadata
-
The fields that are part of the Document content can be specified using the `pageContentFields` parameter.
The metadata fields for the Document can be specified using the `metadataFields` parameter.
diff --git a/docs/core_docs/docs/integrations/vectorstores/couchbase.mdx b/docs/core_docs/docs/integrations/vectorstores/couchbase.mdx
new file mode 100644
index 000000000000..588169f00195
--- /dev/null
+++ b/docs/core_docs/docs/integrations/vectorstores/couchbase.mdx
@@ -0,0 +1,347 @@
+---
+hide_table_of_contents: true
+sidebar_class_name: node-only
+---
+
+import CodeBlock from "@theme/CodeBlock";
+
+# Couchbase
+
+:::tip Compatibility
+Only available on Node.js.
+:::
+
+[Couchbase](http://couchbase.com/) is an award-winning distributed NoSQL cloud database that delivers unmatched versatility, performance, scalability, and financial value for all of your cloud, mobile,
+AI, and edge computing applications. Couchbase embraces AI with coding assistance for developers and vector search for their applications.
+
+Vector search is a part of the [Full Text Service](https://docs.couchbase.com/server/current/learn/services-and-indexes/services/search-service.html)(FTS) in Couchbase.
+
+This tutorial explains how to use vector search in Couchbase. You can work with both [Couchbase Capella](https://www.couchbase.com/products/capella/) and your self-managed Couchbase server.
+
+## Installation
+
+You will need couchbase and langchain community to use couchbase vector store. For this tutorial, we will use OpenAI embeddings
+
+```bash npm2yarn
+npm install couchbase @langchain/openai @langchain/community
+```
+
+## Create Couchbase Connection Object
+
+We create a connection to the Couchbase cluster initially and then pass the cluster object to the Vector Store. Here, we are connecting using the username and password.
+You can also connect using any other supported way to your cluster.
+
+For more information on connecting to the Couchbase cluster, please check the [Node SDK documentation](https://docs.couchbase.com/nodejs-sdk/current/hello-world/start-using-sdk.html#connect).
+
+```typescript
+import { Cluster } from "couchbase";
+
+const connectionString = "couchbase://localhost"; // valid couchbase connection string
+const dbUsername = "Administrator"; // valid database user with read access to the bucket being queried
+const dbPassword = "Password"; // password for the database user
+
+const couchbaseClient = await Cluster.connect(connectionString, {
+ username: dbUsername,
+ password: dbPassword,
+ configProfile: "wanDevelopment",
+});
+```
+
+## Create the Vector Index
+
+Currently, the vector index needs to be created from the Couchbase Capella or Server UI or using the REST interface.
+
+Let us define a vector index with the name `vector-index` on the testing bucket
+
+For this example, let us use the Import Index feature on the Full Text Search on the UI.
+We are defining an index on the `testing` bucket's `_default` scope on the `_default` collection with the vector field set to `embedding` and text field set to `text`.
+We are also indexing and storing all the fields under `metadata` in the document dynamically. The similarity metric is set to `dot_product`.
+
+How to Import an Index to the Full Text Search service?
+
+- Couchbase Server: Click on Search -> Add Index -> Import
+ - Copy the following Index definition in the Import screen
+- [Couchbase Capella](https://docs.couchbase.com/cloud/search/import-search-index.html)
+ - Copy the following index definition to a new file `index.json` and import that file in Capella using the instructions in the documentation.
+
+### Index Definition
+
+```json
+{
+ "name": "vector-index",
+ "type": "fulltext-index",
+ "params": {
+ "doc_config": {
+ "docid_prefix_delim": "",
+ "docid_regexp": "",
+ "mode": "type_field",
+ "type_field": "type"
+ },
+ "mapping": {
+ "default_analyzer": "standard",
+ "default_datetime_parser": "dateTimeOptional",
+ "default_field": "_all",
+ "default_mapping": {
+ "dynamic": true,
+ "enabled": true,
+ "properties": {
+ "metadata": {
+ "dynamic": true,
+ "enabled": true
+ },
+ "embedding": {
+ "enabled": true,
+ "dynamic": false,
+ "fields": [
+ {
+ "dims": 1536,
+ "index": true,
+ "name": "embedding",
+ "similarity": "dot_product",
+ "type": "vector",
+ "vector_index_optimized_for": "recall"
+ }
+ ]
+ },
+ "text": {
+ "enabled": true,
+ "dynamic": false,
+ "fields": [
+ {
+ "index": true,
+ "name": "text",
+ "store": true,
+ "type": "text"
+ }
+ ]
+ }
+ }
+ },
+ "default_type": "_default",
+ "docvalues_dynamic": false,
+ "index_dynamic": true,
+ "store_dynamic": true,
+ "type_field": "_type"
+ },
+ "store": {
+ "indexType": "scorch",
+ "segmentVersion": 16
+ }
+ },
+ "sourceType": "gocbcore",
+ "sourceName": "testing",
+ "sourceParams": {},
+ "planParams": {
+ "maxPartitionsPerPIndex": 103,
+ "indexPartitions": 10,
+ "numReplicas": 0
+ }
+}
+```
+
+For more details on how to create an FTS index with support for Vector fields, please refer to the documentation:
+
+- [Couchbase Capella](https://docs.couchbase.com/cloud/search/create-search-indexes.html)
+- [Couchbase Server](https://docs.couchbase.com/server/current/search/create-search-indexes.html)
+
+For using this vector store, CouchbaseVectorStoreArgs needs to be configured.
+
+```typescript
+const couchbaseConfig: CouchbaseVectorStoreArgs = {
+ cluster: couchbaseClient,
+ bucketName: "testing",
+ scopeName: "_default",
+ collectionName: "_default",
+ indexName: "vector-index",
+ textKey: "text",
+ embeddingKey: "embedding",
+};
+```
+
+## Similarity Search
+
+The following example showcases how to use couchbase vector search and perform similarity search.
+For this example, we are going to load the "state_of_the_union.txt" file via the RecursiveCharacterTextSplitter, create langchain documents from the chunks and send to couchbase vector store.
+After the data is indexed, we perform a simple query to find the top 4 chunks that are similar to the query "What did president say about Ketanji Brown Jackson".
+This example at the end, also shows how to get similarity score
+
+import SimilaritySearch from "@examples/indexes/vector_stores/couchbase/similaritySearch.ts";
+
+{SimilaritySearch}
+
+## Specifying Fields to Return
+
+You can specify the fields to return from the document using `fields` parameter in the filter during searches.
+These fields are returned as part of the `metadata` object. You can fetch any field that is stored in the index.
+The `textKey` of the document is returned as part of the document's `pageContent`.
+
+If you do not specify any fields to be fetched, all the fields stored in the index are returned.
+
+If you want to fetch one of the fields in the metadata, you need to specify it using `.`
+For example, to fetch the `source` field in the metadata, you need to use `metadata.source`.
+
+```typescript
+const result = await store.similaritySearch(query, 1, {
+ fields: ["metadata.source"],
+});
+console.log(result[0]);
+```
+
+## Hybrid Search
+
+Couchbase allows you to do hybrid searches by combining vector search results with searches on non-vector fields of the document like the `metadata` object.
+
+The results will be based on the combination of the results from both vector search and the searches supported by full text search service.
+The scores of each of the component searches are added up to get the total score of the result.
+
+To perform hybrid searches, there is an optional key, `searchOptions` in `fields` parameter that can be passed to all the similarity searches.
+The different search/query possibilities for the `searchOptions` can be found [here](https://docs.couchbase.com/server/current/search/search-request-params.html#query-object).
+
+### Create Diverse Metadata for Hybrid Search
+
+In order to simulate hybrid search, let us create some random metadata from the existing documents.
+We uniformly add three fields to the metadata, `date` between 2010 & 2020, `rating` between 1 & 5 and `author` set to either John Doe or Jane Doe.
+We will also declare few sample queries.
+
+```typescript
+for (let i = 0; i < docs.length; i += 1) {
+ docs[i].metadata.date = `${2010 + (i % 10)}-01-01`;
+ docs[i].metadata.rating = 1 + (i % 5);
+ docs[i].metadata.author = ["John Doe", "Jane Doe"][i % 2];
+}
+
+const store = await CouchbaseVectorStore.fromDocuments(
+ docs,
+ embeddings,
+ couchbaseConfig
+);
+
+const query = "What did the president say about Ketanji Brown Jackson";
+const independenceQuery = "Any mention about independence?";
+```
+
+### Example: Search by Exact Value
+
+We can search for exact matches on a textual field like the author in the `metadata` object.
+
+```typescript
+const exactValueResult = await store.similaritySearch(query, 4, {
+ fields: ["metadata.author"],
+ searchOptions: {
+ query: { field: "metadata.author", match: "John Doe" },
+ },
+});
+console.log(exactValueResult[0]);
+```
+
+### Example: Search by Partial Match
+
+We can search for partial matches by specifying a fuzziness for the search. This is useful when you want to search for slight variations or misspellings of a search query.
+
+Here, "Jae" is close (fuzziness of 1) to "Jane".
+
+```typescript
+const partialMatchResult = await store.similaritySearch(query, 4, {
+ fields: ["metadata.author"],
+ searchOptions: {
+ query: { field: "metadata.author", match: "Johny", fuzziness: 1 },
+ },
+});
+console.log(partialMatchResult[0]);
+```
+
+### Example: Search by Date Range Query
+
+We can search for documents that are within a date range query on a date field like `metadata.date`.
+
+```typescript
+const dateRangeResult = await store.similaritySearch(independenceQuery, 4, {
+ fields: ["metadata.date", "metadata.author"],
+ searchOptions: {
+ query: {
+ start: "2022-12-31",
+ end: "2023-01-02",
+ inclusiveStart: true,
+ inclusiveEnd: false,
+ field: "metadata.date",
+ },
+ },
+});
+console.log(dateRangeResult[0]);
+```
+
+### Example: Search by Numeric Range Query
+
+We can search for documents that are within a range for a numeric field like `metadata.rating`.
+
+```typescript
+const ratingRangeResult = await store.similaritySearch(independenceQuery, 4, {
+ fields: ["metadata.rating"],
+ searchOptions: {
+ query: {
+ min: 3,
+ max: 5,
+ inclusiveMin: false,
+ inclusiveMax: true,
+ field: "metadata.rating",
+ },
+ },
+});
+console.log(ratingRangeResult[0]);
+```
+
+### Example: Combining Multiple Search Conditions
+
+Different queries can by combined using AND (conjuncts) or OR (disjuncts) operators.
+
+In this example, we are checking for documents with a rating between 3 & 4 and dated between 2015 & 2018.
+
+```typescript
+const multipleConditionsResult = await store.similaritySearch(texts[0], 4, {
+ fields: ["metadata.rating", "metadata.date"],
+ searchOptions: {
+ query: {
+ conjuncts: [
+ { min: 3, max: 4, inclusive_max: true, field: "metadata.rating" },
+ { start: "2016-12-31", end: "2017-01-02", field: "metadata.date" },
+ ],
+ },
+ },
+});
+console.log(multipleConditionsResult[0]);
+```
+
+### Other Queries
+
+Similarly, you can use any of the supported Query methods like Geo Distance, Polygon Search, Wildcard, Regular Expressions, etc in the `search_options` parameter. Please refer to the documentation for more details on the available query methods and their syntax.
+
+- [Couchbase Capella](https://docs.couchbase.com/cloud/search/search-request-params.html#query-object)
+- [Couchbase Server](https://docs.couchbase.com/server/current/search/search-request-params.html#query-object)
+
+
+
+
+# Frequently Asked Questions
+
+## Question: Should I create the FTS index before creating the CouchbaseVectorStore object?
+
+Yes, currently you need to create the FTS index before creating the `CouchbaseVectoreStore` object.
+
+## Question: I am not seeing all the fields that I specified in my search results.
+
+In Couchbase, we can only return the fields stored in the FTS index. Please ensure that the field that you are trying to access in the search results is part of the index.
+
+One way to handle this is to store a document's fields dynamically in the index. To do that, you need to select `Store Dynamic Fields` in the Advanced Settings of the FTS index.
+
+Similarly, if you want to search on dynamic fields, you must index those fields by selecting the option `Index Dynamic Fields` in the FTS index settings.
+
+Note that these options will increase the size of the index.
+
+## Question: I am unable to see the metadata object in my search results.
+
+This is most likely due to the `metadata` field in the document not being indexed by the Couchbase FTS index. In order to index the `metadata` field in the document, you need to add it to the index as a mapping.
+
+If you select to map all the fields in the mapping, you will be able to search by all metadata fields. Alternatively, you can select the specific fields inside `metadata` object to be indexed. You can refer to the docs to learn more about indexing child mappings.
+
+- [Couchbase Capella](https://docs.couchbase.com/cloud/search/create-child-mapping.html)
+- [Couchbase Server](https://docs.couchbase.com/server/current/fts/fts-creating-index-from-UI-classic-editor-dynamic.html)
diff --git a/examples/package.json b/examples/package.json
index 5e778c67cc97..9aa874c52a3f 100644
--- a/examples/package.json
+++ b/examples/package.json
@@ -64,6 +64,7 @@
"axios": "^0.26.0",
"chromadb": "^1.5.3",
"convex": "^1.3.1",
+ "couchbase": "^4.2.11",
"date-fns": "^3.3.1",
"exa-js": "^1.0.12",
"faiss-node": "^0.5.1",
diff --git a/examples/src/indexes/vector_stores/couchbase/.env.example b/examples/src/indexes/vector_stores/couchbase/.env.example
new file mode 100644
index 000000000000..1e6596582a0c
--- /dev/null
+++ b/examples/src/indexes/vector_stores/couchbase/.env.example
@@ -0,0 +1,13 @@
+# Couchbase connection params
+DB_CONN_STR=
+DB_USERNAME=
+DB_PASSWORD=
+
+# Couchbase vector store args
+DB_BUCKET_NAME=
+DB_SCOPE_NAME=
+DB_COLLECTION_NAME=
+DB_INDEX_NAME=
+
+# Open AI Key for embeddings
+OPENAI_API_KEY=
\ No newline at end of file
diff --git a/examples/src/indexes/vector_stores/couchbase/similaritySearch.ts b/examples/src/indexes/vector_stores/couchbase/similaritySearch.ts
new file mode 100644
index 000000000000..ca34556d9733
--- /dev/null
+++ b/examples/src/indexes/vector_stores/couchbase/similaritySearch.ts
@@ -0,0 +1,60 @@
+import { OpenAIEmbeddings } from "@langchain/openai";
+import {
+ CouchbaseVectorStoreArgs,
+ CouchbaseVectorStore,
+} from "@langchain/community/vectorstores/couchbase";
+import { Cluster } from "couchbase";
+import { readFileSync } from "fs";
+import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
+
+const connectionString = process.env.DB_CONN_STR ?? "couchbase://localhost";
+const databaseUsername = process.env.DB_USERNAME ?? "Administrator";
+const databasePassword = process.env.DB_PASSWORD ?? "Password";
+
+const text = readFileSync("state_of_the_union.txt", "utf8");
+const docs = await new RecursiveCharacterTextSplitter().createDocuments([text]);
+
+const couchbaseClient = await Cluster.connect(connectionString, {
+ username: databaseUsername,
+ password: databasePassword,
+ configProfile: "wanDevelopment",
+});
+
+// Open AI API Key is required to use OpenAIEmbeddings, some other embeddings may also be used
+const embeddings = new OpenAIEmbeddings({
+ openAIApiKey: process.env.OPENAI_API_KEY,
+});
+
+const couchbaseConfig: CouchbaseVectorStoreArgs = {
+ cluster: couchbaseClient,
+ bucketName: "testing",
+ scopeName: "_default",
+ collectionName: "_default",
+ indexName: "vector-index",
+ textKey: "text",
+ embeddingKey: "embedding",
+};
+
+const store = await CouchbaseVectorStore.fromDocuments(
+ docs,
+ embeddings,
+ couchbaseConfig
+);
+
+const query = "What did president say about Ketanji Brown Jackson";
+
+const resultsSimilaritySearch = await store.similaritySearch(query);
+console.log("resulting documents: ", resultsSimilaritySearch[0]);
+
+// Similarity Search With Score
+const resultsSimilaritySearchWithScore = await store.similaritySearchWithScore(
+ query,
+ 1
+);
+console.log("resulting documents: ", resultsSimilaritySearchWithScore[0][0]);
+console.log("resulting scores: ", resultsSimilaritySearchWithScore[0][1]);
+
+const result = await store.similaritySearch(query, 1, {
+ fields: ["metadata.source"],
+});
+console.log(result[0]);
diff --git a/langchain/package.json b/langchain/package.json
index 4bd712b0a6e0..5e40bf1b9323 100644
--- a/langchain/package.json
+++ b/langchain/package.json
@@ -1255,7 +1255,7 @@
"cheerio": "^1.0.0-rc.12",
"chromadb": "^1.5.3",
"convex": "^1.3.1",
- "couchbase": "^4.2.10",
+ "couchbase": "^4.2.11",
"d3-dsv": "^2.0.0",
"dotenv": "^16.0.3",
"dpdm": "^3.12.0",
@@ -1325,7 +1325,7 @@
"cheerio": "^1.0.0-rc.12",
"chromadb": "*",
"convex": "^1.3.1",
- "couchbase": "^4.2.10",
+ "couchbase": "^4.2.11",
"d3-dsv": "^2.0.0",
"epub2": "^3.0.1",
"fast-xml-parser": "*",
diff --git a/libs/langchain-community/.gitignore b/libs/langchain-community/.gitignore
index 27ea0134a2e8..ca303c5592af 100644
--- a/libs/langchain-community/.gitignore
+++ b/libs/langchain-community/.gitignore
@@ -302,6 +302,10 @@ vectorstores/convex.cjs
vectorstores/convex.js
vectorstores/convex.d.ts
vectorstores/convex.d.cts
+vectorstores/couchbase.cjs
+vectorstores/couchbase.js
+vectorstores/couchbase.d.ts
+vectorstores/couchbase.d.cts
vectorstores/elasticsearch.cjs
vectorstores/elasticsearch.js
vectorstores/elasticsearch.d.ts
diff --git a/libs/langchain-community/langchain.config.js b/libs/langchain-community/langchain.config.js
index bacd744e9db8..21c70401224b 100644
--- a/libs/langchain-community/langchain.config.js
+++ b/libs/langchain-community/langchain.config.js
@@ -106,6 +106,7 @@ export const config = {
"vectorstores/closevector/web": "vectorstores/closevector/web",
"vectorstores/cloudflare_vectorize": "vectorstores/cloudflare_vectorize",
"vectorstores/convex": "vectorstores/convex",
+ "vectorstores/couchbase": "vectorstores/couchbase",
"vectorstores/elasticsearch": "vectorstores/elasticsearch",
"vectorstores/faiss": "vectorstores/faiss",
"vectorstores/googlevertexai": "vectorstores/googlevertexai",
@@ -268,6 +269,7 @@ export const config = {
"vectorstores/closevector/web",
"vectorstores/cloudflare_vectorize",
"vectorstores/convex",
+ "vectorstores/couchbase",
"vectorstores/elasticsearch",
"vectorstores/faiss",
"vectorstores/googlevertexai",
diff --git a/libs/langchain-community/package.json b/libs/langchain-community/package.json
index d32b4aebc305..d392843a20b1 100644
--- a/libs/langchain-community/package.json
+++ b/libs/langchain-community/package.json
@@ -122,6 +122,7 @@
"closevector-web": "0.1.6",
"cohere-ai": ">=6.0.0",
"convex": "^1.3.1",
+ "couchbase": "^4.2.11",
"discord.js": "^14.14.1",
"dotenv": "^16.0.3",
"dpdm": "^3.12.0",
@@ -225,6 +226,7 @@
"closevector-web": "0.1.6",
"cohere-ai": "*",
"convex": "^1.3.1",
+ "couchbase": "^4.2.11",
"discord.js": "^14.14.1",
"dria": "^0.0.3",
"faiss-node": "^0.5.1",
@@ -418,6 +420,9 @@
"convex": {
"optional": true
},
+ "couchbase": {
+ "optional": true
+ },
"discord.js": {
"optional": true
},
@@ -1203,6 +1208,15 @@
"import": "./vectorstores/convex.js",
"require": "./vectorstores/convex.cjs"
},
+ "./vectorstores/couchbase": {
+ "types": {
+ "import": "./vectorstores/couchbase.d.ts",
+ "require": "./vectorstores/couchbase.d.cts",
+ "default": "./vectorstores/couchbase.d.ts"
+ },
+ "import": "./vectorstores/couchbase.js",
+ "require": "./vectorstores/couchbase.cjs"
+ },
"./vectorstores/elasticsearch": {
"types": {
"import": "./vectorstores/elasticsearch.d.ts",
@@ -2438,6 +2452,10 @@
"vectorstores/convex.js",
"vectorstores/convex.d.ts",
"vectorstores/convex.d.cts",
+ "vectorstores/couchbase.cjs",
+ "vectorstores/couchbase.js",
+ "vectorstores/couchbase.d.ts",
+ "vectorstores/couchbase.d.cts",
"vectorstores/elasticsearch.cjs",
"vectorstores/elasticsearch.js",
"vectorstores/elasticsearch.d.ts",
diff --git a/libs/langchain-community/src/load/import_constants.ts b/libs/langchain-community/src/load/import_constants.ts
index bf5526878cb5..996fdebec21a 100644
--- a/libs/langchain-community/src/load/import_constants.ts
+++ b/libs/langchain-community/src/load/import_constants.ts
@@ -43,6 +43,7 @@ export const optionalImportEntrypoints: string[] = [
"langchain_community/vectorstores/closevector/web",
"langchain_community/vectorstores/cloudflare_vectorize",
"langchain_community/vectorstores/convex",
+ "langchain_community/vectorstores/couchbase",
"langchain_community/vectorstores/elasticsearch",
"langchain_community/vectorstores/faiss",
"langchain_community/vectorstores/googlevertexai",
diff --git a/libs/langchain-community/src/vectorstores/couchbase.ts b/libs/langchain-community/src/vectorstores/couchbase.ts
new file mode 100644
index 000000000000..c893b5bfbabc
--- /dev/null
+++ b/libs/langchain-community/src/vectorstores/couchbase.ts
@@ -0,0 +1,606 @@
+/* eslint-disable no-param-reassign */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { EmbeddingsInterface } from "@langchain/core/embeddings";
+import { VectorStore } from "@langchain/core/vectorstores";
+import {
+ Bucket,
+ Cluster,
+ Collection,
+ Scope,
+ SearchRequest,
+ VectorQuery,
+ VectorSearch,
+} from "couchbase";
+import { Document } from "@langchain/core/documents";
+import { v4 as uuid } from "uuid";
+
+/**
+ * This interface define the optional fields for adding vector
+ * - `ids` - vector of ids for each document. If undefined, then uuid will be used
+ * - `metadata` - vector of metadata object for each document
+ */
+export interface AddVectorOptions {
+ ids?: string[];
+ metadata?: Record[];
+}
+
+/**
+ * This interface defines the fields required to initialize a vector store
+ * These are the fields part of config:
+ * @property {Cluster} cluster - The Couchbase cluster that the store will interact with.
+ * @property {string} bucketName - The name of the bucket in the Couchbase cluster.
+ * @property {string} scopeName - The name of the scope within the bucket.
+ * @property {string} collectionName - The name of the collection within the scope.
+ * @property {string} indexName - The name of the index to be used for vector search.
+ * @property {string} textKey - The key to be used for text in the documents. Defaults to "text".
+ * @property {string} embeddingKey - The key to be used for embeddings in the documents. If not provided, defaults to undefined.
+ * @property {boolean} scopedIndex - Whether to use a scoped index for vector search. Defaults to true.
+ * @property {AddVectorOptions} addVectorOptions - Options for adding vectors with specific id/metadata
+ */
+export interface CouchbaseVectorStoreArgs {
+ cluster: Cluster;
+ bucketName: string;
+ scopeName: string;
+ collectionName: string;
+ indexName: string;
+ textKey?: string;
+ embeddingKey?: string;
+ scopedIndex?: boolean;
+ addVectorOptions?: AddVectorOptions;
+}
+
+/**
+ * This type defines the search filters used in couchbase vector search
+ * - `fields`: Optional list of fields to include in the
+ * metadata of results. Note that these need to be stored in the index.
+ * If nothing is specified, defaults to all the fields stored in the index.
+ * - `searchOptions`: Optional search options that are passed to Couchbase search. Defaults to empty object.
+ */
+type CouchbaseVectorStoreFilter = {
+ fields?: any;
+ searchOptions?: any;
+};
+
+/**
+ * Class for interacting with the Couchbase database. It extends the
+ * VectorStore class and provides methods for adding vectors and
+ * documents, and searching for similar vectors.
+ * Initiate the class using initialize() method.
+ */
+export class CouchbaseVectorStore extends VectorStore {
+ declare FilterType: CouchbaseVectorStoreFilter;
+
+ private metadataKey = "metadata";
+
+ private readonly defaultTextKey = "text";
+
+ private readonly defaultScopedIndex = true;
+
+ private cluster: Cluster;
+
+ private _bucket: Bucket;
+
+ private _scope: Scope;
+
+ private _collection: Collection;
+
+ private bucketName: string;
+
+ private scopeName: string;
+
+ private collectionName: string;
+
+ private indexName: string;
+
+ private textKey = "text";
+
+ private embeddingKey: string;
+
+ private scopedIndex: boolean;
+
+ /**
+ * The private constructor used to provide embedding to parent class.
+ * Initialize the class using static initialize() method
+ * @param embedding - object to generate embedding
+ * @param config - the fields required to initialize a vector store
+ */
+ private constructor(
+ embedding: EmbeddingsInterface,
+ config: CouchbaseVectorStoreArgs
+ ) {
+ super(embedding, config);
+ }
+
+ /**
+ * initialize class for interacting with the Couchbase database.
+ * It extends the VectorStore class and provides methods
+ * for adding vectors and documents, and searching for similar vectors.
+ * This also verifies the params
+ *
+ * @param embeddings - object to generate embedding
+ * @param config - the fields required to initialize a vector store
+ */
+ static async initialize(
+ embeddings: EmbeddingsInterface,
+ config: CouchbaseVectorStoreArgs
+ ) {
+ const store = new CouchbaseVectorStore(embeddings, config);
+
+ const {
+ cluster,
+ bucketName,
+ scopeName,
+ collectionName,
+ indexName,
+ textKey,
+ embeddingKey,
+ scopedIndex,
+ } = config;
+
+ store.cluster = cluster;
+ store.bucketName = bucketName;
+ store.scopeName = scopeName;
+ store.collectionName = collectionName;
+ store.indexName = indexName;
+ if (textKey) {
+ store.textKey = textKey;
+ } else {
+ store.textKey = store.defaultTextKey;
+ }
+
+ if (embeddingKey) {
+ store.embeddingKey = embeddingKey;
+ } else {
+ store.embeddingKey = `${store.textKey}_embedding`;
+ }
+
+ if (scopedIndex !== undefined) {
+ store.scopedIndex = scopedIndex;
+ } else {
+ store.scopedIndex = store.defaultScopedIndex;
+ }
+
+ try {
+ store._bucket = store.cluster.bucket(store.bucketName);
+ store._scope = store._bucket.scope(store.scopeName);
+ store._collection = store._scope.collection(store.collectionName);
+ } catch (err) {
+ throw new Error(
+ "Error connecting to couchbase, Please check connection and credentials"
+ );
+ }
+
+ try {
+ if (
+ !(await store.checkBucketExists()) ||
+ !(await store.checkIndexExists()) ||
+ !(await store.checkScopeAndCollectionExists())
+ ) {
+ throw new Error("Error while initializing vector store");
+ }
+ } catch (err) {
+ throw new Error(`Error while initializing vector store: ${err}`);
+ }
+ return store;
+ }
+
+ /**
+ * An asynchrononous method to verify the search indexes.
+ * It retrieves all indexes and checks if specified index is present.
+ *
+ * @throws - If the specified index does not exist in the database.
+ *
+ * @returns - returns promise true if no error is found
+ */
+ private async checkIndexExists(): Promise {
+ if (this.scopedIndex) {
+ const allIndexes = await this._scope.searchIndexes().getAllIndexes();
+ const indexNames = allIndexes.map((index) => index.name);
+ if (!indexNames.includes(this.indexName)) {
+ throw new Error(
+ `Index ${this.indexName} does not exist. Please create the index before searching.`
+ );
+ }
+ } else {
+ const allIndexes = await this.cluster.searchIndexes().getAllIndexes();
+ const indexNames = allIndexes.map((index) => index.name);
+ if (!indexNames.includes(this.indexName)) {
+ throw new Error(
+ `Index ${this.indexName} does not exist. Please create the index before searching.`
+ );
+ }
+ }
+ return true;
+ }
+
+ /**
+ * An asynchronous method to verify the existence of a bucket.
+ * It retrieves the bucket using the bucket manager and checks if the specified bucket is present.
+ *
+ * @throws - If the specified bucket does not exist in the database.
+ *
+ * @returns - Returns a promise that resolves to true if no error is found, indicating the bucket exists.
+ */
+ private async checkBucketExists(): Promise {
+ const bucketManager = this.cluster.buckets();
+ try {
+ await bucketManager.getBucket(this.bucketName);
+ return true;
+ } catch (error) {
+ throw new Error(
+ `Bucket ${this.bucketName} does not exist. Please create the bucket before searching.`
+ );
+ }
+ }
+
+ /**
+ * An asynchronous method to verify the existence of a scope and a collection within that scope.
+ * It retrieves all scopes and collections in the bucket, and checks if the specified scope and collection are present.
+ *
+ * @throws - If the specified scope does not exist in the bucket, or if the specified collection does not exist in the scope.
+ *
+ * @returns - Returns a promise that resolves to true if no error is found, indicating the scope and collection exist.
+ */
+ private async checkScopeAndCollectionExists(): Promise {
+ const scopeCollectionMap: Record = {};
+
+ // Get a list of all scopes in the bucket
+ const scopes = await this._bucket.collections().getAllScopes();
+ for (const scope of scopes) {
+ scopeCollectionMap[scope.name] = [];
+
+ // Get a list of all the collections in the scope
+ for (const collection of scope.collections) {
+ scopeCollectionMap[scope.name].push(collection.name);
+ }
+ }
+
+ // Check if the scope exists
+ if (!Object.keys(scopeCollectionMap).includes(this.scopeName)) {
+ throw new Error(
+ `Scope ${this.scopeName} not found in Couchbase bucket ${this.bucketName}`
+ );
+ }
+
+ // Check if the collection exists in the scope
+ if (!scopeCollectionMap[this.scopeName].includes(this.collectionName)) {
+ throw new Error(
+ `Collection ${this.collectionName} not found in scope ${this.scopeName} in Couchbase bucket ${this.bucketName}`
+ );
+ }
+
+ return true;
+ }
+
+ _vectorstoreType(): string {
+ return "couchbase";
+ }
+
+ /**
+ * Formats couchbase metadata by removing `metadata.` from initials
+ * @param fields - all the fields of row
+ * @returns - formatted metadata fields
+ */
+ private formatMetadata = (fields: any) => {
+ delete fields[this.textKey];
+ const metadataFields: { [key: string]: any } = {};
+ // eslint-disable-next-line guard-for-in
+ for (const key in fields) {
+ const newKey = key.replace(`${this.metadataKey}.`, "");
+ metadataFields[newKey] = fields[key];
+ }
+ return metadataFields;
+ };
+
+ /**
+ * Performs a similarity search on the vectors in the Couchbase database and returns the documents and their corresponding scores.
+ *
+ * @param queryEmbeddings - Embedding vector to look up documents similar to.
+ * @param k - Number of documents to return. Defaults to 4.
+ * @param filter - Optional search filter that are passed to Couchbase search. Defaults to empty object.
+ * - `fields`: Optional list of fields to include in the
+ * metadata of results. Note that these need to be stored in the index.
+ * If nothing is specified, defaults to all the fields stored in the index.
+ * - `searchOptions`: Optional search options that are passed to Couchbase search. Defaults to empty object.
+ *
+ * @returns - Promise of list of [document, score] that are the most similar to the query vector.
+ *
+ * @throws If the search operation fails.
+ */
+ async similaritySearchVectorWithScore(
+ queryEmbeddings: number[],
+ k = 4,
+ filter: CouchbaseVectorStoreFilter = {}
+ ): Promise<[Document, number][]> {
+ let { fields } = filter;
+ const { searchOptions } = filter;
+
+ if (!fields) {
+ fields = ["*"];
+ }
+ if (
+ !(fields.length === 1 && fields[0] === "*") &&
+ !fields.includes(this.textKey)
+ ) {
+ fields.push(this.textKey);
+ }
+
+ const searchRequest = new SearchRequest(
+ VectorSearch.fromVectorQuery(
+ new VectorQuery(this.embeddingKey, queryEmbeddings).numCandidates(k)
+ )
+ );
+
+ let searchIterator;
+ const docsWithScore: [Document, number][] = [];
+ try {
+ if (this.scopedIndex) {
+ searchIterator = this._scope.search(this.indexName, searchRequest, {
+ limit: k,
+ fields,
+ raw: searchOptions,
+ });
+ } else {
+ searchIterator = this.cluster.search(this.indexName, searchRequest, {
+ limit: k,
+ fields,
+ raw: searchOptions,
+ });
+ }
+
+ const searchRows = (await searchIterator).rows;
+ for (const row of searchRows) {
+ const text = row.fields[this.textKey];
+ const metadataFields = this.formatMetadata(row.fields);
+ const searchScore = row.score;
+ const doc = new Document({
+ pageContent: text,
+ metadata: metadataFields,
+ });
+ docsWithScore.push([doc, searchScore]);
+ }
+ } catch (err) {
+ console.log("error received");
+ throw new Error(`Search failed with error: ${err}`);
+ }
+ return docsWithScore;
+ }
+
+ /**
+ * Return documents that are most similar to the vector embedding.
+ *
+ * @param queryEmbeddings - Embedding to look up documents similar to.
+ * @param k - The number of similar documents to return. Defaults to 4.
+ * @param filter - Optional search filter that are passed to Couchbase search. Defaults to empty object.
+ * - `fields`: Optional list of fields to include in the
+ * metadata of results. Note that these need to be stored in the index.
+ * If nothing is specified, defaults to all the fields stored in the index.
+ * - `searchOptions`: Optional search options that are passed to Couchbase search. Defaults to empty object.
+ *
+ * @returns - A promise that resolves to an array of documents that match the similarity search.
+ */
+ async similaritySearchByVector(
+ queryEmbeddings: number[],
+ k = 4,
+ filter: CouchbaseVectorStoreFilter = {}
+ ): Promise {
+ const docsWithScore = await this.similaritySearchVectorWithScore(
+ queryEmbeddings,
+ k,
+ filter
+ );
+ const docs = [];
+ for (const doc of docsWithScore) {
+ docs.push(doc[0]);
+ }
+ return docs;
+ }
+
+ /**
+ * Return documents that are most similar to the query.
+ *
+ * @param query - Query to look up for similar documents
+ * @param k - The number of similar documents to return. Defaults to 4.
+ * @param filter - Optional search filter that are passed to Couchbase search. Defaults to empty object.
+ * - `fields`: Optional list of fields to include in the
+ * metadata of results. Note that these need to be stored in the index.
+ * If nothing is specified, defaults to all the fields stored in the index.
+ * - `searchOptions`: Optional search options that are passed to Couchbase search. Defaults to empty object.
+ *
+ * @returns - Promise of list of documents that are most similar to the query.
+ */
+ async similaritySearch(
+ query: string,
+ k = 4,
+ filter: CouchbaseVectorStoreFilter = {}
+ ): Promise {
+ const queryEmbeddings = await this.embeddings.embedQuery(query);
+ const docsWithScore = await this.similaritySearchVectorWithScore(
+ queryEmbeddings,
+ k,
+ filter
+ );
+ const docs = [];
+ for (const doc of docsWithScore) {
+ docs.push(doc[0]);
+ }
+ return docs;
+ }
+
+ /**
+ * Return documents that are most similar to the query with their scores.
+ *
+ * @param query - Query to look up for similar documents
+ * @param k - The number of similar documents to return. Defaults to 4.
+ * @param filter - Optional search filter that are passed to Couchbase search. Defaults to empty object.
+ * - `fields`: Optional list of fields to include in the
+ * metadata of results. Note that these need to be stored in the index.
+ * If nothing is specified, defaults to all the fields stored in the index.
+ * - `searchOptions`: Optional search options that are passed to Couchbase search. Defaults to empty object.
+ *
+ * @returns - Promise of list of documents that are most similar to the query.
+ */
+ async similaritySearchWithScore(
+ query: string,
+ k = 4,
+ filter: CouchbaseVectorStoreFilter = {}
+ ): Promise<[Document, number][]> {
+ const queryEmbeddings = await this.embeddings.embedQuery(query);
+ const docsWithScore = await this.similaritySearchVectorWithScore(
+ queryEmbeddings,
+ k,
+ filter
+ );
+ return docsWithScore;
+ }
+
+ /**
+ * Add vectors and corresponding documents to a couchbase collection
+ * If the document IDs are passed, the existing documents (if any) will be
+ * overwritten with the new ones.
+ * @param vectors - The vectors to be added to the collection.
+ * @param documents - The corresponding documents to be added to the collection.
+ * @param options - Optional parameters for adding vectors.
+ * This may include the IDs and metadata of the documents to be added. Defaults to an empty object.
+ *
+ * @returns - A promise that resolves to an array of document IDs that were added to the collection.
+ */
+ public async addVectors(
+ vectors: number[][],
+ documents: Document[],
+ options: AddVectorOptions = {}
+ ): Promise {
+ // Get document ids. if ids are not available then use UUIDs for each document
+ let ids: string[] | undefined = options ? options.ids : undefined;
+ if (ids === undefined) {
+ ids = Array.from({ length: documents.length }, () => uuid());
+ }
+
+ // Get metadata for each document. if metadata is not available, use empty object for each document
+ let metadata: any = options ? options.metadata : undefined;
+ if (metadata === undefined) {
+ metadata = Array.from({ length: documents.length }, () => ({}));
+ }
+
+ const documentsToInsert = ids.map((id: string, index: number) => ({
+ [id]: {
+ [this.textKey]: documents[index].pageContent,
+ [this.embeddingKey]: vectors[index],
+ [this.metadataKey]: metadata[index],
+ },
+ }));
+
+ const docIds: string[] = [];
+ for (const document of documentsToInsert) {
+ try {
+ const currentDocumentKey = Object.keys(document)[0];
+ await this._collection.upsert(
+ currentDocumentKey,
+ document[currentDocumentKey]
+ );
+ docIds.push(currentDocumentKey);
+ } catch (e) {
+ console.log("error received while upserting document", e);
+ }
+ }
+
+ return docIds;
+ }
+
+ /**
+ * Run texts through the embeddings and persist in vectorstore.
+ * If the document IDs are passed, the existing documents (if any) will be
+ * overwritten with the new ones.
+ * @param documents - The corresponding documents to be added to the collection.
+ * @param options - Optional parameters for adding documents.
+ * This may include the IDs and metadata of the documents to be added. Defaults to an empty object.
+ *
+ * @returns - A promise that resolves to an array of document IDs that were added to the collection.
+ */
+ public async addDocuments(
+ documents: Document[],
+ options: AddVectorOptions = {}
+ ) {
+ const texts = documents.map(({ pageContent }) => pageContent);
+ const metadatas = documents.map((doc) => doc.metadata);
+ if (!options.metadata) {
+ options.metadata = metadatas;
+ }
+ return this.addVectors(
+ await this.embeddings.embedDocuments(texts),
+ documents,
+ options
+ );
+ }
+
+ /**
+ * Create a new CouchbaseVectorStore from a set of documents.
+ * This function will initialize a new store, add the documents to it, and then return the store.
+ * @param documents - The documents to be added to the new store.
+ * @param embeddings - The embeddings to be used for the documents.
+ * @param config - The configuration for the new CouchbaseVectorStore. This includes the options for adding vectors.
+ *
+ * @returns - A promise that resolves to the new CouchbaseVectorStore that contains the added documents.
+ */
+ static async fromDocuments(
+ documents: Document[],
+ embeddings: EmbeddingsInterface,
+ config: CouchbaseVectorStoreArgs
+ ): Promise {
+ const store = await this.initialize(embeddings, config);
+ await store.addDocuments(documents, config.addVectorOptions);
+ return store;
+ }
+
+ /**
+ * Create a new CouchbaseVectorStore from a set of texts.
+ * This function will convert each text and its corresponding metadata into a Document,
+ * initialize a new store, add the documents to it, and then return the store.
+ * @param texts - The texts to be converted into Documents and added to the new store.
+ * @param metadatas - The metadata for each text. If an array is passed, each text will have its corresponding metadata.
+ * If not, all texts will have the same metadata.
+ * @param embeddings - The embeddings to be used for the documents.
+ * @param config - The configuration for the new CouchbaseVectorStore. This includes the options for adding vectors.
+ *
+ * @returns - A promise that resolves to the new CouchbaseVectorStore that contains the added documents.
+ */
+ static async fromTexts(
+ texts: string[],
+ metadatas: any,
+ embeddings: EmbeddingsInterface,
+ config: CouchbaseVectorStoreArgs
+ ): Promise {
+ const docs = [];
+
+ for (let i = 0; i < texts.length; i += 1) {
+ const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;
+ const newDoc = new Document({
+ pageContent: texts[i],
+ metadata,
+ });
+ docs.push(newDoc);
+ }
+ return await this.fromDocuments(docs, embeddings, config);
+ }
+
+ /**
+ * Delete documents from the collection.
+ * This function will attempt to remove each document in the provided list of IDs from the collection.
+ * If an error occurs during the deletion of a document, an error will be thrown with the ID of the document and the error message.
+ * @param ids - An array of document IDs to be deleted from the collection.
+ *
+ * @returns - A promise that resolves when all documents have been attempted to be deleted. If a document could not be deleted, an error is thrown.
+ */
+ public async delete(ids: string[]): Promise {
+ for (let i = 0; i < ids.length; i += 1) {
+ const removeId = ids[i];
+ try {
+ await this._collection.remove(removeId);
+ } catch (err) {
+ throw new Error(
+ `Error while deleting document - Document Id: ${ids[i]}, Error: ${err}`
+ );
+ }
+ }
+ }
+}
diff --git a/libs/langchain-community/src/vectorstores/tests/couchbase.int.test.ts b/libs/langchain-community/src/vectorstores/tests/couchbase.int.test.ts
new file mode 100644
index 000000000000..76f7e8ad266e
--- /dev/null
+++ b/libs/langchain-community/src/vectorstores/tests/couchbase.int.test.ts
@@ -0,0 +1,240 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable no-process-env */
+import { describe, test } from "@jest/globals";
+import { Cluster } from "couchbase";
+import { OpenAIEmbeddings } from "@langchain/openai";
+import { Document } from "@langchain/core/documents";
+import {
+ CouchbaseVectorStore,
+ CouchbaseVectorStoreArgs,
+} from "../couchbase.js";
+
+describe.skip("Couchbase vector store", () => {
+ const connectionString = process.env.DB_CONN_STR ?? "couchbase://localhost";
+ const databaseUsername = process.env.DB_USERNAME ?? "Administrator";
+ const databasePassword = process.env.DB_PASSWORD ?? "Password";
+ const bucketName = process.env.DB_BUCKET_NAME ?? "testing";
+ const scopeName = process.env.DB_SCOPE_NAME ?? "_default";
+ const collectionName = process.env.DB_COLLECTION_NAME ?? "_default";
+ const indexName = process.env.DB_INDEX_NAME ?? "vector-index";
+ const textFieldKey = "text";
+ const embeddingFieldKey = "embedding";
+ const isScopedIndex = true;
+ let couchbaseClient: Cluster;
+ let embeddings: OpenAIEmbeddings;
+
+ const texts = [
+ "Couchbase, built on a key-value store, offers efficient data operations.",
+ "As a NoSQL database, Couchbase provides scalability and flexibility to handle diverse data types.",
+ "Couchbase supports N1QL, a SQL-like language, easing the transition for developers familiar with SQL.",
+ "Couchbase ensures high availability with built-in fault tolerance and automatic multi-master replication.",
+ "With its memory-first architecture, Couchbase delivers high performance and low latency data access.",
+ ];
+
+ const metadata = [
+ { id: "101", name: "Efficient Operator" },
+ { id: "102", name: "Flexible Storer" },
+ { id: "103", name: "Quick Performer" },
+ { id: "104", name: "Reliable Guardian" },
+ { id: "105", name: "Adaptable Navigator" },
+ ];
+
+ beforeEach(async () => {
+ couchbaseClient = await Cluster.connect(connectionString, {
+ username: databaseUsername,
+ password: databasePassword,
+ configProfile: "wanDevelopment",
+ });
+
+ embeddings = new OpenAIEmbeddings({
+ openAIApiKey: process.env.OPENAI_API_KEY,
+ });
+ });
+
+ test("from Texts to vector store", async () => {
+ const couchbaseConfig: CouchbaseVectorStoreArgs = {
+ cluster: couchbaseClient,
+ bucketName,
+ scopeName,
+ collectionName,
+ indexName,
+ textKey: textFieldKey,
+ embeddingKey: embeddingFieldKey,
+ scopedIndex: isScopedIndex,
+ };
+
+ const store = await CouchbaseVectorStore.fromTexts(
+ texts,
+ metadata,
+ embeddings,
+ couchbaseConfig
+ );
+ const results = await store.similaritySearchWithScore(texts[0], 1);
+
+ expect(results.length).toEqual(1);
+ expect(results[0][0].pageContent).toEqual(texts[0]);
+ expect(results[0][0].metadata.name).toEqual(metadata[0].name);
+ expect(results[0][0].metadata.id).toEqual(metadata[0].id);
+ });
+
+ test("Add and delete Documents to vector store", async () => {
+ const couchbaseConfig: CouchbaseVectorStoreArgs = {
+ cluster: couchbaseClient,
+ bucketName,
+ scopeName,
+ collectionName,
+ indexName,
+ textKey: textFieldKey,
+ embeddingKey: embeddingFieldKey,
+ scopedIndex: isScopedIndex,
+ };
+
+ const documents: Document[] = [];
+ for (let i = 0; i < texts.length; i += 1) {
+ documents.push({
+ pageContent: texts[i],
+ metadata: {},
+ });
+ }
+
+ const store = await CouchbaseVectorStore.initialize(
+ embeddings,
+ couchbaseConfig
+ );
+ const ids = await store.addDocuments(documents, {
+ ids: metadata.map((val) => val.id),
+ metadata: metadata.map((val) => {
+ const metadataObj = {
+ name: val.name,
+ };
+ return metadataObj;
+ }),
+ });
+
+ expect(ids.length).toEqual(texts.length);
+ for (let i = 0; i < ids.length; i += 1) {
+ expect(ids[i]).toEqual(metadata[i].id);
+ }
+
+ const results = await store.similaritySearch(texts[1], 1);
+
+ expect(results.length).toEqual(1);
+ expect(results[0].pageContent).toEqual(texts[1]);
+ expect(results[0].metadata.name).toEqual(metadata[1].name);
+
+ await store.delete(ids);
+ const cbCollection = couchbaseClient
+ .bucket(bucketName)
+ .scope(scopeName)
+ .collection(collectionName);
+ expect((await cbCollection.exists(ids[0])).exists).toBe(false);
+ expect((await cbCollection.exists(ids[4])).exists).toBe(false);
+
+ const resultsDeleted = await store.similaritySearch(texts[1], 1);
+ expect(resultsDeleted.length).not.toEqual(1);
+ });
+
+ test("hybrid search", async () => {
+ const couchbaseConfig: CouchbaseVectorStoreArgs = {
+ cluster: couchbaseClient,
+ bucketName,
+ scopeName,
+ collectionName,
+ indexName,
+ textKey: textFieldKey,
+ embeddingKey: embeddingFieldKey,
+ scopedIndex: isScopedIndex,
+ };
+
+ const query = `Couchbase offers impressive memory-first performance for your important applications.`;
+
+ const hybridSearchMetadata: { [key: string]: any }[] = [];
+
+ // Add More Metadata
+ for (let i = 0; i < texts.length; i += 1) {
+ const doc: { [key: string]: any } = {};
+ doc.date = `${2020 + (i % 10)}-01-01`;
+ doc.rating = 1 + (i % 5);
+ doc.author = ["John Doe", "Jane Doe"][(i + 1) % 2];
+ doc.id = (i + 100).toString();
+ hybridSearchMetadata.push(doc);
+ }
+ const store = await CouchbaseVectorStore.fromTexts(
+ texts,
+ hybridSearchMetadata,
+ embeddings,
+ couchbaseConfig
+ );
+
+ const resultsSimilaritySearch = await store.similaritySearch(query, 1);
+ expect(resultsSimilaritySearch.length).toEqual(1);
+ expect(resultsSimilaritySearch[0].metadata.date).not.toEqual(undefined);
+
+ // search by exact value in metadata
+ const exactValueResult = await store.similaritySearch(query, 4, {
+ fields: ["metadata.author"],
+ searchOptions: {
+ query: { field: "metadata.author", match: "John Doe" },
+ },
+ });
+
+ expect(exactValueResult.length).toEqual(4);
+ expect(exactValueResult[0].metadata.author).toEqual("John Doe");
+
+ // search by partial match in metadata
+ const partialMatchResult = await store.similaritySearch(query, 4, {
+ fields: ["metadata.author"],
+ searchOptions: {
+ query: { field: "metadata.author", match: "Johny", fuzziness: 1 },
+ },
+ });
+
+ expect(partialMatchResult.length).toEqual(4);
+ expect(partialMatchResult[0].metadata.author).toEqual("John Doe");
+
+ // search by date range
+ const dateRangeResult = await store.similaritySearch(query, 4, {
+ fields: ["metadata.date", "metadata.author"],
+ searchOptions: {
+ query: {
+ start: "2022-12-31",
+ end: "2023-01-02",
+ inclusiveStart: true,
+ inclusiveEnd: false,
+ field: "metadata.date",
+ },
+ },
+ });
+
+ expect(dateRangeResult.length).toEqual(4);
+
+ // search by rating range
+ const ratingRangeResult = await store.similaritySearch(texts[0], 4, {
+ fields: ["metadata.rating"],
+ searchOptions: {
+ query: {
+ min: 3,
+ max: 5,
+ inclusiveMin: false,
+ inclusiveMax: true,
+ field: "metadata.rating",
+ },
+ },
+ });
+ expect(ratingRangeResult.length).toEqual(4);
+
+ // multiple search conditions
+ const multipleConditionsResult = await store.similaritySearch(texts[0], 4, {
+ fields: ["metadata.rating", "metadata.date"],
+ searchOptions: {
+ query: {
+ conjuncts: [
+ { min: 3, max: 4, inclusive_max: true, field: "metadata.rating" },
+ { start: "2022-12-31", end: "2023-01-02", field: "metadata.date" },
+ ],
+ },
+ },
+ });
+ expect(multipleConditionsResult.length).toEqual(4);
+ });
+});
diff --git a/yarn.lock b/yarn.lock
index e71c060f8991..c48c7461b2d9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6645,44 +6645,44 @@ __metadata:
languageName: node
linkType: hard
-"@couchbase/couchbase-darwin-arm64-napi@npm:4.2.10":
- version: 4.2.10
- resolution: "@couchbase/couchbase-darwin-arm64-napi@npm:4.2.10"
+"@couchbase/couchbase-darwin-arm64-napi@npm:4.2.11":
+ version: 4.2.11
+ resolution: "@couchbase/couchbase-darwin-arm64-napi@npm:4.2.11"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
-"@couchbase/couchbase-darwin-x64-napi@npm:4.2.10":
- version: 4.2.10
- resolution: "@couchbase/couchbase-darwin-x64-napi@npm:4.2.10"
+"@couchbase/couchbase-darwin-x64-napi@npm:4.2.11":
+ version: 4.2.11
+ resolution: "@couchbase/couchbase-darwin-x64-napi@npm:4.2.11"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
-"@couchbase/couchbase-linux-arm64-napi@npm:4.2.10":
- version: 4.2.10
- resolution: "@couchbase/couchbase-linux-arm64-napi@npm:4.2.10"
+"@couchbase/couchbase-linux-arm64-napi@npm:4.2.11":
+ version: 4.2.11
+ resolution: "@couchbase/couchbase-linux-arm64-napi@npm:4.2.11"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
-"@couchbase/couchbase-linux-x64-napi@npm:4.2.10":
- version: 4.2.10
- resolution: "@couchbase/couchbase-linux-x64-napi@npm:4.2.10"
+"@couchbase/couchbase-linux-x64-napi@npm:4.2.11":
+ version: 4.2.11
+ resolution: "@couchbase/couchbase-linux-x64-napi@npm:4.2.11"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
-"@couchbase/couchbase-linuxmusl-x64-napi@npm:4.2.10":
- version: 4.2.10
- resolution: "@couchbase/couchbase-linuxmusl-x64-napi@npm:4.2.10"
+"@couchbase/couchbase-linuxmusl-x64-napi@npm:4.2.11":
+ version: 4.2.11
+ resolution: "@couchbase/couchbase-linuxmusl-x64-napi@npm:4.2.11"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
-"@couchbase/couchbase-win32-x64-napi@npm:4.2.10":
- version: 4.2.10
- resolution: "@couchbase/couchbase-win32-x64-napi@npm:4.2.10"
+"@couchbase/couchbase-win32-x64-napi@npm:4.2.11":
+ version: 4.2.11
+ resolution: "@couchbase/couchbase-win32-x64-napi@npm:4.2.11"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@@ -8999,6 +8999,7 @@ __metadata:
closevector-web: 0.1.6
cohere-ai: ">=6.0.0"
convex: ^1.3.1
+ couchbase: ^4.2.11
discord.js: ^14.14.1
dotenv: ^16.0.3
dpdm: ^3.12.0
@@ -9105,6 +9106,7 @@ __metadata:
closevector-web: 0.1.6
cohere-ai: "*"
convex: ^1.3.1
+ couchbase: ^4.2.11
discord.js: ^14.14.1
dria: ^0.0.3
faiss-node: ^0.5.1
@@ -9244,6 +9246,8 @@ __metadata:
optional: true
convex:
optional: true
+ couchbase:
+ optional: true
discord.js:
optional: true
dria:
@@ -18770,16 +18774,16 @@ __metadata:
languageName: node
linkType: hard
-"couchbase@npm:^4.2.10":
- version: 4.2.10
- resolution: "couchbase@npm:4.2.10"
- dependencies:
- "@couchbase/couchbase-darwin-arm64-napi": 4.2.10
- "@couchbase/couchbase-darwin-x64-napi": 4.2.10
- "@couchbase/couchbase-linux-arm64-napi": 4.2.10
- "@couchbase/couchbase-linux-x64-napi": 4.2.10
- "@couchbase/couchbase-linuxmusl-x64-napi": 4.2.10
- "@couchbase/couchbase-win32-x64-napi": 4.2.10
+"couchbase@npm:^4.2.11":
+ version: 4.2.11
+ resolution: "couchbase@npm:4.2.11"
+ dependencies:
+ "@couchbase/couchbase-darwin-arm64-napi": 4.2.11
+ "@couchbase/couchbase-darwin-x64-napi": 4.2.11
+ "@couchbase/couchbase-linux-arm64-napi": 4.2.11
+ "@couchbase/couchbase-linux-x64-napi": 4.2.11
+ "@couchbase/couchbase-linuxmusl-x64-napi": 4.2.11
+ "@couchbase/couchbase-win32-x64-napi": 4.2.11
cmake-js: ^7.2.1
node-addon-api: ^7.0.0
dependenciesMeta:
@@ -18795,7 +18799,7 @@ __metadata:
optional: true
"@couchbase/couchbase-win32-x64-napi":
optional: true
- checksum: 1cc4725c5f16c3173691a9e4f702e479df545473deac694f7a8627f58a63a92718824d018730b51a7d4d6a0a8e125b0ef5f3f81cf995a831b8a3adfa05e9ecc7
+ checksum: f1987b8cbe1763d2f30fcd5a51ab5fb1c6c3828ab3e7fa744bc4911e091f3aac89c918960f4664d539e57ff4b2b30d1a7fe5a69c44dc02f60062794c5d45c6de
languageName: node
linkType: hard
@@ -21426,6 +21430,7 @@ __metadata:
axios: ^0.26.0
chromadb: ^1.5.3
convex: ^1.3.1
+ couchbase: ^4.2.11
date-fns: ^3.3.1
dotenv: ^16.0.3
eslint: ^8.33.0
@@ -26122,7 +26127,7 @@ __metadata:
cheerio: ^1.0.0-rc.12
chromadb: ^1.5.3
convex: ^1.3.1
- couchbase: ^4.2.10
+ couchbase: ^4.2.11
d3-dsv: ^2.0.0
dotenv: ^16.0.3
dpdm: ^3.12.0
@@ -26204,7 +26209,7 @@ __metadata:
cheerio: ^1.0.0-rc.12
chromadb: "*"
convex: ^1.3.1
- couchbase: ^4.2.10
+ couchbase: ^4.2.11
d3-dsv: ^2.0.0
epub2: ^3.0.1
fast-xml-parser: "*"