¿Qué tan difícil es incorporar la búsqueda de texto completo con SQL Server?
-
03-07-2019 - |
Pregunta
Estoy creando una aplicación C # / ASP.NET con un servidor SQL. Estoy en la fecha límite y terminando mis páginas, fuera del campo izquierdo, uno de mis diseñadores incorporó una búsqueda de texto completo en una de mis páginas. Mis " búsquedas " hasta este punto, se han filtrado, pudiendo reducir un conjunto de resultados por ciertos factores y valores de columna.
Siendo que estoy en la fecha límite (sabes 3 horas de sueño por noche, en el punto en el que me veo como algo que el gato comió y vomitó), esperaba que esta página fuera muy similar a las otras y yo Estoy intentando decidir si apestar o no. Nunca he hecho una búsqueda de texto completo en una página antes ... ¿Es esta una montaña para escalar o hay una solución simple?
gracias.
Solución
En primer lugar, debe habilitar la indexación de búsqueda de texto completo en los servidores de producción, por lo que si eso no está dentro del alcance, no querrá continuar con esto.
Sin embargo, si eso ya está listo, la búsqueda de texto completo es relativamente simple.
T-SQL tiene 4 predicados utilizados para la búsqueda de texto completo:
- FREETEXT
- FREETEXTTABLE
- CONTIENE
- CONTAINSTABLE
FREETEXT es el más simple, y se puede hacer así:
SELECT UserName
FROM Tbl_Users
WHERE FREETEXT (UserName, 'bob' )
Results:
JimBob
Little Bobby Tables
FREETEXTTABLE funciona igual que FreeTEXT, excepto que devuelve los resultados como una tabla.
El verdadero poder de la búsqueda de texto completo de T-SQL proviene del predicado CONTAINS (y CONTAINSTABLE) ... Este es enorme, así que pegaré su uso en:
CONTAINS
( { column | * } , '< contains_search_condition >'
)
< contains_search_condition > ::=
{ < simple_term >
| < prefix_term >
| < generation_term >
| < proximity_term >
| < weighted_term >
}
| { ( < contains_search_condition > )
{ AND | AND NOT | OR } < contains_search_condition > [ ...n ]
}
< simple_term > ::=
word | " phrase "
< prefix term > ::=
{ "word * " | "phrase * " }
< generation_term > ::=
FORMSOF ( INFLECTIONAL , < simple_term > [ ,...n ] )
< proximity_term > ::=
{ < simple_term > | < prefix_term > }
{ { NEAR | ~ } { < simple_term > | < prefix_term > } } [ ...n ]
< weighted_term > ::=
ISABOUT
( { {
< simple_term >
| < prefix_term >
| < generation_term >
| < proximity_term >
}
[ WEIGHT ( weight_value ) ]
} [ ,...n ]
)
Esto significa que puede escribir consultas como:
SELECT UserName
FROM Tbl_Users
WHERE CONTAINS(UserName, '"little*" NEAR tables')
Results:
Little Bobby Tables
Buena suerte :)
Otros consejos
La búsqueda de texto completo en SQL Server es realmente fácil, un poco de configuración y un ligero ajuste en el lado de la consulta, ¡y listo! Lo he hecho para clientes en menos de 20 minutos antes, familiarizado con el proceso
Aquí está el artículo de MSDN 2008 , los enlaces salen a las versiones de 2005 a partir de ahí
He usado dtSearch antes para agregar búsquedas de texto completo a archivos y bases de datos, y sus cosas son bastante baratas y fáciles de usar.
A falta de agregar todo eso y configurar SQL, este script buscará en todas las columnas de una base de datos y le dirá qué columnas contienen los valores que está buscando. Sé que no es el "correcto" solución, pero puede comprarte algo de tiempo.
/*This script will find any text value in the database*/
/*Output will be directed to the Messages window. Don't forget to look there!!!*/
SET NOCOUNT ON
DECLARE @valuetosearchfor varchar(128), @objectOwner varchar(64)
SET @valuetosearchfor = '%staff%' --should be formatted as a like search
SET @objectOwner = 'dbo'
DECLARE @potentialcolumns TABLE (id int IDENTITY, sql varchar(4000))
INSERT INTO @potentialcolumns (sql)
SELECT
('if exists (select 1 from [' +
[tabs].[table_schema] + '].[' +
[tabs].[table_name] +
'] (NOLOCK) where [' +
[cols].[column_name] +
'] like ''' + @valuetosearchfor + ''' ) print ''SELECT * FROM [' +
[tabs].[table_schema] + '].[' +
[tabs].[table_name] +
'] (NOLOCK) WHERE [' +
[cols].[column_name] +
'] LIKE ''''' + @valuetosearchfor + '''''' +
'''') as 'sql'
FROM information_schema.columns cols
INNER JOIN information_schema.tables tabs
ON cols.TABLE_CATALOG = tabs.TABLE_CATALOG
AND cols.TABLE_SCHEMA = tabs.TABLE_SCHEMA
AND cols.TABLE_NAME = tabs.TABLE_NAME
WHERE cols.data_type IN ('char', 'varchar', 'nvchar', 'nvarchar','text','ntext')
AND tabs.table_schema = @objectOwner
AND tabs.TABLE_TYPE = 'BASE TABLE'
ORDER BY tabs.table_catalog, tabs.table_name, cols.ordinal_position
DECLARE @count int
SET @count = (SELECT MAX(id) FROM @potentialcolumns)
PRINT 'Found ' + CAST(@count as varchar) + ' potential columns.'
PRINT 'Beginning scan...'
PRINT ''
PRINT 'These columns contain the values being searched for...'
PRINT ''
DECLARE @iterator int, @sql varchar(4000)
SET @iterator = 1
WHILE @iterator <= (SELECT Max(id) FROM @potentialcolumns)
BEGIN
SET @sql = (SELECT [sql] FROM @potentialcolumns where [id] = @iterator)
IF (@sql IS NOT NULL) and (RTRIM(LTRIM(@sql)) <> '')
BEGIN
--SELECT @sql --use when checking sql output
EXEC (@sql)
END
SET @iterator = @iterator + 1
END
PRINT ''
PRINT 'Scan completed'
He estado allí. Funciona a las mil maravillas hasta que empiezas a considerar la escalabilidad y las funcionalidades de búsqueda avanzada, como buscar en varias columnas, dando a cada uno valores de peso diferentes.
Por ejemplo, la única forma de buscar en las columnas Título y Resumen es tener una columna calculada con SearchColumn = CONCAT (Título, Resumen)
e indexe sobre SearchColumn
. Ponderación? SearchColumn = CONCAT (CONCAT (Título, Título), Resumen)
algo así. ;) ¿Filtrado? Olvídate de eso.
" ¿Qué tan difícil es " Es una pregunta difícil de responder. Por ejemplo, alguien que ya lo ha hecho 10 veces probablemente considerará que es fácil. Todo lo que realmente puedo decir es que es probable que lo encuentre mucho más fácil si usa algo como NLucene en lugar de rodar el tuyo.