Couchbase
Couchbase 是一款屡获殊荣的分布式 NoSQL 云数据库,为您的所有云、移动、AI 和边缘计算应用程序提供无与伦比的多功能性、性能、可扩展性和经济价值。 Couchbase 通过为开发人员提供编码帮助和为他们的应用程序提供向量搜索来拥抱 AI。
向量搜索是 Couchbase 中 全文搜索服务 (搜索服务) 的一部分。
本教程解释如何在 Couchbase 中使用向量搜索。您可以同时使用 Couchbase Capella 和您自管理的 Couchbase 服务器。
安装
您需要 couchbase 和 langchain 社区才能使用 couchbase 向量存储。在本教程中,我们将使用 OpenAI 嵌入
- npm
- Yarn
- pnpm
npm install couchbase @langchain/openai @langchain/community
yarn add couchbase @langchain/openai @langchain/community
pnpm add couchbase @langchain/openai @langchain/community
创建 Couchbase 连接对象
我们首先创建一个到 Couchbase 集群的连接,然后将集群对象传递给向量存储。在这里,我们使用用户名和密码进行连接。您还可以使用任何其他支持的方式连接到您的集群。
有关连接到 Couchbase 集群的更多信息,请查看 Node SDK 文档。
import { Cluster } from "couchbase";
const connectionString = "couchbase://localhost"; // or couchbases://localhost if you are using TLS
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",
});
创建搜索索引
目前,需要从 Couchbase Capella 或 Server UI 或使用 REST 接口创建搜索索引。
对于此示例,让我们在 UI 上的搜索服务上使用导入索引功能。
让我们在测试桶上定义一个名为 vector-index
的搜索索引。我们正在定义一个索引,该索引位于 testing
桶的 _default
范围内的 _default
集合上,其中向量字段设置为 embedding
,维度为 1536,文本字段设置为 text
。我们还在索引和存储文档中 metadata
下的所有字段,作为动态映射,以适应不同的文档结构。相似性指标设置为 dot_product
。
如何将索引导入到全文搜索服务?
- Couchbase 服务器
- 单击搜索 -> 添加索引 -> 导入
- 将以下索引定义复制到导入屏幕
- 单击创建索引以创建索引。
- Couchbase Capella
- 将以下索引定义复制到一个新文件
index.json
- 按照文档中的说明将文件导入 Capella。
- 单击创建索引以创建索引。
- 将以下索引定义复制到一个新文件
索引定义
{
"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
}
}
有关如何创建支持向量字段的搜索索引的更多详细信息,请参阅文档
为了使用此向量存储,需要配置 CouchbaseVectorStoreArgs。textKey 和 embeddingKey 是可选字段,如果您想使用特定的键,则需要它们
const couchbaseConfig: CouchbaseVectorStoreArgs = {
cluster: couchbaseClient,
bucketName: "testing",
scopeName: "_default",
collectionName: "_default",
indexName: "vector-index",
textKey: "text",
embeddingKey: "embedding",
};
创建向量存储
我们使用集群信息和搜索索引名称创建向量存储对象。
const store = await CouchbaseVectorStore.initialize(
embeddings, // embeddings object to create embeddings from text
couchbaseConfig
);
基本向量搜索示例
以下示例展示了如何使用 couchbase 向量搜索并执行相似性搜索。对于此示例,我们将通过 TextLoader 加载“state_of_the_union.txt”文件,将文本分成 500 个字符的块,没有重叠,并将所有这些块索引到 Couchbase 中。
在对数据进行索引后,我们执行一个简单的查询以找到与查询“总统对凯坦吉·布朗·杰克逊说了什么”相似的四个前四个块。在 emd 处,还显示了如何获取相似性分数
import { OpenAIEmbeddings } from "@langchain/openai";
import {
CouchbaseVectorStoreArgs,
CouchbaseVectorStore,
} from "@langchain/community/vectorstores/couchbase";
import { Cluster } from "couchbase";
import { TextLoader } from "langchain/document_loaders/fs/text";
import { CharacterTextSplitter } from "@langchain/textsplitters";
const connectionString =
process.env.COUCHBASE_DB_CONN_STR ?? "couchbase://localhost";
const databaseUsername = process.env.COUCHBASE_DB_USERNAME ?? "Administrator";
const databasePassword = process.env.COUCHBASE_DB_PASSWORD ?? "Password";
// Load documents from file
const loader = new TextLoader("./state_of_the_union.txt");
const rawDocuments = await loader.load();
const splitter = new CharacterTextSplitter({
chunkSize: 500,
chunkOverlap: 0,
});
const docs = await splitter.splitDocuments(rawDocuments);
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({
apiKey: 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]);
API 参考
- OpenAIEmbeddings 来自
@langchain/openai
- CouchbaseVectorStoreArgs 来自
@langchain/community/vectorstores/couchbase
- CouchbaseVectorStore 来自
@langchain/community/vectorstores/couchbase
- TextLoader 来自
langchain/document_loaders/fs/text
- CharacterTextSplitter 来自
@langchain/textsplitters
指定要返回的字段
您可以在搜索期间使用 fields
参数在过滤器中指定要从文档中返回的字段。这些字段作为 metadata
对象的一部分返回。您可以获取索引中存储的任何字段。文档的 textKey
作为文档的 pageContent
的一部分返回。
如果您没有指定要获取的任何字段,则将返回索引中存储的所有字段。
如果您想获取元数据中的某个字段,则需要使用 .
指定它。例如,要获取元数据中的 source
字段,您需要使用 metadata.source
。
const result = await store.similaritySearch(query, 1, {
fields: ["metadata.source"],
});
console.log(result[0]);
混合搜索
Couchbase 允许您通过将向量搜索结果与文档非向量字段(如 metadata
对象)的搜索相结合来进行混合搜索。
结果将基于向量搜索和全文搜索服务支持的搜索的组合结果。每个组件搜索的分数加起来以获得结果的总分数。
要执行混合搜索,在 fields
参数中有一个可选键 searchOptions
可以传递给所有相似性搜索。
searchOptions
的不同搜索/查询可能性可以在 这里 找到。
为混合搜索创建多样化的元数据
为了模拟混合搜索,让我们从现有文档中创建一些随机元数据。 我们统一在元数据中添加三个字段:date
在 2010 年至 2020 年之间,rating
在 1 到 5 之间,author
设置为 John Doe 或 Jane Doe。 我们还将声明一些示例查询。
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?";
示例:按精确值搜索
我们可以在文本字段(如metadata
对象中的作者)上搜索精确匹配项。
const exactValueResult = await store.similaritySearch(query, 4, {
fields: ["metadata.author"],
searchOptions: {
query: { field: "metadata.author", match: "John Doe" },
},
});
console.log(exactValueResult[0]);
示例:按部分匹配搜索
我们可以通过为搜索指定模糊度来搜索部分匹配项。 当您想搜索搜索查询的细微变化或拼写错误时,这很有用。
这里,“Johny”与“John Doe”非常接近(模糊度为 1)。
const partialMatchResult = await store.similaritySearch(query, 4, {
fields: ["metadata.author"],
searchOptions: {
query: { field: "metadata.author", match: "Johny", fuzziness: 1 },
},
});
console.log(partialMatchResult[0]);
示例:按日期范围查询搜索
我们可以搜索在日期字段(如metadata.date
)上的日期范围查询中的文档。
const dateRangeResult = await store.similaritySearch(independenceQuery, 4, {
fields: ["metadata.date", "metadata.author"],
searchOptions: {
query: {
start: "2016-12-31",
end: "2017-01-02",
inclusiveStart: true,
inclusiveEnd: false,
field: "metadata.date",
},
},
});
console.log(dateRangeResult[0]);
示例:按数字范围查询搜索
我们可以搜索在数字字段(如metadata.rating
)的范围内的文档。
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]);
示例:组合多个搜索条件
不同的查询可以使用 AND(合取词)或 OR(析取词)运算符组合。
在这个例子中,我们正在检查评分在 3 到 4 之间且日期在 2015 年到 2018 年之间的文档。
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]);
其他查询
同样,您可以在filter
参数的searchOptions
键中使用任何支持的查询方法,例如 Geo Distance、Polygon Search、Wildcard、Regular Expressions 等。 请参考文档以了解有关可用查询方法及其语法的更多详细信息。
常见问题解答
问题:在创建 CouchbaseVectorStore 对象之前,是否应该创建搜索索引?
是的,目前您需要在创建CouchbaseVectorStore
对象之前创建搜索索引。
问题:我在搜索结果中没有看到我指定的所有字段。
在 Couchbase 中,我们只能返回存储在搜索索引中的字段。 请确保您尝试在搜索结果中访问的字段是搜索索引的一部分。
一种处理方法是在索引中动态地索引和存储文档的字段。
- 在 Capella 中,您需要转到“高级模式”,然后在“常规设置”下拉菜单下,您可以选中“[X]存储动态字段”或“[X]索引动态字段”
- 在 Couchbase Server 中,在索引编辑器(不是快速编辑器)中,在“高级”下拉菜单下,您可以选中“[X]存储动态字段”或“[X]索引动态字段”
请注意,这些选项会增加索引的大小。
有关动态映射的更多详细信息,请参考文档。
问题:我在搜索结果中看不到元数据对象。
这很可能是由于文档中的metadata
字段没有被 Couchbase Search 索引索引和/或存储。 为了在文档中索引metadata
字段,您需要将其作为子映射添加到索引中。
如果您选择在映射中映射所有字段,您将能够通过所有元数据字段进行搜索。 或者,为了优化索引,您可以选择要索引的metadata
对象中的特定字段。 您可以参考文档以了解有关索引子映射的更多信息。
要创建子映射,您可以参考以下文档 -