Comment mettre en évidence les résultats d'une requête de texte intégral SQL Server
-
09-06-2019 - |
Question
Nous avons une application Web qui utilise SQL Server 2008 comme base de données. Nos utilisateurs peuvent effectuer des recherches en texte intégral sur des colonnes particulières de la base de données. La fonctionnalité de texte intégral de SQL Server ne semble pas prendre en charge la mise en surbrillance des résultats. Avons-nous besoin de construire cela nous-mêmes ou existe-t-il une bibliothèque ou des connaissances sur la manière de procéder?
BTW l’application étant écrite en C #, une solution .Net serait idéale mais non nécessaire car nous pourrions la traduire.
La solution
En développant l’idée d’Ismaël, ce n’est pas la solution finale, mais je pense que c’est une bonne façon de commencer.
Tout d'abord, nous devons obtenir la liste des mots récupérés avec le moteur de recherche en texte intégral:
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'
On peut déjà développer beaucoup de choses, par exemple, le modèle de recherche est assez basique; de plus, il existe probablement de meilleurs moyens de filtrer les mots dont vous n’avez pas besoin, mais le moins que l’on puisse vous donner est une liste de mots clés, etc. auxquels une recherche en texte intégral pourrait correspondre.
Après avoir obtenu les résultats dont vous avez besoin, vous pouvez utiliser RegEx pour analyser l'ensemble de résultats (ou de préférence un sous-ensemble pour l'accélérer, bien que je n'ai pas encore trouvé le moyen de le faire). Pour cela, j'utilise simplement deux boucles while et un groupe de tables et de variables temporaires:
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
Plusieurs remarques:
1. Les boucles while imbriquées ne sont probablement pas le moyen le plus efficace de le faire, mais rien d'autre ne me vient à l'esprit. Si je devais utiliser des curseurs, ce serait essentiellement la même chose?
2. @FirstSearchWord
fait ici référence à la première instance dans le texte de l'un des mots de recherche d'origine. Le texte que vous remplacez ne figurera donc que dans le résumé. Encore une fois, c'est une méthode assez basique, une sorte d'algorithme de recherche de groupe de texte serait probablement pratique.
3. Pour obtenir RegEx en premier lieu, vous avez besoin des fonctions CLR définies par l'utilisateur.
Autres conseils
Il semble que vous puissiez analyser la sortie du nouveau SQL Server 2008 stocké Utilisez la procédure sys.dm_fts_parser et utilisez regex, mais je ne l'ai pas trop regardée.
Il se peut que vous manquiez le point de la base de données dans cette instance. Son travail consiste à vous renvoyer les données qui répondent aux conditions que vous lui avez données. Je pense que vous voudrez implémenter la surbrillance en utilisant probablement regex dans votre contrôle Web.
Voici quelque chose qu'une recherche rapide révélerait.
Quelques détails:
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
...
Fonctions
'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