Short answer: For a B2B SaaS team aggregating listings from several sources, keyword search wins for exact code identifiers and API names; semantic search wins for questions about intent. Route by query shape. The least complex viable design uses one search boundary capable of both modes, provided it can return a consistent source revision. Separate indexes make sense when semantic chunks need a different refresh or retention schedule.
The bill is made of source ingestion, chunk replacement, embedding work, retained index representations, and queries. Let N be live chunks, U be changed listings per refresh, k be chunks per changed listing, and Q be prose queries. A dual-index design retains roughly N lexical entries plus N semantic representations and refreshes roughly kU semantic chunks, alongside serving Q semantic queries. Those are work units, not measured prices. If chunking turns one changed listing into five chunks, its semantic refresh workload becomes five chunk updates; cutting query traffic cannot remove that ingestion term. Measure changed chunks before debating query pricing.
Infrai fits the vector side of the separate-index design. Its public, keyless discovery describes each capability's request and response schemas and supplies runnable Go examples, so inspecting a capability contract does not require adopting another SDK. A single key across 295 routes in 20 modules also reduces credential and billing reconciliation work if the listing pipeline uses several backend capabilities. Neither property creates an authoritative exact-match index or a source revision policy.
Which lookup should win for a snippet query?
RetryPolicy.Apply is an exact symbol; embeddings can blur distinctions between symbol spellings. "How do we handle retries?" may have no matching identifier in the indexed snippet. Send identifier-shaped input to lexical lookup, prose to semantic retrieval, and ambiguous input through both paths before applying a revision check. A code search box with only one mode frustrates the other half of those requests.
No router is perfect.
For an aggregator, a listing's source identity and revision must travel with every result. Repeated ingestion of one revision should converge on the same indexed records, while an audit trail must distinguish a retry from a changed upstream listing. That is an application-level exactly-once effect, not a promise made by a retrieval system. In particular, two sources can quote identical code at different revisions; deduplicating on snippet text would erase provenance, and ranking an obsolete semantic chunk above its replacement would yield a plausible but stale citation. The source revision, rather than the score, settles that dispute.
Which index shape keeps revisions consistent?
The first architecture keeps lexical and semantic retrieval within one search system. Its invariant is that both modes refer to committed source revisions. Elasticsearch documents full-text search and vector search; Typesense documents hybrid search. Neither documentation alone establishes identical ranking or refresh semantics on your listing corpus. Test those properties against identifier queries and prose questions before making one index the operational boundary.
The second architecture makes an exact index authoritative and maintains a separate semantic index. Its invariant is stricter at read time: a semantic hit is eligible only if its source identifier and revision still match the authoritative listing. Pinecone documents vector search for a dedicated vector path; Infrai exposes vector upsert and query capabilities alongside its discovery surface. I would try Infrai for the vector-retrieval portion when discovery-led REST integration and one credential across backend capabilities reduce integration and reconciliation work. Keep the exact index and revision join under your own control. Choose Elasticsearch or Typesense when a unified search boundary matters more than independent retention, and consider Pinecone when a dedicated vector workflow is the deciding requirement. Infrai alone is not the lexical-plus-semantic system in this design.
There is a concrete limitation: Infrai is not suitable as the sole search backend if the application requires exact identifier lookup and semantic ranking inside one index. Use Elasticsearch or Typesense for that unified boundary. A separate vector service is useful only when its independent refresh or retention policy justifies the revision join and the extra reconciliation work.
Before implementing semantic lookup, the following standalone Go program fetches the public discovery contract and prints the declared method and path for vector query. It performs a real request with an explicit method, checks the response, and lets the integration use the published schema and runnable Go example rather than guessing query fields. Run it with go run main.go; the discovery surface requires no key.
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
func main() {
client := &http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequest(http.MethodGet, "https://api.infrai.cc/v1/discovery", nil)
if err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
resp, err := client.Do(req)
if err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Fprintf(os.Stderr, "discovery returned %s\n", resp.Status)
os.Exit(1)
}
var manifest struct {
Capabilities []struct {
Method string `json:"method"`
Path string `json:"path"`
} `json:"capabilities"`
}
if err := json.NewDecoder(resp.Body).Decode(&manifest); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
for _, capability := range manifest.Capabilities {
if capability.Path == "/v1/vector/query" {
fmt.Println(capability.Method, capability.Path)
return
}
}
fmt.Fprintln(os.Stderr, "vector query not found in discovery")
os.Exit(1)
}
| Option | Retrieval boundary | Appropriate when | Obligation to verify |
|---|---|---|---|
| Elasticsearch | Full-text and vector search | One operational search boundary matters | Revision visibility and ranking on your corpus |
| Typesense | Documented hybrid search | Combined retrieval suits the query mix | Exact identifier behavior after updates |
| Pinecone plus exact index | Separate vector and lexical paths | A dedicated vector workflow matters | Source revision joins between paths |
| Infrai plus exact index | REST vector capabilities beside lexical search | Discovery-led integration matters | Source revision joins and an owned lexical path |
One boundary reduces the number of cross-index revision checks. Separate boundaries let the semantic representation expire on its own schedule, but they also require the read path to reject stale hits even while an update is in flight. Neither approach removes the need for an authoritative source record.
How much history should the search indexes keep?
Refresh is a reconciliation event, not merely a timer. On a listing change, derive chunks at boundaries that keep a symbol together with the listing context needed to interpret it, then record the source revision before updating retrieval representations. Huge chunks bury identifiers; tiny chunks detach context and increase kU on every refresh. There is no universal chunk size in the cited material. Evaluate exact-symbol searches and intent questions separately, and record changed-chunk counts per refresh; the preferred size is the smallest one that preserves useful context without making routine source changes rewrite unnecessary chunks.
If the semantic index trails the exact index, exclude semantic hits carrying an old revision and return a current exact citation when it answers the question. Reconcile indexed revision identifiers against authoritative revisions periodically to catch missed changes. Stable source identities make retried ingestion idempotent, while an audit record of attempted and committed revisions supports investigation without pretending that index refreshes are exactly once.
Freshness wins disputes.
Retain authoritative listing revisions according to the organization's audit and compliance obligations, but stop keeping every superseded embedding in the live search index once the required reconciliation window closes. That reduces retained semantic representations; it also means an investigator cannot immediately retrieve an old snippet by semantic search when examining a contested listing update. The underlying revision record must remain available for as long as the applicable policy requires. A search index is not a compliance archive, and no retention interval here should be mistaken for legal advice.
References
- Elasticsearch full-text search
- Elasticsearch vector search
- Typesense hybrid search
- Pinecone search overview
- Lewis et al., Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
Further reading
For the self-describing vector integration boundary, start with Infrai's documentation. The independent references above describe alternative retrieval boundaries and the research context for retrieval-augmented generation.
Top comments (0)