Trovella Wiki

Query Classification

How classifyQuery categorizes search input by word count and the planned evolution toward weighted routing.

The classifyQuery() function in packages/search/src/fusion.ts is a lightweight heuristic that categorizes a search query into one of three types based on word count. It is used by the hybridSearch.search and hybridSearch.debugSearch procedures.

Current Implementation

export function classifyQuery(query: string): "keyword" | "semantic" | "balanced" {
  const words = query.trim().split(/\s+/).length;
  if (words <= 3) return "keyword";
  if (words >= 7) return "semantic";
  return "balanced";
}

Classification Rules

Word CountTypeRationaleExamples
1-3keywordShort queries are typically exact term lookups or entity names"notion pricing", "AI", "competitor analysis"
4-6balancedMedium queries may be either keyword-oriented or natural language"AI research workflow comparison", "document management market size"
7+semanticLonger queries are typically natural language questions"what are the key differences between notion and coda for research workflows"

Current Behavior

Today, the classification result is informational only. The hybridSearch.search procedure:

  1. Calls classifyQuery() to get the type
  2. Runs both keyword and semantic searches in parallel regardless of the type
  3. Fuses them with equal-weight RRF
  4. Returns the queryType in the response metadata

The classification appears in the Search Debugger as a badge above the results, helping developers understand how a query was categorized. But it does not alter the search execution or scoring.

Why Word Count?

The heuristic makes a pragmatic assumption: short queries tend to be exact lookups ("SWOT analysis", "pricing table") while long queries tend to be natural language ("how do competitors handle cost attribution in their pricing models"). This holds well for Trovella's research-oriented content where users alternate between looking up specific terms and asking open-ended questions.

The approach has known limitations:

  • A 3-word query like "machine learning fundamentals" is classified as keyword but is arguably semantic in intent
  • A 7-word query with specific entity names like "Notion Coda Roam Research comparison pricing table" is classified as semantic but has strong keyword characteristics
  • Language and domain context are ignored entirely

These limitations are acceptable at the current scale because the classification does not affect search behavior. As it evolves to influence scoring (see below), more sophisticated approaches will be needed.

Planned Evolution: Weighted Routing

The classification system is designed to evolve into a weight router that biases RRF scores toward the more appropriate search engine for each query type.

Target Behavior

ClassificationKeyword WeightSemantic WeightRationale
keyword1.2-1.5x0.8-0.7xShort exact queries benefit more from BM25 term matching
balanced1.0x1.0xEqual weighting (current default behavior)
semantic0.8-0.7x1.2-1.5xNatural language questions benefit more from vector similarity

Implementation Approach

Weighted RRF modifies the score formula per source:

RRF(d) = w_keyword * 1/(k + rank_keyword) + w_semantic * 1/(k + rank_semantic)

This would require adding keywordWeight and semanticWeight parameters to reciprocalRankFusion(). The function signature would become:

function reciprocalRankFusion(
  keywordResults: RankedResult[],
  semanticResults: RankedResult[],
  limit?: number,
  k?: number,
  weights?: { keyword: number; semantic: number },
): FusedResult[];

Prerequisites

Before implementing weighted routing:

  1. Baseline evaluation data. Collect a set of test queries with known-good results to measure whether weighting improves relevance. See Evaluation.
  2. Sufficient indexed content. The system needs enough content for BM25 and vector search to produce meaningfully different rankings.
  3. Admin tuning UI. A way to adjust weights experimentally and compare results. The existing Search Debugger is the natural home for this.

Test Coverage

The classifyQuery tests in packages/search/src/__tests__/fusion.test.ts verify all three classification buckets:

  • Short queries (1-3 words) classify as keyword
  • Long queries (7+ words) classify as semantic
  • Medium queries (4-6 words) classify as balanced
  • Relevance Overview -- how classification fits into the scoring system
  • RRF Algorithm -- the fusion algorithm that classification will eventually influence
  • Tuning Guide -- practical tuning guidance including query routing

On this page