Busquedas rapidas e inteligentes al estilo Google en ASP .NET

Saludos,

Esta vez les comentare como he conseguido gestionar un control de usuario web que me permita hacer busquedas inteligentes, eficientes y eficaces.

Siempre que desarrollo intento que el codigo sea reutilizable al maximo, es asi que he implementado en mis sitios web busquedas al estilo de Google, usando siempre el mismo control para todos los casos de busqueda que necesite, es decir, un solo control llamado SmartSearch me resulve todos los problemas de busqueda que mis sitios puedan necesitar.

Por ejemplo:

En un formulario de facturacion o ventas, es necesario darle a nuestro usuario la opcion de buscar un proveedor o un cliente, asi como los productos que posee en su inventario y que necesitara elegir para facturarlos.

Siendo este el problema, deberiamos establecer codigo fuente de busqueda de tales entes en nuestra base de datos, asi como presentarlos en una lista y luego permitir que el usuario seleccione el dato deseado. Para esto, en programacion siemple deberiamos codificar cada caso. Si, tedioso y demorado, y es por eso que decidi implementar un control de usuario que al llenar unos pocos parametros me ayude a resolver estas tediosas busquedas en tiempos record.

Entrando a tema:

1) La idea es usar un solo textbox para que el usuario ingrese el texto buscado, ya sea codigo de cliente o nombre del cliente o numero de identificacion, en fin. He visto muchos sistemas que usan varios textbox o radiobutton para que el usuario deba elegir si quiere buscar por codigo o por nombre o por id, en fin, esto me parece en lo personal, algo muy desatendido, es decir, al construir un software debemos tener presente siempre que el tiempo y esfuerzo de muestros usuarios es muy valioso como para ponerlos a decidir cosas que nuestro sistema deberia resolver con un poco de ingenio, ademas, eso hara que nuestro sistema y por ende nuestro trabajo sea bien visto por nuestros usuarios, por lo tanto, nos recomendaran con sus amigos y conocidos, lo que implica en incremento en nuestros negocios. Desde luego no podemos por este hecho, desatender los recursos de los equipos, es decir, memoria, procesamiento y tiempos de respuesta, en fin, son muchos parametros que hay que tener presentes para hacer software de calidad, siempre funcionales y faciles de usar, que esto tambien es muy importante, ya que si nuestros sistemas son facilies de usar, no deberemos hacer manuales de usuario enormes que tambien es una perdidad de tiempo para nosotros programadores y el tiempo de adiestramiento del usuario sera muy corto tambien.

Otra cosa terrible que he visto por ahi es que, si el usuario digito un valor a buscar, si este no es exactamente identico a alguno de los datos almacenados en nuestra base de datos, el sistema no presenta resultados. Esto me parece de lo mas down… La idea de esta busqueda propuesta tambien es la de que el usuario por ejemplo esta buscando un cliente de nombre Fabricio Leon, pues el pueda escribir algo asi como esto: bricio on, o, fab leo, o, leo fab, o como se le ocurra y nuestro sistema debera interpretar la busqueda y mostrar las posibles alternativas.

2) Posterior a la orden de busqueda debemos presentar un listado con las coincidencias que hemos encontrado para que el usuario pueda elegir, para esto usaremos mediante javascript un webservice que nos retorne una htmltable con el listado, de tal manera, que no necesitaremos hacer un submit o postback de la pagina entera para que busque y traiga y presente los resultados.

Entonces necesitamos implementar un javascript que consuma un WebService y un control de usuario web que contenga estos elementos.

Comencemos por el principio,

1.- Creamos el WebService:

Dentro del WebService implementaremos una private function llamada SeekNow que realice la busqueda:

Los parametros de entrada para la function son:

TextToSearch: Aqui se recibirá el texto que el usuario ingreso en nuestro textbox y que desea buscar, recuerdan: “fab leo”

Q_Select: Aqui va el select que nos permitira ejecutar la busqueda dentro de nuestra base de datos, desde luego, este select es el que nos permitira el dinamismo de buscar en una u otra tabla, vista o conjunto de tablas, aqui depende la inventiva.  Esta sentencia sql es la que nos dara dinamismo a nuestro control SmartSearch.

Q_Like:  Nos permitira definir por que columnas comparar o buscar el TextToSearch

TBResults: Sera nuestra datatable que retornaremos llena de los resultados encontrados.

Q_TimeOutQueryOnSeconds: Este nos permite definir un timeout de respuesta de nuestra base de datos.

La idea de esta funcion es la de ir ensamblando una consulta sql, por ejemplo, en TextToSearch viene: “fab leo”, en Q_Select viene “select * from clientes”, y en Q_Like viene: “CodigoCliente,NombresCliente,IDCliente”.  Entonces esta funcion lo que haria es ensamblar asi la consulta:

select * from clientes where (CodigoCliente like ‘%fab%’ and CodigoCliente like ‘%leo%’) or (NombresCliente like ‘%fab%’ and NombresCliente like ‘%leo%’) or (IDCliente like ‘%fab%’ and IDCliente like ‘%leo%’)

Por imaginarnos, las posibles respuestas serian: Fabricio Leon, Fabian Leonidas, Leonardo Fabio, en fin…

Ahora imaginen estamos buscando productos:

TextToSearch viene: “nit lano”, en Q_Select viene “select * from items”, y en Q_Like viene: “CodigoItem,DescripcionItem”.  Entonces esta funcion lo que haria es ensamblar asi la consulta:

select * from items where (CodigoItem like ‘%nit%’ and CodigoItem like ‘%lano%’) or (DescripcionItem like ‘%nit%’ and DescripcionItem like ‘%lano%’)

Por imaginarnos, las posibles respuestas serian: Monitor Plano, Aeroplano Nitro, enfin…

Ahora veamos el codigo:

    Private Function SeekNow(ByVal TextToSearch As String, ByVal Q_Select As String, ByVal Q_Like As String, Optional ByRef TBResults As Data.DataTable = Nothing, Optional ByVal Q_TimeOutQueryInSecconds As Integer = 30) As Data.DataTable
            Dim result As String = ""
            If Q_Like.IndexOf(":") >= 0 Then
                Q_Like = Q_Like.Substring(Q_Like.IndexOf(":") + 1)
            End If
            If Q_Like.IndexOf(";") >= 0 Then
                Q_Like = Q_Like.Substring(0, Q_Like.IndexOf(";"))
            End If
            If Q_Like.Trim.Length > 0 Then
                If Q_Like.IndexOf(",") < 0 Then
                    Q_Like &= ","
                End If
            End If
'usaremos el espacio en blanco para identificar o separar las palabras dentro del TextToSearch. En la siguiente linea quitaremos los dobles o triples espacios que el usuario pudo haber escrito por error y reemplazaremos por comas, por eje: "fab leo" quedaria asi "fab,leo""             Dim B As String = TextToSearch.Trim.Replace(" ", ",").Replace(",,", ",").Replace(",,", ",")
            Dim Campos As String = Q_Like  'Aqui la variable Campos guarda los campos o columnas de la tabla que compararemos, por ejemplo: "CodigoCliente,NombresCliente,IDCliente"
            If B.Trim.Length > 0 Then
                If B.IndexOf(",") < 0 Then 'Si no tenia espacios el TextToSearch entonces no tendra comas, asi que le adicionamos una por lo menos al final para poderla dividir sin que nos de error en el siguiente bucle
                    B &= ","
                End If
                For i As Integer = 0 To Campos.Split(",").Length - 1
                    If Campos.Split(",")(i).Trim.Length > 0 Then
                        If i > 0 Then ' Adicionamos un OR por cada palabra buscada y lo almacenamos en result siempre que no sea la primera pasada del bucle, por ejemplo quedaria algo asi: result ~ "CodigoCliente like '%fab%' or CodigoCliente like '%leo%'"  Recuerda, en mysql % significa cualquier caracter(es) adelante o detrás
                            result &= " or "
                        End If
                        For j As Integer = 0 To B.Split(",").Length - 1
                            If B.Split(",")(j).Trim.Length > 0 Then
                                If j = 0 Then ' Si estamos iniciando el ensamblaje de la busqueda ponemos un parentesis al inicio
                                    result &= " ("
                                End If
                                Try
                                    result &= " " & Campos.Split(",")(i) & " like '%" & B.Split(",")(j) & "%' "    'Recuerdas??   result ~ "CodigoCliente like '%fab%' or CodigoCliente like '%leo%'"
                                    If B.Split(",")(j + 1).Trim.Length Then
                                        result &= " and "
                                    End If
                                Catch ex As Exception
                                End Try
                                If j >= B.Split(",").Length - 1 Then
                                    result &= ") "
                                End If
                            Else
                                result &= ") "
                            End If
                        Next
                    End If
                Next
                Q_Like = result
            Else
                'Exit Function
            End If
' De encontrar una clausula Order By dentro del Q_Select, se reaoganiza la sentencia sql
            Dim OrderBy As String = ""
            If Q_Select.ToLower.IndexOf(" order by ") > 0 Then
                OrderBy = Q_Select.Substring(Q_Select.ToLower.IndexOf(" order by "))
                Q_Select = Q_Select.Substring(0, Q_Select.ToLower.IndexOf(" order by "))
            End If
' Igual manera si se encuentra una clausula Group By
            Dim GroupBy As String = ""
            If Q_Select.ToLower.IndexOf(" group by ") > 0 Then
                GroupBy = Q_Select.Substring(Q_Select.ToLower.IndexOf(" group by "))
                Q_Select = Q_Select.Substring(0, Q_Select.ToLower.IndexOf(" group by "))
            End If
' Igual con el Where, si ya existe se remmplaza la palabra Where con " Where y sus likes mas lo que ya tenia el Where de la sentencia sql"  Y si no existia Where se lo adjunta
            If Q_Select.ToLower.LastIndexOf(" where ") > 0 Then
                If Q_Like.ToLower.IndexOf(" like ") >= 0 Then
                    Q_Select = Q_Select.Insert(Q_Select.ToLower.IndexOf(" where ") + 6, " (" & Q_Like & ") and ")
                Else
                    Q_Select = Q_Select.Insert(Q_Select.ToLower.IndexOf(" where ") + 6, " ")
                End If
            Else
                If Q_Like.Trim.Length > 0 Then
                    If Q_Like.IndexOf("%") >= 0 Then
                        Q_Select = Q_Select & " where " & Q_Like
                    End If
                End If
            End If
' A continuacion ensamblo la sentencia sql y uso una funcion DataRecovery que me ejecuta la consulta y me devuelve el resultado en una datatable
            Return Lion.DataRecovery(Q_Select & GroupBy & OrderBy, TBResults, Q_TimeOutQueryInSecconds, , True, , False)
        Catch ex As Exception
        End Try
        Return Nothing
    End Function

La anterior es la función ’kernel’ o alma del proceso de búsqueda.

Como la funcion Seek nos devuelve una data table, la podriamos asignar a una GridView (GV1.datasource = seek(….)) y presentar sus resultados (GV1.databinding()) y listo, sin embargo, la idea propuesta es otra, y la desarrollamos a continuacion:

Vamos a implementar una nueva función que formatee nuestra tabla de resultados, dentro de una HtmlTable, y porque??, pues fácil, como la idea es llamar a este proceso de búsqueda desde una pagina web sin que esta incurra en un postback, entonces, podemos hacer un consumo de servicios via javascript, llamar a la función de búsqueda, y leer los resultados en forma de htmltable y publicar esta tabla en nuestra pagina.  Esto hará transparente el proceso de busqueda en nuestro website.

Implementemos el retorno del htmltable:

Una Respuesta a Busquedas rapidas e inteligentes al estilo Google en ASP .NET

  1. Luis Marcelo dijo:

    tendras el codigo del retorno del htmltable?

Deja un comentario

Fill in your details below or click an icon to log in:

Logo de WordPress.com

You are commenting using your WordPress.com account. Log Out / Cambiar )

Twitter picture

You are commenting using your Twitter account. Log Out / Cambiar )

Facebook photo

You are commenting using your Facebook account. Log Out / Cambiar )

Connecting to %s