Condividi tramite


Ricerca full-text nel provider EF Core di SQL Server

SQL Server offre funzionalità di ricerca full-text che consentono ricerche di testo sofisticate oltre a modelli semplici LIKE . La ricerca full-text supporta il confronto linguistico, le forme flesse, la ricerca di prossimità e la classificazione ponderata.

Il provider SQL Server di EF Core supporta predicati di ricerca full-text (per il filtro) e funzioni con valori di tabella (per filtrare con classificazione).

Prima di usare la ricerca full-text, è necessario creare un catalogo full-text nel database e un indice full-text nelle colonne da cercare.

Annotazioni

Il catalogo full-text e la gestione degli indici nelle migrazioni sono stati introdotti in EF Core 11.

È possibile configurare cataloghi e indici full-text direttamente nel modello di Entity Framework. Quando si aggiunge una migrazione, Entity Framework genererà il codice SQL appropriato per creare (o modificare) il catalogo e l'indice.

Prima di tutto, definire un catalogo full-text nel modello, quindi configurare un indice full-text nel tipo di entità:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasFullTextCatalog("ftCatalog");

    modelBuilder.Entity<Article>()
        .HasFullTextIndex(a => a.Contents)
        .HasKeyIndex("PK_Articles")
        .OnCatalog("ftCatalog");
}

Il HasKeyIndex() metodo specifica l'indice univoco, non annullabile, a colonna singola utilizzato come chiave full-text per la tabella (in genere l'indice della chiave primaria). OnCatalog() assegna l'indice full-text a un catalogo specifico.

È anche possibile configurare più colonne e opzioni aggiuntive, ad esempio lingue per colonna e rilevamento modifiche:

modelBuilder.Entity<Article>()
    .HasFullTextIndex(a => new { a.Title, a.Contents })
    .HasKeyIndex("PK_Articles")
    .OnCatalog("ftCatalog")
    .WithChangeTracking(FullTextChangeTracking.Manual)
    .HasLanguage("Title", "English")
    .HasLanguage("Contents", "French");

Il catalogo full-text può anche essere configurato come catalogo predefinito con sensibilità agli accenti.

modelBuilder.HasFullTextCatalog("ftCatalog")
    .IsDefault()
    .IsAccentSensitive(false);

Per altre informazioni, vedere la documentazione di ricerca full-text di SQL Server.

Predicati di testo completo

EF Core supporta i FREETEXT() predicati e CONTAINS() , usati nelle Where() clausole per filtrare i risultati.

FREETEXT()

FREETEXT() esegue una corrispondenza meno rigorosa, cercando parole in base al loro significato, incluse le forme flessive (ad esempio i verbi tensi e i plurali sostantivali):

var articles = await context.Articles
    .Where(a => EF.Functions.FreeText(a.Contents, "veggies"))
    .ToListAsync();

Questo si traduce in:

SELECT [a].[Id], [a].[Title], [a].[Contents]
FROM [Articles] AS [a]
WHERE FREETEXT([a].[Contents], N'veggies')

Facoltativamente, è possibile specificare un termine linguistico:

var articles = await context.Articles
    .Where(a => EF.Functions.FreeText(a.Contents, "veggies", "English"))
    .ToListAsync();

CONTAINS()

CONTAINS() esegue una corrispondenza più precisa e supporta criteri di ricerca più sofisticati, tra cui termini di prefisso, ricerca di prossimità e termini ponderati:

// Simple search
var articles = await context.Articles
    .Where(a => EF.Functions.Contains(a.Contents, "veggies"))
    .ToListAsync();

// Prefix search (words starting with "vegg")
var articles = await context.Articles
    .Where(a => EF.Functions.Contains(a.Contents, "\"vegg*\""))
    .ToListAsync();

// Phrase search
var articles = await context.Articles
    .Where(a => EF.Functions.Contains(a.Contents, "\"fresh vegetables\""))
    .ToListAsync();

Questo si traduce in:

SELECT [a].[Id], [a].[Title], [a].[Contents]
FROM [Articles] AS [a]
WHERE CONTAINS([a].[Contents], N'veggies')

Per altre informazioni sulla sintassi delle CONTAINS() query, vedere la documentazione di SQL Server CONTAINS.

Funzioni con valori di tabella full-text

Annotazioni

Le funzioni con valori di tabella full-text vengono introdotte in EF Core 11.

Anche se i predicati precedenti sono utili per filtrare, non forniscono informazioni di classificazione. Le funzioni FREETEXTTABLE() con valori di tabella di SQL Server e CONTAINSTABLE() restituiscono sia righe corrispondenti che un punteggio di classificazione che indica il livello di corrispondenza di ogni riga corrispondente alla query di ricerca.

FreeTextTable()

FreeTextTable() è la versione della funzione con valori di tabella di FreeText(). Restituisce FullTextSearchResult<TEntity>, che include sia l'entità che il valore di classificazione:

var results = await context.Articles
    .Join(
        context.Articles.FreeTextTable<Article, int>("veggies", topN: 10),
        a => a.Id,
        ftt => ftt.Key,
        (a, ftt) => new { Article = a, ftt.Rank })
    .OrderByDescending(r => r.Rank)
    .ToListAsync();

foreach (var result in results)
{
    Console.WriteLine($"Article {result.Article.Id} with rank {result.Rank}");
}

Si noti che è necessario specificare i parametri di tipo generico; Article corrisponde al tipo di entità in cui viene eseguita la ricerca, dove int è la chiave di ricerca full-text specificata durante la creazione dell'indice e restituita da FREETEXTTABLE().

La ricerca automatica sopra inclusa esegue la ricerca a testo intero in tutte le colonne registrate e restituisce le prime 10 corrispondenze. È anche possibile specificare una colonna specifica per la ricerca:

var results = await context.Articles
    .Join(
        context.Articles.FreeTextTable<Article, int>(a => a.Contents, "veggies"),
        a => a.Id,
        ftt => ftt.Key,
        (a, ftt) => new { Article = a, ftt.Rank })
    .OrderByDescending(r => r.Rank)
    .ToListAsync();

... o più colonne:

var results = await context.Articles
    .FreeTextTable(a => new { a.Title, a.Contents }, "veggies")
    .Select(r => new { Article = r.Value, Rank = r.Rank })
    .OrderByDescending(r => r.Rank)
    .ToListAsync();

ContainsTable()

ContainsTable() è la versione della funzione che restituisce valori di tabella di Contains(), supportando la stessa sintassi sofisticata di ricerca e fornendo anche informazioni di ranking:

var results = await context.Articles
    .Join(
        context.Articles.ContainsTable<Article, int>( "veggies OR fruits"),
        a => a.Id,
        ftt => ftt.Key,
        (a, ftt) => new { Article = a, ftt.Rank })
    .OrderByDescending(r => r.Rank)
    .ToListAsync();

Limitazione dei risultati

Entrambe le funzioni con valori di tabella supportano un topN parametro per limitare il numero di risultati:

var results = await context.Articles
    .FreeTextTable(a => a.Contents, "veggies", topN: 10)
    .Select(r => new { Article = r.Value, Rank = r.Rank })
    .OrderByDescending(r => r.Rank)
    .ToListAsync();

Specifica di una lingua

Entrambe le funzioni con valori di tabella supportano la specifica di un termine linguistico per la corrispondenza linguistica:

var results = await context.Articles
    .FreeTextTable(a => a.Contents, "veggies", languageTerm: "English")
    .Select(r => new { Article = r.Value, Rank = r.Rank })
    .ToListAsync();

Quando usare predicati e funzioni con valori di tabella

Feature Predicati (FreeText(), Contains()) Funzioni con valori di tabella (FreeTextTable(), ContainsTable())
Fornisce la classificazione ❌ No ✅ Sì
Prestazioni per set di risultati di grandi dimensioni Migliore per filtrare Migliore per la classificazione e l'ordinamento
Combinare con altre entità via join Risultato entità integrata
Usare nella clausola Where() ✅ Sì ❌ No (usare come origine)

Usare i predicati quando è sufficiente filtrare i risultati in base ai criteri di ricerca full-text. Usare funzioni con valori di tabella quando sono necessarie informazioni di classificazione per ordinare i risultati in base alla pertinenza o visualizzare i punteggi di pertinenza per gli utenti.