Dela via


Fulltextsökning i EF Core-leverantören för SQL Server

SQL Server tillhandahåller funktioner för fulltextsökning som möjliggör avancerad textsökning utöver enkla LIKE mönster. Fulltextsökning stöder språklig matchning, böjliga formulär, närhetssökning och viktad rangordning.

EF Cores SQL Server-provider stöder både fulltextsökningspredikat (för filtrering) och tabellvärdesfunktioner (för filtrering med rangordning).

Innan du använder fulltextsökning måste du skapa en fulltextkatalog i databasen och ett fulltextindex för de kolumner som du vill söka i.

Anmärkning

Fullständig textkatalog och indexhantering i migreringar introducerades i EF Core 11.

Du kan konfigurera fulltextkataloger och index direkt i EF-modellen. När du lägger till en migrering genererar EF lämplig SQL för att skapa (eller ändra) katalogen och indexet åt dig.

Definiera först en fulltextkatalog i modellen och konfigurera sedan ett fulltextindex för din entitetstyp:

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

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

Metoden HasKeyIndex() anger det unika, icke-nullbara indexet med en kolumn som används som fulltextnyckel för tabellen (vanligtvis primärnyckelindexet). OnCatalog() tilldelar fulltextindexet till en specifik katalog.

Du kan också konfigurera flera kolumner och ytterligare alternativ, till exempel språk per kolumn och ändringsspårning:

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

Fulltextkatalogen kan också konfigureras som standardkatalog och med accentkänslighet:

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

Mer information finns i sql Server-dokumentationen för fulltextsökning.

Fulltextpredikat

EF Core stöder predikaten FREETEXT() och CONTAINS() som används i Where() satser för att filtrera resultat.

FREETEXT()

FREETEXT() utför en mindre strikt matchning och söker efter ord baserat på deras betydelse, inklusive böjliga former (till exempel verb tempus och substantiv plural):

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

Detta innebär att:

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

Du kan också ange en språkterm:

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

CONTAINS()

CONTAINS() utför mer exakt matchning och stöder mer avancerade sökvillkor, inklusive prefixtermer, närhetssökning och viktade termer:

// 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();

Detta innebär att:

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

Mer information om CONTAINS() frågesyntax finns i SQL Server CONTAINS-dokumentationen.

Tabellvärdesfunktioner i fulltext

Anmärkning

Tabellvärdesfunktioner i fulltext introduceras i EF Core 11.

Även om predikaten ovan är användbara för filtrering ger de inte rangordningsinformation. SQL Server-tabellvärdesfunktioner FREETEXTTABLE() och CONTAINSTABLE() returnerar både matchande rader och en rangordningspoäng som anger hur väl varje rad matchar sökfrågan.

FreeTextTable()

FreeTextTable() är tabellvärdesfunktionens version av FreeText(). Den returnerar FullTextSearchResult<TEntity>, som innehåller både entiteten och rangordningsvärdet:

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}");
}

Observera att du måste ange de allmänna typparametrarna. Article motsvarar den entitetstyp som genomsöks, där int är den fulltextsöknyckel som angavs när indexet skapades och som returneras av FREETEXTTABLE().

Ovanstående söker automatiskt över alla kolumner som registrerats för fulltextsökning och returnerar de 10 bästa matchningarna. Du kan också ange en specifik kolumn för sökning:

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();

... eller flera kolumner:

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() är den tabellvärdesbaserade funktionsversionen av Contains(), som stöder samma avancerade söksyntax och ger även rangordningsinformation:

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();

Begränsa resultat

Båda tabellvärdesfunktionerna stöder en topN parameter för att begränsa antalet resultat:

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();

Ange ett språk

Båda tabellvärdesfunktionerna har stöd för att ange en språkterm för språkmatchning:

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

När du ska använda predikat jämfört med tabellvärdesfunktioner

Feature Predikat (FreeText(), Contains()) Tabellvärdesfunktioner (FreeTextTable(), ContainsTable())
Tillhandahåller rangordning ❌ Nej ✅ Ja
Prestanda för stora resultatuppsättningar Bättre för filtrering Bättre för rangordning och sortering
Kombinera med andra entiteter Via anslutningar Inbyggt entitetsresultat
Använda i-sats Where() ✅ Ja ❌ Nej (använd som källa)

Använd predikat när du helt enkelt behöver filtrera resultat baserat på villkor för fulltextsökning. Använd tabellvärdesfunktioner när du behöver rangordningsinformation för att beställa resultat efter relevans eller visa relevanspoäng för användare.