Como destacar resultados de uma consulta de texto completo do SQL Server
-
09-06-2019 - |
Pergunta
Temos uma aplicação web que usa o SQL Server 2008 como banco de dados.Nossos usuários podem fazer pesquisas de texto completo em colunas específicas do banco de dados.A funcionalidade de texto completo do SQL Server não parece fornecer suporte para destaque de ocorrências.Precisamos construir isso nós mesmos ou talvez haja alguma biblioteca ou conhecimento sobre como fazer isso?
Aliás, o aplicativo é escrito em C#, portanto, uma solução .Net seria ideal, mas não necessária, pois poderíamos traduzir.
Solução
Expandindo a ideia de Ismael, não é a solução final, mas acho que é uma boa maneira de começar.
Primeiramente precisamos obter a lista de palavras que foram recuperadas com o mecanismo de texto completo:
declare @SearchPattern nvarchar(1000) = 'FORMSOF (INFLECTIONAL, " ' + @SearchString + ' ")'
declare @SearchWords table (Word varchar(100), Expansion_type int)
insert into @SearchWords
select distinct display_term, expansion_type
from sys.dm_fts_parser(@SearchPattern, 1033, 0, 0)
where special_term = 'Exact Match'
Já há bastante coisa que pode ser expandida, por exemplo, o padrão de pesquisa é bastante básico;provavelmente também existem maneiras melhores de filtrar as palavras que você não precisa, mas pelo menos fornece uma lista de palavras-tronco, etc.que seria correspondido pela pesquisa de texto completo.
Depois de obter os resultados necessários, você pode usar o RegEx para analisar o conjunto de resultados (ou, de preferência, apenas um subconjunto para acelerá-lo, embora eu ainda não tenha descoberto uma boa maneira de fazer isso).Para isso eu simplesmente uso dois loops while e um monte de tabelas e variáveis temporárias:
declare @FinalResults table
while (select COUNT(*) from @PrelimResults) > 0
begin
select top 1 @CurrID = [UID], @Text = Text from @PrelimResults
declare @TextLength int = LEN(@Text )
declare @IndexOfDot int = CHARINDEX('.', REVERSE(@Text ), @TextLength - dbo.RegExIndexOf(@Text, '\b' + @FirstSearchWord + '\b') + 1)
set @Text = SUBSTRING(@Text, case @IndexOfDot when 0 then 0 else @TextLength - @IndexOfDot + 3 end, 300)
while (select COUNT(*) from @TempSearchWords) > 0
begin
select top 1 @CurrWord = Word from @TempSearchWords
set @Text = dbo.RegExReplace(@Text, '\b' + @CurrWord + '\b', '<b>' + SUBSTRING(@Text, dbo.RegExIndexOf(@Text, '\b' + @CurrWord + '\b'), LEN(@CurrWord) + 1) + '</b>')
delete from @TempSearchWords where Word = @CurrWord
end
insert into @FinalResults
select * from @PrelimResults where [UID] = @CurrID
delete from @PrelimResults where [UID] = @CurrID
end
Várias notas:
1.Loops while aninhados provavelmente não são a maneira mais eficiente de fazer isso, mas nada mais vem à mente.Se eu usasse cursores, seria essencialmente a mesma coisa?
2. @FirstSearchWord
aqui, refere-se à primeira instância no texto de uma das palavras de pesquisa originais, portanto, essencialmente, o texto que você está substituindo estará apenas no resumo.Novamente, é um método bastante básico, algum tipo de algoritmo de localização de cluster de texto provavelmente seria útil.
3.Para obter RegEx em primeiro lugar, você precisa de funções CLR definidas pelo usuário.
Outras dicas
Parece que você poderia analisar a saída do novo Procedimento armazenado do SQL Server 2008 sys.dm_fts_parser e usar regex, mas não olhei muito de perto.
Você pode estar perdendo o objetivo do banco de dados nesta instância.Sua função é retornar os dados que satisfaçam as condições que você forneceu.Acho que você desejará implementar o destaque provavelmente usando regex em seu controle web.
Aqui está algo que uma pesquisa rápida revelaria.
http://www.dotnetjunkies.com/PrintContent.aspx?type=article&id=195E323C-78F3-4884-A5AA-3A1081AC3B35
Alguns detalhes:
search_kiemeles=replace(lcase(search),"""","")
do while not rs.eof 'The search result loop
hirdetes=rs("hirdetes")
data=RegExpValueA("([A-Za-zöüóőúéáűíÖÜÓŐÚÉÁŰÍ0-9]+)",search_kiemeles) 'Give back all the search words in an array, I need non-english characters also
For i=0 to Ubound(data,1)
hirdetes = RegExpReplace(hirdetes,"("&NoAccentRE(data(i))&")","<em>$1</em>")
Next
response.write hirdetes
rs.movenext
Loop
...
Funções
'All Match to Array
Function RegExpValueA(patrn, strng)
Dim regEx
Set regEx = New RegExp ' Create a regular expression.
regEx.IgnoreCase = True ' Set case insensitivity.
regEx.Global = True
Dim Match, Matches, RetStr
Dim data()
Dim count
count = 0
Redim data(-1) 'VBSCript Ubound array bug workaround
if isnull(strng) or strng="" then
RegExpValueA = data
exit function
end if
regEx.Pattern = patrn ' Set pattern.
Set Matches = regEx.Execute(strng) ' Execute search.
For Each Match in Matches ' Iterate Matches collection.
count = count + 1
Redim Preserve data(count-1)
data(count-1) = Match.Value
Next
set regEx = nothing
RegExpValueA = data
End Function
'Replace non-english chars
Function NoAccentRE(accent_string)
NoAccentRE=accent_string
NoAccentRE=Replace(NoAccentRE,"a","§")
NoAccentRE=Replace(NoAccentRE,"á","§")
NoAccentRE=Replace(NoAccentRE,"§","[aá]")
NoAccentRE=Replace(NoAccentRE,"e","§")
NoAccentRE=Replace(NoAccentRE,"é","§")
NoAccentRE=Replace(NoAccentRE,"§","[eé]")
NoAccentRE=Replace(NoAccentRE,"i","§")
NoAccentRE=Replace(NoAccentRE,"í","§")
NoAccentRE=Replace(NoAccentRE,"§","[ií]")
NoAccentRE=Replace(NoAccentRE,"o","§")
NoAccentRE=Replace(NoAccentRE,"ó","§")
NoAccentRE=Replace(NoAccentRE,"ö","§")
NoAccentRE=Replace(NoAccentRE,"ő","§")
NoAccentRE=Replace(NoAccentRE,"§","[oóöő]")
NoAccentRE=Replace(NoAccentRE,"u","§")
NoAccentRE=Replace(NoAccentRE,"ú","§")
NoAccentRE=Replace(NoAccentRE,"ü","§")
NoAccentRE=Replace(NoAccentRE,"ű","§")
NoAccentRE=Replace(NoAccentRE,"§","[uúüű]")
end function