WSS 3.0 & MOSS: Extendiendo las capacidades de búsqueda (III)!

Como toda saga debería tener su fin, con este post vamos a completar la serie de artículos sobre como es posible ir más allá de las capacidades de búsqueda out-of-the-box en plataforma SharePoint que iniciamos con las partes I y II. En este post haremos un recorrido por la tercera opción que nos ofrece SharePoint para extender las capacidades de búsqueda: los servicios web de búsqueda. Empecemos.

Query Web Service en SharePoint

Además de poder realizar consultas a través del modelo de objetos, la plataforma SharePoint permite realizar búsquedas a través del Query Web Service. Esta opción de búsqueda soporta las dos posibilidades vistas para realizar búsquedas mediante el modelo de objetos: SQL Syntax y Keyword Syntax. Entonces, ¿cuándo usamos el Query Object Model y cuando el Query Web Service? La respuesta es bastante sencilla. Usaremos la primera opción cuando la aplicación de búsquedas resida en el mismo servidor que está ejecutando WSS 3.0 o MOSS. Usaremos la segunda opción cuando se trate de aplicaciones remotas.

Los servicios web de búsqueda de WSS 3.0 & MOSS nos permite acceder a las funcionalidades de búsqueda desde aplicaciones cliente que estén fuera del contexto de sitios de SharePoint:

Los métodos web para WSS 3.0 y MOSS son los mismos, si bien, y como ya sabemos, las capacidades de búsqueda son mucho más potentes en MOSS que en WSS 3.0.

Uso del Query Web Service

Para probar esta funcionalidad, partiremos de la misma aplicación web que vimos en el post anterior, al que vamos a añadir al proyecto un nuevo ítem de tipo Web Form. Abrimos la página que acabamos de crear en modo de diseño y añadimos los siguientes elementos (ver Figura):

  • Un control de tipo Table de la sección HTML de la Toolbox.
  • Un control de tipo TextBox en el que configuramos la propiedad TextMode con la opción MultiLine.
  • Un control de tipo DrowDownList.
  • Un control de tipo Label en el que configuramos la propiedad Text con el texto Tipo de Consulta.
  • Un control de tipo TextBox en el que configuramos la propiedad TextMode con la opción MultiLine.
  • Un control de tipo Button en el que configuramos la propiedad Text con el texto Buscar.
  • Un control de tipo Label.
  • Un control de tipo GridView.

Como veis, el aspecto de la web es casi igual al de la realizada en el post previo…de hecho el copy & paste ha sido radical 😛 (cuestión de aprovechar tiempos).

image image

Una vez que hemos diseñado la página de búsquedas en la que habilitaremos SQL Syntax, nos vamos a la vista de código de la misma presionando F7. En esta vista de código vamos a añadir toda la lógica necesaria para poder probar la funcionalidad Query Web Service:

  • Lo primero que vamos a hacer es añadir la referencia al servicio web de búsqueda. Para ello, utilizamos la opción Add Web Reference… disponible cuando clic con el botón derecho del ratón sobre el nombre de la solución.
  • Hacemos clic sobre la opción Web services on the local machine.
  • Buscamos el servicio web de búsquedas de MOSS. Como hemos comentado, la url característica de este servicio web es: http://Nombre_Servidor/[Sites/][Site_Name/]_vti_bin/search.asmx.
  • Hacemos clic sobre el servicio web search y en la siguiente pantalla le damos a la referencia web el nombre de QueryWebServiceProxy.
  • Pulsamos Add Reference y ya tentemos añadido el servicio web de búsqueda de MOSS a nuestro proyecto.
image image image

Una vez que hemos añadido la referencia web al servicio de búsqueda de MOSS, ya podemos comenzar a añadirle la lógica necesaria a la página ASP.NET:

  • Añadimos las siguientes constantes a la clase parcial asociada a la página:

    #region Variables & Constates

    //Constantes valores combo

    const string ID_QUERY = “ID_Query”;

    const string NOMBRE_QUERY = “Query”;

    public const string TIPO_QUERY0_ID = “Q0”;

    public const string TIPO_QUERY0_TEXT = “Seleccione un tipo de query…”;

    public const string TIPO_QUERY1_ID = “Q1”;

    public const string TIPO_QUERY1_TEXT = “Keyword Query”;

    public const string TIPO_QUERY2_ID = “Q2”;

    public const string TIPO_QUERY2_TEXT = “Búsqueda por Scope”;

    public const string TIPO_QUERY3_ID = “Q3”;

    public const string TIPO_QUERY3_TEXT = “Búsqueda por Scope y Campo”;

    public const string TIPO_QUERY4_ID = “Q4”;

    public const string TIPO_QUERY4_TEXT = “Búsqueda con LIKE”;

    public const string TIPO_QUERY5_ID = “Q5”;

    public const string TIPO_QUERY5_TEXT = “Búsqueda con CONTAINS y *”;

    public const string TIPO_QUERY6_ID = “Q6”;

    public const string TIPO_QUERY6_TEXT = “Búsqueda con FreeText”;

    public const string TIPO_QUERY7_ID = “Q7”;

    public const string TIPO_QUERY7_TEXT = “Búsqueda con FreeText y Op. Exclusión”;

    //Constantes para las queries

    public const string QUERY1 = “Introduzca la palabra clave a buscar…”;

    public const string XML_TAG_START1 = “<QueryPacket xmlns=’urn:Microsoft.Search.Query’>”

            + “<Query><SupportedFormats><Format revision=’1′>”

            + “urn:Microsoft.Search.Response.Document:Document</Format>”

            + “</SupportedFormats><Context><QueryText language=’en-US’ type=’STRING’>”;

    public const string XML_TAG_START2 = “<QueryPacket xmlns=’urn:Microsoft.Search.Query’>”

                + “<Query><SupportedFormats><Format revision=’1′>”

                + “urn:Microsoft.Search.Response.Document:Document</Format>”

                + “</SupportedFormats><Context><QueryText language=’en-US’ type=’MSSQLFT’>”;

    public const string XML_TAG_END = “</QueryText></Context></Query></QueryPacket>”;

    public const string QUERY2 = “SELECT Rank, Title, Author, Size, Path, Description” +

                            ” FROM SCOPE() WHERE “scope”=’All Sites'”;

    public const string QUERY3 = “SELECT Rank, Title, Author, Size, Path, Description” +

                            ” FROM SCOPE() WHERE “scope”=’All Sites'” +

                            ” AND  Author=’LitwareInc Administrator’ ORDER BY Rank DESC”;

    public const string QUERY4 = “SELECT Rank, Title, Author, Size, Path, Description” +

                            ” FROM SCOPE() WHERE “scope”=’All Sites'” +

                            ” AND TITLE LIKE ‘Bla%’ ORDER BY Rank DESC”;

    public const string QUERY5 = “SELECT Rank, Title, Author, Size, Path, Description” +

                            ” FROM SCOPE() WHERE CONTAINS (‘”Bla*”‘)”;

    public const string QUERY6 = “SELECT Rank, Title, Author, Size, Path, Description” +

                            ” FROM SCOPE() WHERE “scope”=’All Sites'” +

                            “AND FreeText (‘Bla%’) ORDER BY Rank DESC”;

    public const string QUERY7 = “SELECT Rank, Title, Author, Size, Path, Description” +

                            ” FROM SCOPE() WHERE “scope”=’All Sites'” +

                            “AND FreeText (‘Bla% -CRM’) ORDER BY Rank DESC”;

    #endregion

Como vemos, estas constantes nos van a permitir realizar varios tipos de consultas mediante utilizando el servicio web de búsqueda. Las consultas son las mismas vistas con SQL Syntax y Keyword Syntax, pero con la particularidad de que las envolveremos con unas etiquetas XML particulares:

  • Consultas utilizando una palabra clave.
  • Consultas a un determinado scope (en este caso All Sites).
  • Consultas a un scope y filtrando por un cierto campo (Author).
  • Consultas utilizando la cláusula LIKE que permite extender las búsquedas en SharePoint al habilitar el uso de operadores comodín (%).
  • Consultas utilizando el predicado CONTAINS(), que permite buscar el término de búsqueda utilizando caracteres comodín (*) y utilizando proximidad. Con este operador se utiliza habitualmente el comodín *.
  • Consultas utilizando el predicado FreeText(), que permite buscar el término de búsqueda dentro de los documentos.

Además, en el código anterior también podemos observar lo siguiente:

  • Hay dos tipos de etiquetas XML que envuelven estos tipos de consultas. Por un lado tenemos:

    public const string XML_TAG_START1 = “<QueryPacket xmlns=’urn:Microsoft.Search.Query’>”

            + “<Query><SupportedFormats><Format revision=’1′>”

            + “urn:Microsoft.Search.Response.Document:Document</Format>”

            + “</SupportedFormats><Context><QueryText language=’en-US’ type=’STRING’>”;

Esta etiqueta lo que indica es que la consulta a ejecutar es de tipo STRING y es el el propio componente de búsqueda de MOSS el que se encarga de construir la query a partir de la palabra clave especificada.

  • Por otro lado, nos encontramos con:

    public const string XML_TAG_START2 = “<QueryPacket xmlns=’urn:Microsoft.Search.Query’>”

                + “<Query><SupportedFormats><Format revision=’1′>”

                + “urn:Microsoft.Search.Response.Document:Document</Format>”

                + “</SupportedFormats><Context><QueryText language=’en-US’ type=’MSSQLFT’>”;

Esta etiqueta indica que la consulta a ejecutar es de tipo MSSQLFT, es decir, es la lógica de nuestra aplicación la que se encarga de construir la consulta parametrizada y pasársela al componente de búsqueda de SharePoint.

  • Los resultados se ordenan utilizando la cláusula ODER BY Rank DESC, es decir, los resultados se devuelven ordenador de forma descendente por la propiedad Rank que indica el nivel de raking de un cierto resultado devuelto por el motor de búsquedas de SharePoint (MOSS en este caso).
  • En alguna de las consultas se utiliza el operador de exclusión -, que nos permite especificar que se excluyan aquellos resultados que contengan el término que aparece a continuación del operador.

En el evento Page_Load() de la página ASP.NET añadimos el siguiente código:

   protected void Page_Load(object sender, EventArgs e)

    {

        if (!IsPostBack)

        { 

            this.Label1.Text = “”;

            this.Button.Enabled = false;

            //Filling the combo

            DataTable dtlQueries = new DataTable();

            dtlQueries.Columns.Add(ID_QUERY);

            dtlQueries.Columns.Add(NOMBRE_QUERY);

            Random rdIDProducto = new Random();

            DataRow dtrFila;

            //Default Data

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY0_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY0_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //First Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY1_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY1_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Second Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY2_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY2_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Third Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY3_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY3_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Fourth Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY4_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY4_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Fith Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY5_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY5_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Sixth Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY6_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY6_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Seventh Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY7_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY7_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Adding the data to the dropdownlist

            this.DropDownList1.DataSource = dtlQueries;

            this.DropDownList1.DataTextField = NOMBRE_QUERY;

            this.DropDownList1.DataValueField = ID_QUERY;

            this.DropDownList1.DataBind();

            this.DropDownList1.AutoPostBack = true;

            this.DropDownList1.SelectedItem.Value = TIPO_QUERY0_ID;

        }

    }

Como vemos, el código anterior nos permite configurar en la primera carga de la página los parámetros iniciales de los controles de la misma. En particular, estamos realizando las siguientes tareas:

  • Rellenar el control DropDownList con los distintos tipos de consultas que vamos a realizar.
  • Configuramos la propiedad AutoPostBack a true para que la página se enteré de que el usuario ha seleccionado una cierta opción y se llame al correspondiente manejador.
  • Especificar el valor del DropDownList que se muestra por defecto.

Codificamos el manejador del evento SelectedIndexChanged del control DropDownList añadiéndolo directamente en la vista de código de la página o bien haciendo doble clic sobre dicho control desde la vista de diseño de la página:

  protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)

    {

        if (this.DropDownList1.SelectedItem.Value != “0”)

        {

            this.Label1.Text = “”;

            string sOpcionQuery = this.DropDownList1.SelectedItem.Value;

            switch (sOpcionQuery)

            {

                case TIPO_QUERY1_ID:                                                             

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY1;

                    break;

                case TIPO_QUERY2_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY2;

                    break;

                case TIPO_QUERY3_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY3;

                    break;

                case TIPO_QUERY4_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY4;

                    break;

                case TIPO_QUERY5_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY5;

                    break;

                case TIPO_QUERY6_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY6;

                    break;

                case TIPO_QUERY7_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY7;

                    break;

                default:

                    this.Label1.Text = “”;

                    this.Button.Enabled = false;

                    break;

            }

        }

        else

        {

            this.Label1.Text = “”;

            this.Button.Enabled = false;

        } 

    }

Con el código anterior, estamos pintando en el control TextBox de la página la consulta, seleccionada por el usuario.

Finalmente, codificamos el manejador del evento clic del control Button utilizando una de las dos opciones comentadas para el manejador SelectedIndexChanged del control DropDownList:

   protected void Button1_Click(object sender, EventArgs e)

    {

        try

        {

            int totalNumRecords=0;

            string keywordString = this.TextBox1.Text;

            string qXMLString;

            DataSet queryResults=new DataSet();

            QueryWebServiceProxy.QueryService queryService =

               new QueryWebServiceProxy.QueryService();

            queryService.Credentials = System.Net.CredentialCache.DefaultCredentials;

            if (this.DropDownList1.SelectedItem.Value==TIPO_QUERY1_ID)

            {

               qXMLString = XML_TAG_START1 + keywordString + XML_TAG_END;                

            }

            else

            {

                qXMLString = XML_TAG_START2 + keywordString + XML_TAG_END;         

            } 

            queryResults = queryService.QueryEx(qXMLString);

            totalNumRecords = queryResults.Tables[0].Rows.Count;         

            if (totalNumRecords > 0)

            {

                this.Label1.Text = “Número de coincidencias ” + totalNumRecords.ToString();

                this.GridView1.DataSource = queryResults.Tables[0];

                this.GridView1.DataBind();

            }

            else

            {

                this.Label1.Text = “No hay resultados para esta búsqueda”;

                this.GridView1.DataSource = null;

                this.GridView1.DataBind();

            }          

        }

        catch (Exception ex)

        {

            this.Label1.Text = ex.Message;

        }

    }

Como vemos en el listado del código (en negrita las partes clave), los pasos necesarios para realizar consultas utilizando el servicio web de búsqueda son los siguientes:

  • Definimos un objeto de tipo QueryService() a partir de la referencia creada al servicio web y que nos permitirá realizar los dos tipos de búsquedas comentados.
  • Especificamos las credenciales de acceso al servicio web de búsqueda (ponemos las credenciales por defecto).
  • En función de la opción seleccionada por el usuario, búsqueda por palabra clave o mediante una consulta SQL Syntax, parametrizamos de forma adecuada la consulta que tiene que ejecutar el servicio web de búsqueda de MOSS:
    • En el caso de Keyword Syntax, es el propio componente de búsqueda de MOSS el que se encarga de construir la query a partir de la palabra clave especificada.
    • En el caso de SQL Syntax, es la lógica de nuestra aplicación la que se encarga de construir la consulta parametrizada y pasársela al componente de búsqueda de SharePoint.

En cualquiera de las dos situaciones, la consulta se ejecuta mediante el método QueryEx() y el resultado devuelto (una colección de registros que cumplen las condiciones de búsqueda definidas) lo almacenamos en un objeto de tipo DatSet.

  • Sin más, comprobamos que hay resultados en el tabla con índice 0 del Dataset y los renderizamos en el control GridView e la página.

Probando la solución

Una vez diseñada la aplicación de búsquedas preparada para definir búsquedas el servicio web de búsquedas de MOSS, así como implementada la lógica de búsquedas, vamos a probar dos de las situaciones modelada:

  • Búsqueda mediante palabra clave:
    • Escogemos la opción Keyword query.
  • Búsqueda mediante SQL Syntax:
  • o Seleccionamos la opción Búsqueda con FreeText y Op. de Exclusión.

image image

Y aquí concluye esta serie de post sobre extensibilidad de las capacidades de búsqueda en plataforma SharePoint. Espero que el tema os haya resultado interesante.

Publicado por

Juan Carlos González

Juan Carlos es Ingeniero de Telecomunicaciones por la Universidad de Valladolid y Diplomado en Ciencias Empresariales por la Universidad Oberta de Catalunya (UOC). Cuenta con más de 12 años de experiencia en tecnologías y plataformas de Microsoft diversas (SQL Server, Visual Studio, .NET Framework, etc.), aunque su trabajo diario gira en torno a SharePoint & Office 365. Juan Carlos es MVP de Office Servers & Services desde 2015 (anteriormente fue reconocido por Microsoft como MVP de Office 365 y MVP de SharePoint Server desde 2008 hasta 2015), coordinador del grupo de usuarios .NET de Cantabria (Nuberos.Net, www.nuberos.es), co-fundador y coordinador del Grupo de Usuarios de SharePoint de España (SUGES, www.suges.es), así como co-director de la revista gratuita en castellano sobre SharePoint CompartiMOSS (www.compartimoss.com). Hasta la fecha, ha publicado 8 libros sobre SharePoint & Office 365 y varios artículos en castellano y en inglés sobre ambas plataformas.

2 comentarios en “WSS 3.0 & MOSS: Extendiendo las capacidades de búsqueda (III)!”

  1. Felicidades, por las tres entregas de capacidades de búsqueda, me han servidor de referencia. Solo me queda una duda que cambios o que problemas podria tener entre una implementacion de este servicio en un Sharepoint 2003 y un 2007

  2. Buenas fcorrea,
    Pues, aunque no lo he probado en SharePoint 2003 permíteme dudarlo porque aunque los servicios web se puedan llamar, en MOSS si que se ha cambiado y mejorado notablemente el motor de búsquedas por lo que seguramente estos artículos no se apliquen de manera exacta.

    Un saludo

    JC’s

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *