Skip to content

Latest commit

 

History

History
179 lines (140 loc) · 5.03 KB

File metadata and controls

179 lines (140 loc) · 5.03 KB
name search-scala
summary Couchbase Full-Text Search, vector search, hybrid search, and geospatial search for Scala
description Couchbase Full-Text Search, vector search, hybrid search, and geospatial search for Scala
compatibility Scala SDK 1.x. Vector search requires Couchbase Server 7.6+.
metadata
last_verified min_server_version handoff
2026-05
7.0
condition skill
user asks about search concepts, index setup, or RAG patterns
search-concepts
condition skill
user asks about SQL++ queries
server-querying-scala
condition skill
user asks about CAS, bulk ops, sub-document, or SDK patterns
sdk-patterns-scala
condition skill
user asks about connection setup or SDK configuration
server-connection-scala

Full-Text & Vector Search — Scala

Prerequisites — Index must exist before querying

Create an FTS index before running text or vector queries. Use the templates as a starting point:

See shared/server/search-concepts.md for setup instructions.

Imports

import com.couchbase.client.scala.search.{SearchOptions, SearchScanConsistency}
import com.couchbase.client.scala.search.queries.{MatchQuery, SearchQuery}
import com.couchbase.client.scala.search.result.{SearchResult, SearchRow}
import com.couchbase.client.scala.search.vector.{SearchRequest, VectorQuery, VectorSearch}
import com.couchbase.client.scala.kv.MutationState
import scala.util.{Success, Failure}

Full-Text Search

val result = cluster.searchQuery(
  "travel-sample-index-hotel-description",
  MatchQuery("swanky"),
  SearchOptions().limit(10)
)

result match {
  case Success(res) =>
    res.rows.foreach(row => println(s"id=${row.id} score=${row.score}"))
    println(s"total=${res.metaData.metrics.totalRows}")
  case Failure(err) => println(s"Error: $err")
}

Scope-Level Search

val scope = cluster.bucket("travel-sample").scope("inventory")

scope.searchQuery(
  "my-scoped-index",
  MatchQuery("ocean view"),
  SearchOptions().limit(5)
) match {
  case Success(res) => res.rows.foreach(row => println(row.id))
  case Failure(err) => println(s"Error: $err")
}

Common Query Types

import com.couchbase.client.scala.search.queries._

// Term — exact token match
TermQuery("airline")

// Prefix
PrefixQuery("air")

// Boolean — AND/OR/NOT
BooleanQuery()
  .must(MatchQuery("hotel"))
  .mustNot(TermQuery("hostel"))

// Numeric range
NumericRangeQuery().min(3.0).max(5.0)

// Date range
DateRangeQuery().start("2020-01-01").end("2024-12-31")

Vector Search

val queryVector: Array[Float] = Array(0.1f, 0.2f /*, ... 1536 dims */)

val request = SearchRequest.create(VectorSearch.create(
  VectorQuery("embedding_field", queryVector).numCandidates(3)
))

cluster.search("my-vector-index", request, SearchOptions().limit(5)) match {
  case Success(res) =>
    res.rows.foreach(row => println(s"id=${row.id} score=${row.score}"))
  case Failure(err) => println(s"Error: $err")
}

Hybrid Search (FTS + Vector)

val queryVector: Array[Float] = Array(0.1f, 0.2f /*, ... */)

val request = SearchRequest.create(MatchQuery("ocean view"))
  .vectorSearch(VectorSearch.create(
    VectorQuery("embedding_field", queryVector).numCandidates(3)
  ))

cluster.search("my-hybrid-index", request, SearchOptions().limit(10))

Geospatial Search

import com.couchbase.client.scala.search.queries.GeoDistanceQuery

// Within 10km of a point
cluster.searchQuery(
  "hotel-geo",
  GeoDistanceQuery(37.7749, -122.4194, "10km").field("geo"),
  SearchOptions().limit(20).fields("name", "geo")
) match {
  case Success(result) => result.rows.foreach(row => println(row.id))
  case Failure(err)    => println(s"Search failed: $err")
}

The geo field must be indexed as type: geopoint. Document format: {"geo": {"lat": 37.7749, "lon": -122.4194}}.

RYOW Consistency

val insertResult = collection.insert(
  "hotel::new",
  JsonObject("name" -> "Hotel California", "desc" -> "Such a lonely place")
).get

val ms = MutationState(Seq(insertResult.mutationToken.get))

cluster.searchQuery(
  "travel-sample-index-hotel-description",
  MatchQuery("lonely"),
  SearchOptions()
    .limit(5)
    .scanConsistency(SearchScanConsistency.ConsistentWith(ms))
)

Working with Results

result match {
  case Success(res) =>
    res.rows.foreach { row =>
      println(s"id=${row.id}")
      println(s"score=${row.score}")
      row.fields.foreach(f => println(s"fields=$f"))
    }
    val metrics = res.metaData.metrics
    println(s"total=${metrics.totalRows} took=${metrics.took}")
  case Failure(err) => println(s"Error: $err")
}

Shared concepts: shared/server/search-concepts.md — prerequisites, hybrid search, RYOW, pagination, query types.