Use Vector Search with Full Text Search and Query.
Use Vector Search
To configure a project to use vector search, follow the installation instructions to add the Vector Search extension.
You must install Couchbase Lite to use the Vector Search extension. |
Create a Vector Index
This method shows how you can create a vector index using the Couchbase Lite Vector Search extension.
-
Java
-
Kotlin
// create the configuration for a vector index named "vector"
// with 300 dimensions and 20 centroids
VectorIndexConfiguration config = new VectorIndexConfiguration("vector", 300L, 20L);
First, initialize the config
variable with the VectorIndexConfiguration()
method with the following parameters:
-
The width or
dimensions
of the vector index is set to300
. -
The amount of
centroids
is set to20
. This means that there will be 20 buckets with a single centroid each that gathers together similar vectors.
There are also some default values that we did not set with the VectorIndexConfiguration()
method, these are:
-
The encoding type - 8 bit Scalar Quantization.
-
The distance metric - Squared Euclidean.
-
The minimum and maximum number of vectors for training the index.
You can also alter some optional config settings such as encoding
.
// create the configuration for a vector index named "vector"
// with 300 dimensions, 20 centroids, max training size 200, min training size 100
// no vector encoding and using COSINE distance measurement
VectorIndexConfiguration config = new VectorIndexConfiguration("vector", 300L, 20L)
.setEncoding(VectorEncoding.none())
.setMetric(VectorIndexConfiguration.DistanceMetric.COSINE)
.setMinTrainingSize(100L)
.setMaxTrainingSize(200L);
// create the configuration for a vector index named "vector"
// with 300 dimensions and 20 centroids
val config = VectorIndexConfiguration("vector", 300L, 20L)
First, initialize the config
variable with the VectorIndexConfiguration()
method with the following parameters:
-
The width or
dimensions
of the vector index is set to300
. -
The amount of
centroids
is set to20
. This means that there will be 20 buckets with a single centroid each that gathers together similar vectors.
There are also some default values that we did not set with the VectorIndexConfiguration()
method, these are:
-
The encoding type - 8 bit Scalar Quantization.
-
The distance metric - Squared Euclidean.
-
The minimum and maximum number of vectors for training the index.
You can also alter some optional config settings such as encoding
.
// create the configuration for a vector index named "vector"
// with 300 dimensions, 20 centroids, max training size 200, min training size 100
// no vector encoding and using COSINE distance measurement
val config = VectorIndexConfiguration("vector", 300L, 20L)
.setEncoding(VectorEncoding.none())
.setMetric(VectorIndexConfiguration.DistanceMetric.COSINE)
.setMinTrainingSize(100L)
.setMaxTrainingSize(200L)
The number of vectors, the width or dimensions of the vectors and the training size can incur high CPU and memory costs as the size of each variable increases. This is because the training vectors have to be resident on the machine. |
Vector Index Configuration
The table below displays the different configurations you can modify within your VectorIndexConfiguration()
function.
For more information on specific configurations, see Vector Search.
The beta release currently only supports the format of vectors as an array of numbers. |
Configuration Name | Is Required | Default Configuration | Further Information |
---|---|---|---|
Expression |
Required |
No default |
A SQL++ expression indicating where to get the vectors.
A document property for embedded vectors
|
Number of Dimensions |
Required |
No default |
2-2048 |
Number of Centroids |
Required |
No default |
1-64000. The general guideline is an approximate square root of the number of documents |
Distance Metric |
Optional |
Euclidean Distance |
You can also configure Cosine Distance as your Distance Metric |
Encoding |
Optional |
Scalar Quantizer(SQ) or SQ-8 bits |
There are three possible configurations:
|
Training Size |
Optional |
There are defaults for both the minimum and maximum training size for training the vectors
|
Altering the default training sizes could be detrimental to the accuracy of returned results produced by the model and total computation time. |
Generating Vectors
You can use two methods to generate vectors in Couchbase Lite:
-
You can call a Machine Learning(ML) model, and embed the generated vectors inside the documents.
-
You can use the
prediction()
function to generate vectors to be indexed for each document at the indexing time.
Below are example configurations of the previously mentioned methods.
Create a Vector Index with Embeddings
This method shows you how to create a Vector Index with embeddings.
-
Java
-
Kotlin
// create a vector index named "words_index"
// in the collection "_default.words"
db.getCollection("words").createIndex("word_index", new VectorIndexConfiguration("vector", 300L, 20L));
The snippet demonstrates the creation of the vector index, word_index
, in the collection _default.words
, demonstrating how you can create a vector index with a word
embedding for use in queries related to written language.
// create a vector index named "words_index"
// in the collection "_default.words"
db.getCollection("words")!!.createIndex("word_index", VectorIndexConfiguration("vector", 300L, 20L))
The snippet demonstrates the creation of the vector index, word_index
, in the collection _default.words
, demonstrating how you can create a vector index with a word
embedding for use in queries related to written language.
Create Vector Index Embeddings from a Predictive Model
This method generates vectors to be indexed for each document at the index time by using the prediction()
function.
-
Java
-
Kotlin
// create a vector index with a simple predictive model
final Collection collection = db.getCollection("words");
Database.prediction.registerModel(
"WordEmbedding",
input -> {
String word = input.getString("word");
if (Utils.isEmpty(word)) {
Logger.log("Input word is empty");
return null;
}
try (ResultSet rs = db.createQuery(
"SELECT vector"
+ " FROM " + collection.getFullName()
+ " WHERE word = '" + word + " '")
.execute()) {
List<Result> results = rs.allResults();
if (results.isEmpty()) { return null; }
Array result = results.get(0).getArray(0);
if (result != null) {
MutableDictionary dict = new MutableDictionary();
dict.setValue("vector", result.toList());
return dict;
}
Logger.log("Unexpected result: " + result);
}
catch (CouchbaseLiteException e) {
Logger.log("Prediction query failed", e);
}
return null;
});
collection.createIndex(
"words_pred_index",
new VectorIndexConfiguration("prediction(WordEmbedding, {'word': word}).vector", 300L, 8L));
// create a vector index with a simple predictive model
val collection = db.getCollection("words")!!
Database.prediction.registerModel("WordEmbedding") {
val word: String? = it.getString("word")
if (TextUtils.isEmpty(word)) {
Logger.log("Input word is empty")
return@registerModel null
}
try {
db.createQuery(
"SELECT vector"
+ " FROM ${collection}"
+ " WHERE word = '${word}'"
).execute().use { rs ->
val results = rs.allResults()
if (results.isEmpty()) {
return@registerModel null
}
results[0].getArray(0)?.let { result ->
val dict = MutableDictionary()
dict.setValue("vector", result.toList())
return@registerModel dict
}
Logger.log("Prediction result is not an array")
}
} catch (e: CouchbaseLiteException) {
Logger.log("Prediction query failed", e)
}
return@registerModel null
}
collection.createIndex(
"words_pred_index",
VectorIndexConfiguration("prediction(WordEmbedding, {'word': word}).vector", 300L, 8L)
)
// end::vs-create-predictive- index[]
}
@Throws(CouchbaseLiteException::class)
fun useVectorMatch(db: Database, hugeListOfFloats: List<Any?>?) {
// use the vector_match function in a query
db.getCollection("words")!!.createIndex("word_index", VectorIndexConfiguration("vector", 300L, 8L))
val query = db.createQuery(
"SELECT meta().id, word"
+ " FROM _default.words"
+ " WHERE vector_match(words_index, \$vectorParam, 20)"
)
val params = Parameters()
params.setArray("vectorParam", MutableArray((hugeListOfFloats)!!))
query.parameters = params
query.execute().use { rs ->
// process results
}
}
@Throws(CouchbaseLiteException::class)
fun useVectorDistance(db: Database, hugeListOfFloats: List<Any?>?) {
// use the vector_distance function in a query
db.getCollection("words")!!.createIndex("word_index", VectorIndexConfiguration("vector", 300L, 8L))
val query = db.createQuery(
("SELECT meta().id, word,vector_distance(words_index)"
+ " FROM _default.words"
+ " WHERE vector_match(words_index, \$dinner, 20)")
)
val params = Parameters()
params.setArray("vectorParam", MutableArray((hugeListOfFloats)!!))
query.parameters = params
query.execute().use { rs ->
// process results
}
}
}
You can use less storage by using the prediction() function as the encoded vectors will only be stored in the index.
However, the index time will be longer as vector embedding generation is occurring at run time.
|
Vector SQL++ Functions
Couchbase Lite currently supports two SQL++ functions, vector_match()
and vector_distance()
.
vector_match(vectorIndexIdentifier, targetVectorExpr, [limit = 3])
Parameter | Is Required | Description |
---|---|---|
vectorIndexIdentifier |
Required |
The name of the vector index to perform the vector search on. |
targetVectorExpr |
Required |
The target vector expression that returns a vector in the form of an array of numbers. |
limit |
Optional |
The limit number of the returned matched results. The maximum number allowed is 10000. An error will be returned when creating a query with a limit greater than 10000. |
The default value for the limit parameter is 3.
|
Use vector_match()
-
Java
-
Kotlin
// use the vector_match function in a query
db.getCollection("words").createIndex("word_index", new VectorIndexConfiguration("vector", 300L, 8L));
Query query = db.createQuery(
"SELECT meta().id, word"
+ " FROM _default.words"
+ " WHERE vector_match(words_index, $vectorParam, 20)");
Parameters params = new Parameters();
params.setArray("vectorParam", new MutableArray(hugeListOfFloats));
query.setParameters(params);
try (ResultSet rs = query.execute()) {
// process results
}
// use the vector_match function in a query
db.getCollection("words")!!.createIndex("word_index", VectorIndexConfiguration("vector", 300L, 8L))
val query = db.createQuery(
"SELECT meta().id, word"
+ " FROM _default.words"
+ " WHERE vector_match(words_index, \$vectorParam, 20)"
)
val params = Parameters()
params.setArray("vectorParam", MutableArray((hugeListOfFloats)!!))
query.parameters = params
query.execute().use { rs ->
// process results
}
This function performs vector search against a specific vector index identifier for the specified vector expression. If the specified index does not exist, an error will occur on creation of the query. The matched vectors will be returned up to the specified limit number, if the limit is not specified then the default value will be used. The returned vectors are sorted by their distance values in ascending order by default.
Similar to the Full Text Search match() function, vector_match() can only be called alone or at the top level AND expression.
|
vector_distance(vectorIndexIdentifier)
Parameter | Is Required | Description |
---|---|---|
vectorIndexIdentifier |
Required |
The name of the vector index. |
Use vector_distance()
-
Java
-
Kotlin
// use the vector_distance function in a query
db.getCollection("words").createIndex("word_index", new VectorIndexConfiguration("vector", 300L, 8L));
Query query = db.createQuery(
"SELECT meta().id, word,vector_distance(words_index)"
+ " FROM _default.words"
+ " WHERE vector_match(words_index, $dinner, 20)");
Parameters params = new Parameters();
params.setArray("vectorParam", new MutableArray(hugeListOfFloats));
query.setParameters(params);
try (ResultSet rs = query.execute()) {
// process results
}
// use the vector_distance function in a query
db.getCollection("words")!!.createIndex("word_index", VectorIndexConfiguration("vector", 300L, 8L))
val query = db.createQuery(
("SELECT meta().id, word,vector_distance(words_index)"
+ " FROM _default.words"
+ " WHERE vector_match(words_index, \$dinner, 20)")
)
val params = Parameters()
params.setArray("vectorParam", MutableArray((hugeListOfFloats)!!))
query.parameters = params
query.execute().use { rs ->
// process results
}
This function returns the distance between the target vector specified in the vector_match()
function and the matched vector in the specified vector index based on the distance metric set in the index configuration.