Full Text Search
This doc is about full-text search capabilities. For vector similarity search and semantic search, see the Vector Embeddings documentation.
Full-Text Search allows you to search text content efficiently.
Enabling FTS
Section titled “Enabling FTS”Declare the full-text index with the collection schema: the fts block lists
the indexed fields and vski apply creates it via a migration (see
IaC).
version: 1database: default
collections: articles: type: base fields: - {name: title, type: text, required: true} - {name: content, type: text, required: true} - {name: tags, type: json} fts: fields: [title, content] # full-text index over these fieldsvski apply # creates the collection and its FTS indexQueries run through the Python SDK (pip install vski-sdk); the examples
below assume an authenticated client is in scope (see
Collections).
Basic Search
Section titled “Basic Search”# Search for articles containing "react"results = await client.collection("articles").search("react", snippet=True)
for item in results["items"]: print(item["title"]) # "React Tutorial" # "Building React Apps"Advanced Search Queries
Section titled “Advanced Search Queries”VSKI supports FTS5 query syntax:
Phrase Search
Section titled “Phrase Search”# Search for exact phraseresults = await client.collection("articles").search('"react hooks"')OR Queries
Section titled “OR Queries”# Search for articles containing react OR vueresults = await client.collection("articles").search("react OR vue")AND Queries
Section titled “AND Queries”# Search for articles containing both react AND hooksresults = await client.collection("articles").search("react hooks")NOT Queries
Section titled “NOT Queries”# Search for articles containing "react" but not "angular"results = await client.collection("articles").search("react NOT angular")Wildcard Searches
Section titled “Wildcard Searches”# Search for words starting with "react"results = await client.collection("articles").search("react*")Proximity Search
Section titled “Proximity Search”# Search for words within 5 words of each otherresults = await client.collection("articles").search( '"react hooks" NEAR/5 "components"')Search with Filters
Section titled “Search with Filters”The search endpoint runs the FTS query; structured filters belong to
get_list(). Use the ~ substring operator to combine a text match with
regular conditions in a single query:
# Full-text queryresults = await client.collection("articles").search("react tutorial", per_page=20)
# Text match combined with regular filterspage = await client.collection("articles").get_list( 1, 20, filter='published = true AND title ~ "react tutorial"')Search Options
Section titled “Search Options”results = await client.collection("articles").search( "react", per_page=10, # limit results (page size) expand="author", # expand relations)# which fields are searched is fixed by fts.fields in the collection YAMLSearch Result Ranking
Section titled “Search Result Ranking”Relevance Scoring
Section titled “Relevance Scoring”Results come back in FTS relevance order (the raw rank score is not part of the response):
results = await client.collection("articles").search("react")
for item in results["items"]: print(item["title"])Custom Ranking
Section titled “Custom Ranking”results = await client.collection("articles").search("react")
# Rank by another field once the results are backranked = sorted( results["items"], key=lambda item: item.get("views", 0), reverse=True)
# Or sort in the database without a full-text querymost_viewed = await client.collection("articles").get_list(1, 20, sort="-views")Search Suggestions
Section titled “Search Suggestions”Implement autocomplete suggestions:
async def get_suggestions(query: str): if len(query) < 2: return []
results = await client.collection("articles").search(f"{query}*", per_page=5)
return [item["title"] for item in results["items"]]Search Analytics
Section titled “Search Analytics”Track search queries for analytics:
from datetime import datetime, timezone
async def log_search(query: str, results_count: int, user_id: str | None = None): await client.collection("search_logs").create({ "query": query, "userId": user_id, "timestamp": datetime.now(timezone.utc).isoformat(), "resultsCount": results_count, })
# Usageresults = await client.collection("articles").search(query)await log_search(query, results["totalItems"], current_user_id)Performance Optimization
Section titled “Performance Optimization”Indexing
Section titled “Indexing”Create appropriate indexes for search:
collections: products: type: base fields: - {name: name, type: text, required: true} - {name: description, type: text} - {name: category, type: text} indexes: - {name: products_category, sql: "CREATE INDEX products_category ON products(category)"} fts: fields: [name, description]vski apply # FTS over name/description plus a regular index on categoryCaching
Section titled “Caching”Cache search results:
search_cache: dict = {}
async def cached_search(query: str): if query in search_cache: return search_cache[query]
results = await client.collection("articles").search(query) search_cache[query] = results
return resultsDebouncing
Section titled “Debouncing”Debounce search input:
import asyncio
pending: asyncio.Task | None = None
async def run_search(delay: float, query: str): await asyncio.sleep(delay) results = await client.collection("articles").search(query) display_results(results["items"])
def debounce_search(query: str, delay: float = 0.3): global pending if pending and not pending.done(): pending.cancel() pending = asyncio.create_task(run_search(delay, query))Practical Examples
Section titled “Practical Examples”E-commerce Product Search
Section titled “E-commerce Product Search”# Search products with filtersasync def search_products( query: str, category: str | None = None, min_price: float | None = None,): conditions = [f'name ~ "{query}"', "published = true"]
if category is not None: conditions.append(f'category = "{category}"')
if min_price is not None: conditions.append(f"price >= {min_price}")
return await client.collection("products").get_list( 1, 20, filter=" AND ".join(conditions), sort="-popularity", )FAQ Search
Section titled “FAQ Search”# FAQ search with relevance rankingasync def search_faq(query: str): return await client.collection("faqs").search(query, per_page=5)API Endpoints
Section titled “API Endpoints”| Method | Endpoint | Description |
|---|---|---|
GET | /api/collections/:name/records/search | Full-text search |
GET | /api/collections/:name/records | List with search parameter |
Best Practices
Section titled “Best Practices”- Use appropriate indexing - Create FTS indexes for searchable fields
- Optimize query syntax - Use specific queries instead of broad ones
- Implement pagination - Always use limits with search results
- Cache popular searches - Reduce server load
- Monitor search performance - Track slow queries
- Normalize text - Consistent text formatting improves search