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

<!--category-- ASP.NET, PostgreSQL, Search, RRF -->
<datetime class="hidden">2026-01-14T12:00</datetime>

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](/blog/textsearchingpt2), 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**](https://www.lucidrag.com) 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](/blog/semantic-search-in-action) 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](/blog/building-a-lawyer-gpt-for-your-blog-part1) 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](/blog/building-a-lawyer-gpt-for-your-blog-part3) 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?

```csharp
// 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):

```csharp
// 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:

```csharp
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:

```csharp
[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.

```csharp
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

```csharp
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](/blog/semantic-search-in-action#hybrid-search-with-reciprocal-rank-fusion). 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

```csharp
// 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](/blog/semantic-search-in-action). Per approfondire le immersioni nelle inserzioni e la somiglianza dei vettoriM SK1 consultate [Costruire una "Lawyer GPT" - Parte 3](/blog/building-a-lawyer-gpt-for-your-blog-part3). 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:

```csharp
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](/blog/building-a-lawyer-gpt-for-your-blog-part1) il sistema per recuperare i post di un blog precedente rilevanti per l'AI-scrittura assistita. Vedete [Parte 4](/blog/building-a-lawyer-gpt-for-your-blog-part4) 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:

```sql
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:

```csharp
// 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:**

- [PostgreSQL ts_rank_cd documentation](https://www.postgresql.org/docs/current/textsearch-controls.html#TEXTSEARCH-RANKING)
- [EF Core RankCoverDensity](https://learn.microsoft.com/en-us/ef/core/providers/postgres/misc#full-text-search)

### 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

```csharp
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:

```csharp
// 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:

```sql
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](https://www.postgresql.org/docs/current/indexes-index-only-scans.html) - 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:

```csharp
// 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:**

- [Indice PostgreSQL](https://www.postgresql.org/docs/current/indexes.html)
- [EF Core Performance](https://learn.microsoft.com/en-us/ef/core/performance/)

### Strategia di ottimizzazione della domanda

La ricerca crea WHERE clause incrementally usando [La composizione dell'albero dell'espressione di EF Core'](https://learn.microsoft.com/en-us/ef/core/querying/):

```csharp
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](/blog/building-a-lawyer-gpt-for-your-blog-part1) assistente di scrittura

**Article connessi:**

- [La ricerca semantica in azione](/blog/semantic-search-in-action) - Foundation: ricerca ibrida con RRF
- [Costruire una "Lawyer GPT" - Parte 3](/blog/building-a-lawyer-gpt-for-your-blog-part3) - Embeddings & database dei vettori
- [Costruire una "Lawyer GPT" - Parte 4](/blog/building-a-lawyer-gpt-for-your-blog-part4) - Pipeline di ingestione

**Documentazione ufficiale:**

- [PostgreSQL Full-Text Search](https://www.postgresql.org/docs/current/textsearch.html)
- [Indice PostgreSQL](https://www.postgresql.org/docs/current/indexes.html)
- [EF Core Performance](https://learn.microsoft.com/en-us/ef/core/performance/)
- [EF Core Npgsql Provider](https://www.npgsql.org/efcore/index.html)

Tutti i codici disponibili nel [blog's Reposito di GitHub](https://github.com/scottgal/mostlylucidweb).