通过


在 SQL Server EF Core 提供程序中使用全文搜索

SQL Server 提供 全文搜索 功能,使复杂的文本搜索超出简单 LIKE 模式。 全文搜索支持语言学匹配、词形变化、邻近搜索和加权排名。

EF Core 的 SQL Server 提供程序支持全文搜索 谓词 (用于筛选)和 表值函数 (用于按排名进行筛选)。

在使用全文搜索之前,必须在数据库上创建 全文目录 ,并在要搜索的列上创建 全文索引

注释

EF Core 11 中引入了迁移中的全文检索目录和索引管理。

可以直接在 EF 模型中配置全文目录和索引。 添加 迁移时,EF 将生成相应的 SQL 以创建或更改目录和索引。

首先,在模型上定义全文目录,然后在实体类型上配置全文索引:

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

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

该方法 HasKeyIndex() 指定用作表全文键的唯一、不可为 null 的单列索引(通常是主键索引)。 OnCatalog() 将全文索引分配给特定目录。

还可以配置多个列和其他选项,例如每列语言和更改跟踪:

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

全文目录也可以配置为默认目录,并且具有重音敏感度:

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

有关详细信息,请参阅 SQL Server 全文搜索文档

全文谓词

EF Core 支持 FREETEXT()CONTAINS() 谓词,它们用于 Where() 子句中来筛选结果。

FREETEXT()

FREETEXT() 执行不太严格的匹配,基于单词的含义搜索,包括词形变化形式(如动词的时态变位和名词的复数形式):

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

这表示:

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

可以选择指定语言术语:

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

CONTAINS()

CONTAINS() 执行更精确的匹配,并支持更复杂的搜索条件,包括前缀词、邻近搜索和加权词:

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

这表示:

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

有关查询语法的详细信息 CONTAINS() ,请参阅 SQL Server CONTAINS 文档

全文表值函数

注释

EF Core 11 中引入了全文表值函数。

虽然上述谓词可用于筛选,但它们不提供排名信息。 SQL Server 的表值函数 FREETEXTTABLE()CONTAINSTABLE() 返回匹配的行和排名分数,指示每行与搜索查询的匹配程度。

FreeTextTable()

FreeTextTable() 是表值函数版本的 FreeText()。 它返回 FullTextSearchResult<TEntity>,其中包括实体和排名值:

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

请注意,必须提供泛型类型参数,Article 对应要搜索的实体类型,int 是在创建索引时指定的全文检索键,并且由 FREETEXTTABLE() 返回。

系统会自动在所有已注册用于全文检索的列中搜索,并返回前 10 个匹配项。 您还可以指定要搜索的特定列:

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

...或多列:

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() 是表值函数版本的 Contains(),支持相同的复杂搜索语法,同时提供排名信息:

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

限制结果

这两个表值函数都支持参数 topN 来限制结果数:

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

指定语言

这两个表值函数都支持指定用于语言匹配的语言参数:

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

何时使用谓词与表值函数

功能 谓词 (FreeText()Contains() 表值函数 (FreeTextTable()ContainsTable()
提供排名 ❌ 否 ✅ 是
大型结果集的性能 更适合筛选 更适合进行排名与排序
与其他实体合并 通过联接 内置实体结果
在子句中使用Where() ✅ 是 ❌ 否(用作源)

只需根据全文搜索条件筛选结果时,请使用谓词。 如果需要排名信息,以便按相关性或向用户显示相关性分数对结果进行排序,请使用表值函数。