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() |
✅ 是 | ❌ 否(用作源) |
只需根据全文搜索条件筛选结果时,请使用谓词。 如果需要排名信息,以便按相关性或向用户显示相关性分数对结果进行排序,请使用表值函数。