Partilhar via


Pesquisa de Texto Completo no Provedor SQL Server EF Core

O SQL Server oferece capacidades de pesquisa em texto completo que permitem uma pesquisa de texto sofisticada para além de padrões simples LIKE . A pesquisa de texto completo suporta correspondência linguística, formas flexivas, pesquisa por proximidade e classificação ponderada.

O fornecedor SQL Server da EF Core suporta tanto predicados de pesquisa em texto completo (para filtragem) como funções de tabela (para filtragem com ranking).

Antes de usar a pesquisa por texto completo, deve criar um catálogo em texto completo na sua base de dados e um índice em texto integral nas colunas que pretende pesquisar.

Observação

A gestão de catálogos e índices em texto completo nas migrações foi introduzida no EF Core 11.

Pode configurar catálogos e índices em texto completo diretamente no seu modelo EF. Quando adiciona uma migração, o EF gera o SQL apropriado para criar (ou alterar) o catálogo e o índice para si.

Primeiro, defina um catálogo de texto completo sobre o modelo, depois configure um índice de texto completo sobre o seu tipo de entidade:

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

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

O HasKeyIndex() método especifica o índice único, não anulável e de coluna única, usado como chave de texto completo para a tabela (tipicamente o índice da chave primária). OnCatalog() atribui o índice do texto completo a um catálogo específico.

Também pode configurar múltiplas colunas e opções adicionais, como linguagens por coluna e acompanhamento de alterações:

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

O catálogo de texto completo também pode ser configurado como catálogo padrão, e com sensibilidade ao acento:

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

Para mais informações, consulte a documentação de pesquisa em texto integral do SQL Server.

Predicados de texto completo

O EF Core suporta os predicados FREETEXT() e CONTAINS(), que são usados nas cláusulas Where() para filtrar resultados.

TEXTO LIVRE()

FREETEXT() realiza uma correspondência menos estrita, procurando palavras com base no respetivo significado, incluindo formas flexionais (como tempos verbais e plurais nominais):

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

Isto traduz-se em:

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

Pode opcionalmente especificar um termo linguístico:

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

CONTÉM()

CONTAINS() realiza correspondências mais precisas e suporta critérios de pesquisa mais sofisticados, incluindo termos prefixos, pesquisa por proximidade e termos ponderados:

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

Isto traduz-se em:

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

Para mais informações sobre CONTAINS() a sintaxe das consultas, consulte a documentação do SQL Server CONTAINS.

Funções com valores de tabela em texto completo

Observação

Funções com valores de tabela em texto completo estão a ser introduzidas no EF Core 11.

Embora os predicados acima sejam úteis para filtrar, não fornecem informação de classificação. As funções de valores de tabela do SQL Server FREETEXTTABLE() e CONTAINSTABLE() devolvem tanto as linhas correspondentes quanto uma pontuação de classificação que indica quão bem cada linha corresponde à consulta de pesquisa.

FreeTextTable()

FreeTextTable() é a função com valores de tabela de FreeText(). Devolve FullTextSearchResult<TEntity>, que inclui tanto a entidade como o valor de classificação:

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

Note que deve fornecer os parâmetros genéricos do tipo; Article corresponde ao tipo de entidade pesquisado, onde int é a chave de pesquisa em texto completo especificada ao criar o índice, e que é devolvida por FREETEXTTABLE().

A função acima efetua automaticamente a pesquisa em todas as colunas registadas para pesquisa em texto completo e retorna as 10 principais correspondências. Também pode fornecer uma coluna específica para pesquisar:

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

... ou múltiplas colunas:

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() é a versão de função com valores de tabela de Contains(), suportando a mesma sintaxe sofisticada de pesquisa enquanto fornece também informação de classificação:

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

Limitar os resultados

Ambas as funções de tabela suportam um topN parâmetro para limitar o número de resultados:

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

Especificação de uma língua

Ambas as funções com valores de tabela suportam a especificação de um termo linguístico para correspondência linguística:

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

Quando usar predicados vs funções com valores de tabela

Feature Predicados (FreeText(), Contains()) Funções com valores de tabela (FreeTextTable(), ContainsTable())
Fornece classificação ❌ Não ✅ Sim
Desempenho para conjuntos de resultados grandes Melhor para filtrar Melhor para classificação e ordenação
Combinar com outras entidades Junções via Resultado da entidade integrada
Uso na Where() cláusula ✅ Sim ❌ Não (uso como fonte)

Use predicados quando simplesmente precisar de filtrar os resultados com base em critérios de pesquisa em texto completo. Use funções de tabela quando precisar de informação de classificação para ordenar os resultados por relevância ou mostrar as pontuações de relevância aos utilizadores.