Back to "Fixing the Site's Search: PostgreSQL FullM SK2Text Search | (and Where It Breaks"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET PostgreSQL RRF Search

Fixing the Site's Search: PostgreSQL FullM SK2Text Search | (and Where It Breaks

Wednesday, 14 January 2026

La ricerca è una di quelle caratteristiche che tutti sottovalutano. Sembra banale finché gli utenti reali non iniziano a digitare le domande vere -acroniemiM SK2 metà - termine tecnici ricordatiMska4 punteggiaturaMske5 nomi pesanti come "ASPMsko7NETMSC8 o ricerche che sono conceptualmente. Giusto ma sbagliato dal punto di vista textuale. Quando la ricerca fallisce in quei casi, gli utenti non pensanoM SK2non credono "esempio di erroreMST4 M ST5Pendono che il sito sia rottoMst6

È anche sometigno che il ' abbia plagiato questo sito da quando è nato. OpenSearch, ha lavorato su PostgreSQL per la ricerca del testo completo con questo ' le cose fantastiche dei vettori ma non è mai stato veramente felice con questo Infestata. Io, in particolare ora, sto costruendo uno strumento di ricerca. **lucido.RAG Beh, ho pensato che avrei dovuto ripararlo. Che sia O meno è un'altra questione.

Questo articolo non riguarda la costruzione di un motore di ricerca da zero... Ma ' riguarda la riparazione dei bordi acuti dell'intero PostgreSQL.-. La ricerca del testo in un sistema di produzione reale.M SK4. Io. ' passerò attraverso i specifici fallimenti che ho colpito.

Costruire sulla base dei lavori precedenti: Questo articolo estende la Implementazione di ricerca semantica che ha aggiunto la ricerca ibrida con Reciprocal Rank Fusion (RRF). L'articolo precedente ha coperto il fondamento -combinando PostgreSQL fullM SK3la ricerca del testo con la ricerca dei vectori QdrantMSC4 Questo articolo corregge i casi marginali che l'implementazione ha persoMST5acroniemi che non corrispondonoM ST6non corrispondenoM st7 termine tecnici con caratteri specialiM SST8 e domande naturali che la parsetrice di Postgre SQLM SP9 rompeMSST10

L'infrastruttura di ricerca semantica serve a due scopi: alimenta il sitoM SK1 l'utente- la ricerca orientata. e fornisce la stratagemma di recupero per il Avvocato GPT Il sistema RAG -un assistente di scrittura che disegna nuovi posti usando il sito'il contenuto esistente come base di conoscenzaM SK2 Per approfondire la tecnologia sottostanteMSC3 consultate Costruire una "Lawyer GPT" – Parte 3 sulle inserzioni e sulla ricerca dei vettori.

I problemi

1. Results Empty Showed Random Articles

Quando una ricerca non ha risposto nessun risultato, il sistema mostrava articoli vecchi casuali invece di consigli utili. Questo violava il principio della minore sorpresa M SK2 gli utenti aspettavano risultati rilevanti o chiari "no match foundMSC4 messaggio con post recenti come suggerimentiMST5 Questo faceva sembrare che la ricerca avesse funzionato SST6 solo maleSTS7

La soluzione:Modificato BlogSearchService.HybridSearchWithPagingAsync() per rilevare quando la ricerca riporta risultati zero e tornare a mostrare i post recenti ordinati alla data di declino. Added a NoMatchFound bandiere verso. BasePagingModel Così l'interfaccia può mostrare un messaggio appropriato come "Niente corrispondenza trovata. Voglio dire uno di questi?

// No match found - return recent posts as suggestions
if (noMatchFound)
{
    Log.Logger.Information("No search results for '{Query}', returning recent posts as suggestions", query);
    return await GetRecentPostsAsSuggestions(targetLanguage, startDate, endDate, page, pageSize);
}

2. Acroniemi come "DiSE" WerenM SK3 Non corrispondono

La configurazione predefinita della ricerca di testo inglese PostgreSQL' indica tecnicamente gli acroniemi, ma la normalizzazione e il deterrimento rendeno i termini significativi poco affidabili nella praticaM SK4 Quando si cerca MSSK5DiSEMSC6 viene abbassato a "dise+" e poi il dizionario inglese può rimuoverlo o assegnare un basso peso+ , causando la completa ricerca del testo a perdere articoli che contieneno chiaramente "DiSe+" nel titolo e nel contenuto+M SK13

La soluzione: Added acronym detection (terms ≤6 characters with uppercase lettersMSC3 and caseM SK4insensitive substring fallback using PostgreSQL's ILIKE. Questo completa la ricerca di testo completaM SK1 anziché la sostituire ( consultate i Consideramenti sul Rendimento sotto per capire perché è accettabile):

// Detect if query looks like an acronym or short term
var isAcronymLike = query.Length <= 6 && query.Any(char.IsUpper);

// Add substring search for acronyms
searchQuery = searchQuery.Where(x =>
    EF.Functions.ILike(x.Title, $"%{acronym}%")
    || EF.Functions.ILike(x.PlainTextContent, $"%{acronym}%"));

3. Terme tecnici con caratteri speciali La ricerca è andata storta

Le ricerche per "ASPM SK1NET" o "C#" fallirebbero perché il parsatore di ricerca del testo PostgreSQLMSC5 tratta i periodi e i simboli di hash come dei delimitatori.

La soluzione: Creato un SearchQueryParser che riconosce termini tecnici comuni e li sostituisce con versioni ricercabili:

private static readonly Dictionary<string, string> TechnicalTerms = new(StringComparer.OrdinalIgnoreCase)
{
    ["asp.net"] = "aspnet",
    ["c#"] = "csharp",
    [".net"] = "dotnet",
    ["f#"] = "fsharp",
    ["node.js"] = "nodejs",
    // ... more terms
};

4. Queries Like "ASP.NET and AlpineM SK3 Non funzionava

PostgreSQL tratta "e" come una parola di stop e la rimuove dalle ricercheM SK2 In combinazione con il problema del carattere specialeMSC3 una domanda naturale come "ASPMNK5NET e AlpineSSK6 sarebbe essenzialmente diventata una ricerca per "AlpineMS" soloMSSK9

La soluzione: Ha implementato un parsatore di query del tipo Google - che gestisce intelligentmente le parole stop e supporta operatori di ricerca avanzati

Le soluzioni

Operatori di ricerca Google-Style

Ho messo in atto un SearchQueryParser una classe che analizza le domande con il supporto di:

  1. Frasi citate: "exact match" - cerca la frase esatta.
  2. Condizioni Esclusive: -unwanted - esclude i risultati che contengono questo termine.
  3. Carte selvatiche: ASP* - corrisponde "ASP", "AspNETM SK4 "ASPNetCoreMSC6 eccMST7
  4. Condizioni tecniche: Gestisce automaticamente "ASPM SK2NET", "C+#", | | ".NET" ecc.

Il traduttore usa un modello di regex compilato per etichettare la domanda:

[GeneratedRegex(@"""([^""]+)""|(-)?(\S+)", RegexOptions.Compiled)]
private static partial Regex QueryTokenRegex();

public ParsedQuery Parse(string query)
{
    var matches = QueryTokenRegex().Matches(processedQuery);

    foreach (Match match in matches)
    {
        // Quoted phrase
        if (match.Groups[1].Success)
        {
            var phrase = match.Groups[1].Value.Trim();
            result.Phrases.Add(phrase);
            continue;
        }

        // Excluded term (starts with -)
        if (match.Groups[2].Success)
        {
            var term = match.Groups[3].Value.Trim();
            result.ExcludeTerms.Add(term.ToLowerInvariant());
            continue;
        }

        // Regular term or wildcard
        var token = match.Groups[3].Value.Trim();
        if (token.Contains('*'))
        {
            result.WildcardTerms.Add(token.Replace("*", ""));
        }
        else if (!StopWords.Contains(token))
        {
            result.IncludeTerms.Add(token.ToLowerInvariant());
        }
    }
}

Costruire PostgreSQL tsquery

Il programmatore produce dati strutturati che poi sono convertiti in PostgreSQL. to_tsquery Perché noi' generiamo una sintaxa strutturata - non. websearch_to_tsquery Perché noi, ', abbiamo già analizzato noi stessi gli operatori,, dandoci più controllo su come si combinano i termini.

public string BuildTsQuery(ParsedQuery parsed)
{
    var queryParts = new List<string>();

    // Add include terms with AND
    foreach (var term in parsed.IncludeTerms)
    {
        queryParts.Add(term);
    }

    // Add wildcard terms with :* suffix
    foreach (var term in parsed.WildcardTerms)
    {
        queryParts.Add($"{term}:*");
    }

    return queryParts.Count > 0 ? string.Join(" & ", queryParts) : string.Empty;
}

Perché non usare solo? websearch_to_tsquery? Perché continua a fallire in termini tecnici, acroniemi, e dominioM SK2sintax specifica MSC3e non offre alcun imbocco per classificazioni ibride o fallbacks . Analizzando noi stessiMska5 possiamo gestire questi casi marginali prima che raggiungano PostgreSQLMske6

La ricerca BuildSearch migliorata

L'informazione figura nella parte dispositiva. BuildSearchQuery il metodo è stato completamente riscritto per usare la struttura di query parsed. Notate che baseQuery già filtri secondo la lingua, gamma data, e visibilità M SK2 noiMSC3 stiamo aggiungendo ricerca -condizioni specifiche in altoMNK5

private IOrderedQueryable<BlogPostEntity> BuildSearchQuery(
    string query,
    string language,
    DateTime? startDate,
    DateTime? endDate,
    string order)
{
    var parsed = _queryParser.Parse(query);
    IQueryable<BlogPostEntity> searchQuery = baseQuery;

    // Handle phrases (exact substring matching)
    foreach (var phrase in parsed.Phrases)
    {
        searchQuery = searchQuery.Where(x =>
            EF.Functions.ILike(x.Title, $"%{phrase}%")
            || EF.Functions.ILike(x.PlainTextContent, $"%{phrase}%")
            || x.Categories.Any(c => EF.Functions.ILike(c.Name, $"%{phrase}%")));
    }

    // Build tsquery for include terms and wildcards
    var tsQuery = _queryParser.BuildTsQuery(parsed);

    // Apply full-text search if we have terms
    if (!string.IsNullOrWhiteSpace(tsQuery))
    {
        searchQuery = searchQuery.Where(x =>
            x.SearchVector.Matches(EF.Functions.ToTsQuery("english", tsQuery))
            || x.Categories.Any(c =>
                EF.Functions.ToTsVector("english", c.Name)
                    .Matches(EF.Functions.ToTsQuery("english", tsQuery))));
    }

    // Handle acronyms with case-insensitive substring search
    // This supplements full-text search (additive OR), not replaces it
    var acronymTerms = parsed.IncludeTerms
        .Concat(parsed.WildcardTerms)
        .Where(t => _queryParser.IsAcronymLike(t))
        .ToList();

    foreach (var acronym in acronymTerms)
    {
        searchQuery = searchQuery.Where(x =>
            EF.Functions.ILike(x.Title, $"%{acronym}%")
            || EF.Functions.ILike(x.PlainTextContent, $"%{acronym}%"));
    }

    // Handle excluded terms (must NOT contain these)
    foreach (var excludeTerm in parsed.ExcludeTerms)
    {
        searchQuery = searchQuery.Where(x =>
            !EF.Functions.ILike(x.Title, $"%{excludeTerm}%")
            && !EF.Functions.ILike(x.PlainTextContent, $"%{excludeTerm}%")
            && !x.Categories.Any(c => EF.Functions.ILike(c.Name, $"%{excludeTerm}%")));
    }

    return orderedQuery;
}

Esempi di ricerca

Ecco alcuni esempi della migliore funzionalità di ricerca:

La ricerca di base

DiSE

✅ Ora corrisponde agli articoli con "DiSE" nel titolo o nel contenuto SSK3caseM SK4insensitiveMSC5

Condizioni tecniche

ASP.NET

✅ Trova articoli su ASPM SK1NET (convertito automaticamente in SSK3aspnet" per la ricerca completa

La ricerca frasi

"semantic search"

✅ Trova la frase esatta "la ricerca semantica" negli articoli

Condizioni Esclusive

ASP.NET -Core

✅ Trova articoli ASPM SK1NET ma esclude quelli che menzionano "Core"

Carte selvatiche

ASP*

✅ Matches "ASP", "AspNETM SK4 "ASPNetCoreMSC6 etcMST7

La domanda complessa

"full text search" PostgreSQL -MySQL

✅ Trova articoli con la frase esatta "la ricerca del testo completoM SK2 E citazioni di PostgreSQL, ma escludendo gli articoli che menzionano MySQL

Esempio prima e dopo

La domanda: ASP.NET and Alpine

Prima (bloccatoM SK1

  • "ASP.NETM SK2 diviso in "AspMska4 + |
  • " e" rimuovete come parola di stop.
  • risultato: Solo le ricerche per "AlpineM SK2

Dopo. (fixed):

  • "ASPM SK1NET" convertito in "aspnetMSC4
  • "and" intelligentemente conservato come parte dell'intenzione di ricerca degli utenti
  • "Alpine" cercata normalmente
  • Result: Trova articoli su ASP.NET e Alpine

Integrazione del ranking RRF

La ricerca si integra con la fusione reciproca di classifica (RRFM SK1 come implementata nel articolo di ricerca semantica precedente. RRF qui è usato per La classificazione, non ricordare - combina già- risultati ottenuti dal BMM SK3 interoMSC4 ricerca di testo e ricerca semantica con il vettoreMST5

// Fuse using RRF with category/freshness boosts
var fusedDtos = _ranker.FuseResults(bm25Results, vectorResults, query);

L'algoritmo RRF usa 1/(k+rank) dove k=60 combina i risultati da diverse fonti , poi applica incrementi per:

  • Correspondenza tra categorie: +2.0
  • Correspondenza di titolo: +1.0
  • Frischezza: Decasione esponenziale nel corso di 1 anno

Per la completa implementazione del RRF e come funziona la ricerca ibrida, consultate La ricerca semantica in azione. Per approfondire le immersioni nelle inserzioni e la somiglianza dei vettoriM SK1 consultate Costruire una "Lawyer GPT" - Parte 3. La stessa infrastruttura ha potere sia per l'utente - per la ricerca che per il rilevamento RAG dell'AI- per il scrittura assistitaM SK3

L'architettura tecnica

Injezione di dipendenza

I nuovi componenti sono registrati come servizi:

services.AddSingleton<SearchQueryParser>();
services.AddSingleton<SearchRanker>();
services.AddScoped<BlogSearchService>();

SearchQueryParser e SearchRanker sono singletons perché'no statolessi,proiettile -sicuriM SK3e possono essere riutilizzati per tutte le richieste BlogSearchService è scoppato perché accessisce il contesto della base di dati.

Flusso di domanda

  1. L'utente inserisce una domanda di ricerca → Controller di ricerca
  2. Percezione della domanda → SearchQueryParser si divide in componenti strutturati
  3. La ricerca semantica (se è attivata) → Database di vectori Qdrant con inserzioni ONNX
  4. PostgreSQL completo-la ricerca di testo (solo disponibileM SK2 → BM25-combaciamento basato su keyword
  5. Fusione RRF → Combina entrambi i set di risultati con la categoriaM SK1incrementi di frescozza
  6. Nessun risultato. → Rende i post recenti come suggerimenti

Lo stesso componente di ricerca semantica viene anche usato dal Avvocato GPT il sistema per recuperare i post di un blog precedente rilevanti per l'AI-scrittura assistita. Vedete Parte 4 per i dettagli sul tubo di ingestione.

Schema della base di dati

La ricerca si basa su un precomputato. SearchVector in una colonna. BlogPosts Tabella:

ALTER TABLE mostlylucid."BlogPosts"
ADD COLUMN "SearchVector" tsvector
GENERATED ALWAYS AS (
    to_tsvector('english',
        coalesce("Title", '') || ' ' ||
        coalesce("PlainTextContent", '')
    )
) STORED;

CREATE INDEX idx_blog_posts_search_vector
ON mostlylucid."BlogPosts"
USING GIN ("SearchVector");

Il GIN (Generalized Inverted Index) fornisce una rapida ricerca completa del testo - attraverso un grosso corporato di testo

Considerazioni di performance

Perché ILIKE per acroniemi?

Mentre ILIKE (caseM SK1insensitive LIKE) è più lento di un'intera

  1. La ricerca completa-normalizza il testo/termina i terminiM SK2 rompe le stringhe in alto intestino brevi.
  2. Gli acroniemi sono tipicamente brevi (≤6 caratteriM SK1 limitando l'impatto sul risultato.
  3. La condizione viene aggiunto solo quando è necessario (acronimi rilevati)

Questo approccio non si sarebbe adattato alla ricerca arbitraria di sottostringi su milioni di linee -ma per gli acronimi mirati il fallback è ' l'appropriato.

PostgreSQL Cover Density Ranking (ts_rank_cd)

PostgreSQL offre due funzioni di classificazione per la ricerca completa.

  • ts_rank: Frequenza di termine base ( quante volte si presentano i termini)
  • ts_rank_cd: Ripartimento della densità di copertura (come le condizioni si presentano insiemeM SK2

Noi usiamo. ts_rank_cd Perché fornisce Relevance simile a BM25- Considerando la vicinanza del termine:

// Order by cover density ranking - rewards term proximity
orderedQuery = searchQuery.OrderByDescending(x =>
    x.SearchVector.RankCoverDensity(EF.Functions.ToTsQuery("english", tsQuery)));

Perché? ts_rank_cd è superiore:

Metrica ts_rank ts_rank_cd
Algoritmo Frequenza territoriale Densità di copertura proximitàMSC3 S
Multi-questioni di parole Conta i termini separatamente Termini di ricompensa che si presentano insieme
Esempio: M SK1docker containers" Un articolo con "docker" 50 ha un punteggio alto SSK4 Un artigo con "doker containersM SK6 ha una punteggio più alto
La performance veloce Marginalmente più lentoM SK2 ancora sfrutta l'indice GIN
Comportamento Calcolo semplice Approximato BM

Questo è un l'ottimizzazione di rapidi vincitori - migliore classificazione di rilevanza con un'applicazione zero- calcolo di livelloM SK2 Tutte le classifiche si fanno in PostgreSQL usando l'indice GIN esistente su SearchVector.

Referenze:

Optimizzazioni di prestazioni aggiuntive

Oltre a riparare la funzione di ricerca, alcune ottimizzazioni chiave migliorano la performance:

1. Caching linguistico

Le lingue disponibili raramente cambiano ma sono state consultate su ogni richiesta di ricerca. Ora in cassa con il doppio-controllato l'interrutturaM SK2

private static readonly TimeSpan LanguageCacheDuration = TimeSpan.FromHours(1);
private static List<string>? _cachedLanguages;
private static DateTime _languageCacheExpiry = DateTime.MinValue;
private static readonly SemaphoreSlim _cacheLock = new(1, 1);

Impacto: Esclude la domanda di base 1 per ogni richiesta di ricerca

2. Queries ILIKE in serie

Il codice originale usato foreach loops che creano più clause WHERE. Ora batchiate in espressioni singole:

// BEFORE: Multiple WHERE clauses
foreach (var acronym in acronymTerms)
{
    searchQuery = searchQuery.Where(x =>
        EF.Functions.ILike(x.Title, $"%{acronym}%"));
}

// AFTER: Single batched WHERE
if (acronymTerms.Count > 0)
{
    searchQuery = searchQuery.Where(x =>
        acronymTerms.Any(acronym =>
            EF.Functions.ILike(x.Title, $"%{acronym}%")));
}

Impacto: SQL più pulito, ~5-10% più veloce per le domande a termineM SK3

3. Indice di copertura per le domande comuni

Added partial index with INCLUDE columns for frequent access patterns:

CREATE INDEX idx_blog_posts_search_covering
ON mostlylucid."BlogPosts" ("LanguageId", "IsHidden", "ScheduledPublishDate")
INCLUDE ("Id", "Slug", "Title", "PublishedDate")
WHERE "IsHidden" = false;

Impacto: Si accende index-scanna solo - PostgreSQL non ha bisogno di avere accesso alla mappa della tavola , riducendo l'I/O a 20-30% per le domande comuniM SK5

4. Removed Unnecessary Includes

Core EF's Include() loads full navigation entities. Removed where only navigation properties used in WHERE clauses:

// BEFORE: Loads full LanguageEntity into memory
.Include(x => x.LanguageEntity)
.Where(x => x.LanguageEntity.Name == "en")

// AFTER: EF translates navigation property without loading entity
.Where(x => x.LanguageEntity.Name == "en")

Impacto: ~5-10% riduzione della memoriaM SK2 meno dati trasferiti dalla base di dati.

Referenze:

Strategia di ottimizzazione della domanda

La ricerca crea WHERE clause incrementally usando La composizione dell'albero dell'espressione di EF Core':

IQueryable<BlogPostEntity> searchQuery = baseQuery;

// Each filter added conditionally - PostgreSQL optimizes the final query
if (parsed.Phrases.Count > 0) { searchQuery = searchQuery.Where(...); }
if (!string.IsNullOrWhiteSpace(tsQuery)) { searchQuery = searchQuery.Where(...); }
if (acronymTerms.Count > 0) { searchQuery = searchQuery.Where(...); }

Questo genera un una singola domanda SQL ottimizzata . PostgreSQL' il programmatore di query può usare statistiche e indexi in modo efficace quando vede la completa frase WHEREM SK2

riassunto del risultato

Impatti cumulattivi di tutte le ottimizzazioni:

Optimizzazione Impatti di latenza Imppatti della carica del DB M
Caching linguistico Minimale
Rimuovere Includere() -5-10% Meno trasferimento di dati
Batch ILIKE -5-10% ≥ Cleaner SQL
Indice di copertura -20-30% Indiceso-scan solo S
tsM SK1rank_cd Analogo Rilevantere S
Riscaldamento del cachio (parami di rotazione) NM SK4A M Prevede i dati immutabili R

Aumento totale previsto: 30-50% ricerca più veloce con una significativa riduzione del carico della base di dati.

Lezioni imparate

  1. Don't assume full-la ricerca di testo gestisce tutto: Case a bordo come acroniemi e caratteri speciali hanno bisogno di un trattamento speciale.

  2. Combinare diversi approcci.: La ricerca completaM SK1la ricerca del testo (BM25) + la ricerca semantica SSK5vectoriMSC6 + i fallback della sottostringa forniscono una copertura migliore di qualsiasi singolo metodoMNK8

  3. Google ha formato le aspettative degli utenti: Sostenere le frasi citate, escluzioniM SK2 e cartoline selvatiche fa sembrare la ricerca naturale perché gli utenti sono già familiari con questi operatori

  4. Fornire dei collaterali utili.: Quando la ricerca fallisceM SK1 non ' non mostra nulla - mostra i post recenti e rende chiaro che non è stato trovato nessun corrispondenza

  5. Parlare, don't hack: Un parsatore di query appropriato è più pulito e più mantenibile di una serie di manipolazioni di stringhe.

  6. Leverage PostgreSQL's built-in ranking: Usare ts_rank_cd invece di ts_rank per BM25-relevance simileM SK1 La classifica della densità di copertura considera la vicinanza del termine - una vittoria veloce che migliora la qualità dei risultati con zero applicazione -codè di livelloMSC4

  7. Profilo prima di ottimizzare: I "ovvi" bottiglie di bottiglia (ParsingFTSM SK4 non erano il problema realeMSC5I lookups linguisticiMSL7 include troppoMST8 e gli indici mancanti hanno avuto un impatto più grande che previstoMSM9

  8. Operazioni di base di dati a lotto: Molto foreach loop create separate WHERE clauses generate suboptimal SQL. Usare Any() o All() per batchiare in espressioni singole.

futuri miglioramenti

Possibili miglioramenti per il futuro, classificati secondo le preoccupazioniM SK1

Riparazioni alla ricerca:

  • Correspondenza confusa: La distanza di Levenshtein per la tolleranza al tipo.
  • L'espansione del sinonimo:

Riparazioni:

  • Filtrazione delle categorie: category:ASP.NET l'operatore
  • La gamma della data: after:2025-01-01 l'operatore

Ridefinizioni di classificazione:

  • Evidenza dei risultati: Mostrare parti di testo corrispondenti nei risultati
  • Click- attraverso il tracciamento: Imparare dal comportamento degli utenti per migliorare il ranking

Osservabilità:

  • Analitica della ricerca: Seguire le domande popolari e le ricerche fallite per identificare le lacune

Conclusione

La ricerca a livello di costruzione- richiede riparare le case a bordo. e Optimizzare la performance. Questo articolo ha riguardato sia l'aggiustazione della PostgreSQL completa, sia le case di edge di ricerca del testo, sia gli acroniemi, sia i termini tecnici, sia Google, sia -, gli operatori di stile, ma anche ), e l'implementazione delle principali ottimizzazioni di performance. ts_rank_cd).

L'implementazione 30-50% ricerca più veloce riducendo il carico della base di dati attraverso:

  • Ripartimento della densità di copertura (ts_rank_cd) per una migliore rilevanza
  • Il caching linguistico elimina le domande ripetute.
  • Espresi ILIKE in batchi per SQL più pulito
  • Coprire gli indice che permettono agli index-di fare solo scansioni.
  • Rimuovere un nucleo EF inutile include

L'intuizione chiave: nessun approccio singolo gestisce tutti i casi. PostgreSQL FTS è eccezionale nella combinazione di parole chiave , la ricerca semantica gestisce le domande concettualiM SK3 e gli fallbacks mirati catturano i casi a margineMSC4 La strata di parizzazione pulita lo collega insiemeMST5 gestisce gli operatori e i termini tecnici prima che raggiungano i motori di ricercaM ST6

L'infrastruttura di ricerca semantica serve a due scopi:

  • Usatore- ricerca orientata: Combina keyword e semantic matching per ottenere migliori risultati
  • Ricerca RAG: Powers the Avvocato GPT assistente di scrittura

Article connessi:

Documentazione ufficiale:

Tutti i codici disponibili nel blog's Reposito di GitHub.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.