Documentation Index Fetch the complete documentation index at: https://mintlify.com/rjdellecese/confect/llms.txt
Use this file to discover all available pages before exploring further.
Confect provides two types of search capabilities: full-text search for querying text fields, and vector search for finding semantically similar content using embeddings.
Overview
Full-Text Search Search text fields with tokenization and ranking
Vector Search Find similar content using embeddings
Filter Results Combine search with field filters
Ranked Results Results ordered by relevance score
Full-Text Search
Full-text search lets you query text fields using natural language queries.
Defining Search Indexes
Add a search index to your table schema:
import { Table } from "@confect/server" ;
import { Schema } from "effect" ;
const articlesTable = Table . make (
"articles" ,
Schema . Struct ({
title: Schema . String ,
content: Schema . String ,
authorId: Schema . String ,
category: Schema . String ,
publishedAt: Schema . Number ,
})
)
. searchIndex ( "search_content" , {
searchField: "content" ,
filterFields: [ "authorId" , "category" ],
})
. searchIndex ( "search_title" , {
searchField: "title" ,
});
Search indexes require:
A searchField (the text field to search)
Optional filterFields (fields you can filter on)
Search using the search method:
import { DatabaseReader } from "@confect/server" ;
import { Effect } from "effect" ;
const searchArticles = ( query : string ) =>
Effect . gen ( function* () {
const db = yield * DatabaseReader < typeof databaseSchema >();
const results = yield * db
. table ( "articles" )
. search ( "search_content" , ( q ) => q . search ( "content" , query ))
. collect ();
return results ;
});
Search with Filters
Combine search with field filters:
const searchByAuthor = ( query : string , authorId : string ) =>
Effect . gen ( function* () {
const db = yield * DatabaseReader < typeof databaseSchema >();
const results = yield * db
. table ( "articles" )
. search ( "search_content" , ( q ) =>
q
. search ( "content" , query )
. eq ( "authorId" , authorId )
)
. collect ();
return results ;
});
const searchByCategory = ( query : string , category : string ) =>
Effect . gen ( function* () {
const db = yield * DatabaseReader < typeof databaseSchema >();
const results = yield * db
. table ( "articles" )
. search ( "search_content" , ( q ) =>
q
. search ( "content" , query )
. eq ( "category" , category )
)
. collect ();
return results ;
});
Filter fields must be declared in the search index’s filterFields array.
Limiting Results
const searchTop10 = ( query : string ) =>
Effect . gen ( function* () {
const db = yield * DatabaseReader < typeof databaseSchema >();
const results = yield * db
. table ( "articles" )
. search ( "search_content" , ( q ) => q . search ( "content" , query ))
. take ( 10 )
. collect ();
return results ;
});
Search Example Implementation
import { FunctionImpl , DatabaseReader } from "@confect/server" ;
import { Effect } from "effect" ;
const search = FunctionImpl . make (
api ,
"articles" ,
"search" ,
({ query , category , limit = 20 }) =>
Effect . gen ( function* () {
const db = yield * DatabaseReader < typeof databaseSchema >();
let searchQuery = db
. table ( "articles" )
. search ( "search_content" , ( q ) => {
let builder = q . search ( "content" , query );
if ( category ) {
builder = builder . eq ( "category" , category );
}
return builder ;
});
const results = yield * searchQuery
. take ( limit )
. collect ();
return results . map (( article ) => ({
id: article . _id ,
title: article . title ,
excerpt: article . content . slice ( 0 , 200 ),
category: article . category ,
publishedAt: article . publishedAt ,
}));
}). pipe ( Effect . orDie )
);
Vector Search
Vector search finds semantically similar content using embeddings. It’s available in actions only.
Defining Vector Indexes
Add a vector index to your table schema:
import { Table } from "@confect/server" ;
import { Schema } from "effect" ;
const documentsTable = Table . make (
"documents" ,
Schema . Struct ({
text: Schema . String ,
embedding: Schema . Array ( Schema . Number ),
category: Schema . String ,
createdAt: Schema . Number ,
})
)
. vectorIndex ( "by_embedding" , {
vectorField: "embedding" ,
dimensions: 1536 , // OpenAI embedding size
filterFields: [ "category" ],
});
Vector indexes require:
A vectorField (array of numbers)
dimensions (must match your embedding size)
Optional filterFields
Use the VectorSearch service in actions:
import { VectorSearch } from "@confect/server" ;
import { Effect } from "effect" ;
const findSimilar = ( embedding : number []) =>
Effect . gen ( function* () {
const search = yield * VectorSearch < typeof databaseSchema >();
const results = yield * search (
"documents" ,
"by_embedding" ,
{
vector: embedding ,
limit: 10 ,
}
);
return results ;
});
Vector Search with Filters
const findSimilarInCategory = ( embedding : number [], category : string ) =>
Effect . gen ( function* () {
const search = yield * VectorSearch < typeof databaseSchema >();
const results = yield * search (
"documents" ,
"by_embedding" ,
{
vector: embedding ,
filter : ( q ) => q . eq ( "category" , category ),
limit: 10 ,
}
);
return results ;
});
Generating Embeddings
Generate embeddings using an AI model:
import { Effect } from "effect" ;
const generateEmbedding = ( text : string ) =>
Effect . gen ( function* () {
const response = yield * Effect . tryPromise (() =>
fetch ( "https://api.openai.com/v1/embeddings" , {
method: "POST" ,
headers: {
"Authorization" : `Bearer ${ process . env . OPENAI_API_KEY } ` ,
"Content-Type" : "application/json" ,
},
body: JSON . stringify ({
model: "text-embedding-3-small" ,
input: text ,
}),
})
);
const data = yield * Effect . tryPromise (() => response . json ());
return data . data [ 0 ]. embedding as number [];
});
Semantic Search Example
import { FunctionImpl , VectorSearch , DatabaseReader } from "@confect/server" ;
import { Effect } from "effect" ;
const semanticSearch = FunctionImpl . make (
api ,
"documents" ,
"semanticSearch" ,
({ query , limit = 10 }) =>
Effect . gen ( function* () {
const db = yield * DatabaseReader < typeof databaseSchema >();
const search = yield * VectorSearch < typeof databaseSchema >();
// Generate embedding for query
const embedding = yield * generateEmbedding ( query );
// Search for similar documents
const results = yield * search (
"documents" ,
"by_embedding" ,
{
vector: embedding ,
limit ,
}
);
// Fetch full documents
const documents = yield * Effect . forEach (
results ,
( result ) =>
Effect . gen ( function* () {
const doc = yield * db . table ( "documents" ). get ( result . _id );
return {
id: doc . _id ,
text: doc . text ,
score: result . _score ,
};
})
);
return documents ;
}). pipe ( Effect . orDie )
);
Storing Documents with Embeddings
const addDocument = FunctionImpl . make (
api ,
"documents" ,
"add" ,
({ text , category }) =>
Effect . gen ( function* () {
const db = yield * DatabaseWriter < typeof databaseSchema >();
// Generate embedding
const embedding = yield * generateEmbedding ( text );
// Store document with embedding
const documentId = yield * db . table ( "documents" ). insert ({
text ,
embedding ,
category ,
createdAt: Date . now (),
});
return { documentId };
}). pipe ( Effect . orDie )
);
Hybrid Search
Combine full-text and vector search for better results:
const hybridSearch = FunctionImpl . make (
api ,
"articles" ,
"hybridSearch" ,
({ query , limit = 20 }) =>
Effect . gen ( function* () {
const db = yield * DatabaseReader < typeof databaseSchema >();
const search = yield * VectorSearch < typeof databaseSchema >();
// Full-text search
const textResults = yield * db
. table ( "articles" )
. search ( "search_content" , ( q ) => q . search ( "content" , query ))
. take ( limit )
. collect ();
// Vector search
const embedding = yield * generateEmbedding ( query );
const vectorResults = yield * search (
"articles" ,
"by_embedding" ,
{ vector: embedding , limit }
);
// Combine and deduplicate
const combined = [
... textResults . map (( r ) => ({ ... r , source: "text" })),
... vectorResults . map (( r ) => ({
... r ,
source: "vector" ,
score: r . _score ,
})),
];
// Remove duplicates and sort by relevance
const unique = Array . from (
new Map ( combined . map (( r ) => [ r . _id , r ])). values ()
). slice ( 0 , limit );
return unique ;
}). pipe ( Effect . orDie )
);
Search Relevance
Both search types return results ordered by relevance:
// Full-text search (implicit score)
const textSearch = yield * db
. table ( "articles" )
. search ( "search_content" , ( q ) => q . search ( "content" , query ))
. collect ();
// Results are automatically sorted by relevance
// Vector search (explicit score)
const vectorResults = yield * search ( "documents" , "by_embedding" , {
vector: embedding ,
limit: 10 ,
});
// Each result has a _score field
console . log ( vectorResults [ 0 ]. _score ); // 0.95
Best Practices
Choose the Right Search Type
Use full-text for exact keyword matches, vector for semantic similarity.
Optimize Index Fields
Only include necessary fields in search and filter fields.
Cache Embeddings
Store embeddings to avoid regenerating them on every search.
Combine Search Types
Use hybrid search for the best results.
Vector search is only available in actions due to its computational requirements. Use full-text search in queries for real-time results.
Search indexes are built asynchronously. New documents may not appear in search results immediately.
Next Steps
Database Learn about database queries
Functions Implement search functions
Node Actions Generate embeddings in Node
Convex Search Convex search documentation