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

Siguiendo con la serie de post, cuya primera parte podéis encontrar aquí,  acerca de las posibilidades que brinda la plataforma SharePoint para extender la funcionalidad de búsqueda que proporciona a la caja, en este segundo post vamos a ver como podemos definir aplicaciones de búsqueda más granulares que permitan el uso de caracteres comodín como * o %. Empecemos.

Query Syntax y Keyword Syntax en SharePoint

Las búsquedas en plataforma SharePoint proporcionan un nuevo modelo de objetos que se puede utilizar en web parts o aplicaciones de búsqueda personalizada para ejecutar consultas contra el servicio de búsqueda. Todo el modelo de objetos de búsqueda de la plataforma SharePoint se encuentra implementado en dos espacios de nombres clave:

  • Microsoft.SharePoint.Search.Query para Windows SharePoint Services 3.0 (WSS 3.0).
  • Microsoft.Office.Server.Search para Microsoft Office SharePoint Server (MOSS).

Este modelo de objetos habilita dos posibilidades de búsqueda en plataforma SharePoint:

  • SQL Syntax, que proporciona el soporte necesario para construir consultas complejas similares a las consultas T-SQL que enviamos a SQL Server en aplicaciones con acceso a datos. De hecho, esta modalidad es la que nos permite definir consultas en las que utilicemos caracteres comodín como * o %, operadores como <,>, AND, OR, cláusulas como SELECT, WHERE, ORDER BY, LIKE o predicados como CONTAINS() y FREETEXT (). Lógicamente, SQL Syntax es la opción que habilita poder definir escenarios de búsquedas más flexibles.
  • Keyword Syntax, que permite realizar búsquedas por palabra clave. Esta opción devuelve aquellos resultados que contienen de manera exacta el término, frase o prefijo a buscar.

Implementando Query Syntax y SQL Syntax

Para probar esta funcionalidad, vamos a crear una aplicación ASP.NET que iremos extendiendo para probar las distintas alternativas de búsqueda comentadas. Abrimos Visual Studio 2005 / 2008 y a través de menú File -> New Web Site creamos una nueva aplicación web a la que denominaremos Extending_SP_Searches. Lo primero que tenemos que hacer es referenciar el ensamblado adecuado:

  • Microsoft.SharePoint.Search.dll y Microsoft.SharePoint.dll para WSS 3.0.
  • Microsoft.Office.Server.Search.dll y Microsoft.SharePoint.Server.dll para MOSS.

En nuestro caso, vamos a añadir los ensamblados necesarios para extender las búsquedas de MOOS. Para ello, hacemos clic en el nombre del proyecto y a través de la opción Add refence añadimos las referencias a los ensamblados de MOSS comentados. Estos ensamblados se encuentran en el siguiente path del servidor:

    C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI

Dentro del ensamblado Microsoft.Office.Server.Search, tenemos el espacio de nombres Microsoft.SharePoint.Search.Query que incluye tres clases:

  • Query, que no está pensada para uso directo, sino que es la implementación base de las clases de búsqueda dentro del Search Query Object Model.
  • FullTextSqlQuery, que permite ejecutar consultas de búsqueda utilizando SQL Syntax. Es esta clase la que soporta los elementos que nos permitirán construir consultas al estilo SQL: FREETEXT(), CONTAINS(), LIKE, ORDER BY, etc.
  • KeywordQuery, que permite ejecutar consultas de búsqueda utilizando Keyword Syntax.

A la hora de utilizar una clase u otra, tenemos que considerar cuál es el nivel de complejidad de las consultas de búsqueda que necesitamos implementar:

  • Para búsquedas por palabra clave, usaremos la clase KeywordQuery, que nos permite pasar directamente el término a buscar al componente de búsqueda. De esta forma, nos evitamos el parseo a través de los términos de búsqueda para construir la consulta.
  • Para búsquedas más complejas, usaremos la clase FullTextSqlQuery, que supera las limitaciones de la opción Keyword Syntax en cuanto a que sólo devuelve aquellos resultados que contengan de manera exacta el término de búsqueda especificado. Además, SQL Syntax permite construir agrupaciones complejas y soporta elementos de consulta adicionales que no son soportados por la opción Keyword Syntax.

Una vez añadidas las referencias, añadimos al proyecto un nuevo ítem de tipo Web Form y le damos el nombre de SQL Syntax:

image image  

imageAbrimos 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.

El aspecto en vista de diseño de la web debería ser el siguiente:

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 SQL Syntax fuera del entorno de la plataforma SharePoint:

  • Lo primero que vamos a hacer es añadir las correspondientes sentencias using para poder utilizar los espacios de nombres clave para acceder a las clases de búsquedas de MOSS:

using Microsoft.Office.Server;

using Microsoft.Office.Server.Search;

using Microsoft.Office.Server.Search.Query;

using Microsoft.SharePoint;

  • Añadimos las siguientes constantes a la clase parcial asociada a la página (os marco en negrita las constantes en las que he reflejado las consultas que incluye la página ASP.NET):

    #region Variables & Constates

    //Variables y constantes necesarias   

    public const int DefaultRowLimit = 10;

    public const int startRecordNum = 1;

    //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 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 SQL Syntax:

  • Consultas utilizando una palabra clave.
  • Consultas a un determinado scope o ámbito (en este caso All Sites).
  • Consultas a un scope y filtrando por un cierto campo (Author).
  • Consultas utilizando la clausula 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:

  • 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:

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);         

            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 iníciales 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 la control TextBox de la página la consulta, utilizando SQL Syntax o Keyword Syntax, seleccionada por el usuario.

  • Codificamos el manejador del evento clic del control Button utilizando una de las dos opciones comentadas, SQL Syntaxt o Keyword Syntax, para el manejador SelectedIndexChanged del control DropDownList (os marco de nuevo en negrita las partes clave del código):

      protected void Button1_Click(object sender, EventArgs e)

    {

        try

        {

            this.Label1.Text = “”;           

            int endRecordNum, totalNumRecords;

            SPSite spsSitio =

                new SPSite(http://litwaredemo:12000/sites/Intranet/default.aspx);

            //Keyword syntax

            KeywordQuery qRequest = new KeywordQuery(spsSitio);

            //SQL Syntax

            FullTextSqlQuery searchQuery = new FullTextSqlQuery(spsSitio);

            ResultTableCollection searchResultTableCollection;

            if (this.DropDownList1.SelectedItem.Value

                    == TIPO_QUERY1_ID)

            {

                qRequest.EnableStemming = true;

                qRequest.KeywordInclusion = KeywordInclusion.AllKeywords;

                qRequest.ResultTypes = ResultType.RelevantResults;

                qRequest.TrimDuplicates = true;

                qRequest.QueryText = this.TextBox1.Text;

                qRequest.StartRow = startRecordNum;

                qRequest.RowLimit = DefaultRowLimit;

                searchResultTableCollection = qRequest.Execute();

            }

            else

            {

                 searchQuery.EnableStemming = true;

                searchQuery.KeywordInclusion = KeywordInclusion.AllKeywords;

                searchQuery.ResultTypes = ResultType.RelevantResults;

                searchQuery.TrimDuplicates = true;

                searchQuery.QueryText = this.TextBox1.Text;

                searchQuery.StartRow = startRecordNum;

                searchQuery.RowLimit = DefaultRowLimit; 

                searchResultTableCollection = searchQuery.Execute();

            }        

            ResultTable searchResultTable = null;

            if ((int)ResultType.RelevantResults != 0)

            {

                searchResultTable =

                    searchResultTableCollection[ResultType.RelevantResults];

            } 

            totalNumRecords = searchResultTable.TotalRows;

            endRecordNum = startRecordNum + searchResultTable.RowCount – 1;

            if (totalNumRecords > 0)

            {

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

                DataTable resultDataTable = new DataTable();

                resultDataTable.TableName = “SearchResultTable”;

                DataSet resultDataSet = new DataSet(“SearchResultDataSet”);

                resultDataSet.Tables.Add(resultDataTable);

                resultDataSet.Load(searchResultTable,

                    LoadOption.OverwriteChanges, resultDataTable);

                this.GridView1.DataSource = resultDataSet;

                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, los pasos necesarios para realizar consultas utilizando SQL Syntax o Keyword Syntax son los siguientes:

  • Definir un objeto de tipo SPSite en el que especificaremos el sitio de SharePoint dónde vamos a realizar búsquedas.
  • Definimos un objeto de tipo KeywordQuery para poder realizar búsquedas por palabra clave en el sitio indicado.
  • Definimos un objeto de tipo FullTextSqlQuery para poder realizar consultas en el sitio indicado utilizando SQL Syntax.
  • 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 el objeto correspondiente y ejecutamos la consulta:
    • 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 Execute() y el resultado devuelto (una colección de registros que cumplen las condiciones de búsqueda definidas) lo almacenamos en un objeto de tipo ResultTableCollection.

  • A continuación, comprobamos si la búsqueda devuelve resultados considerados por el motor de búsqueda como relevantes de acuerdo a los criterios de búsqueda. Si es así, almacenamos estos resultados relevantes en un objeto de tipo ResultTable.
  • Sin más, comprobamos que hay resultados en el objeto de tipo ResultTable y los renderizamos en el control GridView e la página.

Probando la solución de búsqueda con SQL Syntax y Keyword Syntax

Una vez diseñada la aplicación de búsquedas preparada para definir búsquedas con SQL Syntax y Keyword Syntax, 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:
    • Seleccionamos la opción Búsqueda con FreeText y Op. de Exclusión.
image image

Como conclusión, el modelo de objetos de búsqueda de WSS 3.0 & MOSS habilita la definición de escenarios de búsqueda más flexibles y completo que la funcionalidad que viene a la caja en ambos entornos. Particularmente flexible es la funcionalidad SQL Syntax. Espero que el post os haya resultado interesante. En la próxima entrega (la última de la serie) veremos como usar el servicio web de búsqueda de SharePoint.

S + S: Microsoft Dynamics CRM Online!

Aunque ya hace casi un mes del anuncio, no quería dejar pasar por alto el hecho de que en línea con la estrategia Software + Services (S+S) de Microsoft, el pasado 22 de abril realizó el lanzamiento oficial de Microsoft Dynamics CRM Online. De momento, esta versión de Microsoft Dynamics CRM sólo estará disponible en Norteamerica. Más información sobre Microsoft Dynamics CRM Online:

Visual Studio 2008 SP1 Beta: Novedades en LINQ To SQL!

El otro día Unai nos comentaba algunos de las principales novedades que aparecen en ADO.NET Entity Framework con la beta1 del SP1 de Visual Studio 2008 y .NET Framework 3.5. Pues bien, tal y como nos comentan en el blog del equipo de ADO.NET, no sólo tenemos novedades en ADO.NET Entity Framework, sino que también las habrá en LINQ To SQL (ya nos la habían anunciado previamente). En concreto, la lista de novedades y mejoras es la siguiente:

  • Se han solucionado numerosos bugs reportados.
  • Mejor traducción de consultas en el caso de comparar columnas nullable en Visual Basic.
  • Soporte a nivel de diseñador y runtime para SQL Server 2008:
    • Soporte de conexión a bases de datos SQL Server 2008 en el server explorer.
    • Soporte de drag & drop de tablas de bases de datos de SQL Server 2008.
    • Soporte para algunos de los nuevos tipos de datos que aparecen con SQL Server 2008: Date, Time, DateTime2, DateTimeOffset y Filestream.

…en resumen, creo que como a muchos, me tocará rehacer y testear parte de los ejemplos y demos que tengo tanto de LINQ To SQL como de ADO.NET Entity Framework para evitar sorpresas si uso la beta 1 del SP1 de Visual Studio 2008 y .NET Framework.

image

image

image

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

Sin duda, la funcionalidad de búsquedas es uno de los atractivos más potentes de la plataforma SharePoint. Dicha funcionalidad está presente tanto en Windows SharePoint Serives 3.0 (WSS 3.0) como en Microsoft Office SharePoint Server 2007 (MOSS 2007), si bien es en este último dónde se pueden definir un mayor número de escenarios y dónde la plataforma es más flexible y permite modelar más situaciones. Aunque por las características de búsqueda Out-the-box de WSS 3.0 & MOSS son aplicables en un gran numero de situaciones, existen otras formas de explotar el motor de búsquedas de la plataforma que habilitan un número todavía mayor de escenarios:

  • Definir aplicaciones de búsqueda fuera del entorno de WSS 3.0 & MOSS.
  • Definir aplicaciones de búsqueda más granulares que permitan el uso de caracteres comodín como * o %.
  • Hacer uso de los servicios web de búsqueda para tener una mayor interoperabilidad.

La idea de este post y de los siguientes siguientes es evaluar las posibilidades de extensión de las búsquedas en plataforma SharePoint. En este post, veremos como crear aplicaciones de búsqueda fuera de la plataforma SharePoint mediante el concepto de Url Syntax. Empecemos.

Extendiendo las búsquedas en SharePoint mediante Url Syntax

Mediante Url Syntax  podemos ejecutar consultas contra el motor de búsquedas de WSS 3.0 o de MOSS codificando la consulta en una url parametrizada. Esta técnica está pensada para extender las búsquedas en plataforma SharePoint en los siguientes casos:

  • Visualizar los resultados en una aplicación de búsqueda personalizada.
  • Crear nuestros propios controles de búsqueda.
  • Implementar escenarios de búsquedas “salvadas”.

La clave de la técnica Url Syntax se basa en el uso de una serie de parámetros que vamos a codificar en la url de búsquedas. Esta url varía en función de si estamos hablando de sitios de WSS 3.0 , y que utilicen una plantilla típica como es la de Team Site, o bien de sitios de MOSS, y que utilicen una plantilla típica como es la de publishing (con la feature de búsquedas activada). Así, dependiendo del caso tendremos dos posibles url a parametrizar:

  • Para sitios de WSS 3.0 tendremos una url de la forma:

http://litwaredemo:12000/sites/Intranet/_layouts/OSSSearchResults.aspx?k=Bla&s=All%20Sites&v=date&start=1

  • Para sitios más específicos de MOSS (Nota: Lógicamente, la restricción que hemos visto para los sitios de WSS 3.0 también se aplica a los sitios de MOSS que usen plantillas propias de WSS 3.) la url serían de la formas:

http://litwaredemo:12000/sites/Intranet/results.aspx?k=Bla&s=All%20Sites&v=date&start=1

Como vemos, en el primer de los casos la url de búsqueda con los resultados reside en el directorio /_layouts de SharePoint, mientras que en el segundo caso no. Una vez que conocemos las posibles Url que podemos codificar, necesitamos conocer el significado y uso de los parámetros que se utilizan en dicha codificación. La siguiente tabla recoge los parámetros utilizados en la codificación:

Parámetro

Descripción

Ejemplo

k

Especifica la palabra o palabras claves a buscar. Sólo se puede especificar un valor para este parámetro

results.aspx?k=sharepoint%20search

s

Especifica el scope de búsqueda. Este parámetro puede contener múltiples valores separados por %2c.

results.aspx?s=SDKConent

results.aspx?s=SDKContent%2cTechnicalArticles

v

Especifica el tipo de vista que se utiliza para los resultados. Se permiten dos posibilidades:

·   relevance, los resultados se ordenan por relevancia.

·   date, los resultados se ordenan por fecha

results.aspx?v=date

results.aspx?v=relevance

start

Especifica que página concreta de resultados se va a mostrar.

results.aspx?start=3

Una vez que tenemos claro que parámetros podemos utilizar para realizar la codificación de la url que nos permitirá ejecutar una búsqueda en SharePoint, vamos a llevarlo a la práctica.

Creación de la aplicación de búsquedas

Lo primero que vamos a hacer es crear un nuevo proyecto de C# de tipo Windows Forms. Una vez creado el proyecto, se abrirá la típica superficie de diseño que nos permitirá modelar nuestra aplicación de búsquedas personalizada utilizando Url Syntax. A continuación desde la Toolbox de Visual Studio (2005, con 2008 sería exactamente lo mismo) arrastramos los siguientes controles:

  • Un control de tipo WebBrowser dónde mostremos los resultados obtenidos a partir de codificar la url de resultados de búsqueda.
  • Tres controles de tipo text box que utilizaremos para especificar respectivamente la palabra clave (keyword), el ámbito de búsqueda (scope) y la página concreta de resultados a mostrar.
  • Un control de tipo combobox que permitirá al usuario seleccionar como quiere que se muestren los datos:
    • Ordenados por fecha.
    • Ordenados por relevancia.
image image

El aspecto final del formulario en modo diseño debería ser el siguiente:

image

Añadiendo lógica a la aplicación

Una vez diseñada nuestra aplicación de búsquedas, vamos a añadirle la lógica necesaria para que permita realizar las búsquedas a partir de codificar la url de búsquedas mediante los parámetros comenzados:

  • En primer lugar, necesitamos especificar la url de la página de resultados. Como comentamos más arriba, esta tiene dos formas diferentes en función de si hablamos de un sitio propio de WSS 3.0 o de uno de MOSS. En nuestro caso, se trata de un sitio que utiliza una plantilla propia de WSS 3.0.
  • A partir de esta url, y teniendo en cuenta el diseño del formulario, no tenemos más que añadir la lógica al mismo para conseguir codificar la url de resultados. Por ejemplo, podríamos construir una url para realizar búsquedas por palabra clave, scope, tipo de vista y página de inicio de los resultados (por supuesto, se podría buscar únicamente utilizando uno de estos parámetros).
  • Una vez construida la url codificada, no tenemos más que utilizarla para definir un objeto de tipo Uri que nos permitirá configurar la propiedad Url del control WebBrowser.
  • El código completo que permite implementar esta búsqueda es el siguiente (os subrayo las pates clave del mismo):

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

 

//Espacio de nombres necesarios

using System.Data;

 

 

namespace URL_Syntax

{

    public partial class Form1 : Form

    {

        public const string SEARCH_URL=”http://litwaredemo:12000/sites/Intranet/_layouts/OSSSearchResults.aspx&#8221;;

        public const string QUESTION_MARK_SYMBOL = “?”;

        public const string EQUAL_SYMBOL = “=”;

        public const string AMPERSAND_SYMBOL = “&”;

 

        public const string K_PARAMETER = “k”;

        public const string S_PARAMETER = “s”;

        public const string V_PARAMETER = “v”;

        public const string START_PARAMETER = “start”;

 

        public const string ID_TIPO_VISTA = “ID_Vista”;

        public const string TIPO_VISTA = “Vista”;

        public const string ID_TIPO_VISTA_0 = “D”;

        public const string ID_TIPO_VISTA_1 = “R”;

 

        public const string ID_TIPO_VISTA_TEXT_0 = “date”;

        public const string ID_TIPO_VISTA_TEXT_1 = “relevance”;

 

        public Form1()

        {

            InitializeComponent();

 

            //Filling the combo

            DataTable dtlTipoVista = new DataTable();

            dtlTipoVista.Columns.Add(ID_TIPO_VISTA);

            dtlTipoVista.Columns.Add(TIPO_VISTA);

            DataRow dtrFila;

 

            //Por fecha

            dtrFila = dtlTipoVista.NewRow();

            dtrFila[ID_TIPO_VISTA] = ID_TIPO_VISTA_0;

            dtrFila[TIPO_VISTA] = ID_TIPO_VISTA_TEXT_0;

            dtlTipoVista.Rows.Add(dtrFila);

 

            //Por relevancia

            dtrFila = dtlTipoVista.NewRow();

            dtrFila[ID_TIPO_VISTA] = ID_TIPO_VISTA_1;

            dtrFila[TIPO_VISTA] = ID_TIPO_VISTA_TEXT_1;

            dtlTipoVista.Rows.Add(dtrFila);

 

            this.comboBox1.ValueMember = ID_TIPO_VISTA;

            this.comboBox1.DisplayMember = TIPO_VISTA;

 

            this.comboBox1.DataSource = dtlTipoVista;

           

        }

 

        private void button1_Click(object sender, EventArgs e)

        {

            try

            {

                string sKParameter = this.textBox1.Text;

                string sSParameter = this.textBox3.Text;

                string sVParameter = “”;

 

                if (this.comboBox1.SelectedValue.ToString() == ID_TIPO_VISTA_0)

                {

                    sVParameter = ID_TIPO_VISTA_TEXT_0;

                }

                else

                {

                    sVParameter = ID_TIPO_VISTA_TEXT_1;

                }

 

                string sSTARTParameter = this.textBox4.Text;

 

                string sUrl =

                     SEARCH_URL + QUESTION_MARK_SYMBOL +

                     K_PARAMETER + EQUAL_SYMBOL +

                     sKParameter +

                     AMPERSAND_SYMBOL +

                     S_PARAMETER + EQUAL_SYMBOL +

                     sSParameter +

                     AMPERSAND_SYMBOL +

                     V_PARAMETER + EQUAL_SYMBOL + sVParameter;      

 

                Uri uURL;

 

                if (this.textBox4.Text != “”)

                {

                    sUrl +=

                         AMPERSAND_SYMBOL  +

                         START_PARAMETER + EQUAL_SYMBOL + sSTARTParameter;

 

                    uURL = new Uri(sUrl);

                }

                else

                {

                    uURL = new Uri(sUrl);

                }

 

                this.webBrowser1.Url = uURL;

            }

            catch (Exception ex)

            {

 

                MessageBox.Show(ex.Message, “Error”,

                    MessageBoxButtons.OK, MessageBoxIcon.Error,

                    MessageBoxDefaultButton.Button1);

            }

        }

    }

}

 

Probando la aplicación

Sin más, ya estaríamos listos para probar nuestra mini-aplicación de búsquedas. Para ello:

  • Especificamos una palabra clave de búsqueda.
  • Especificamos el ámbito o ámbitos de búsquedas: All%20Sites (todos los sitios).
  • El tipo de vista: date (los resultados se muestran ordenados por fecha).
  • La página de inicio para los resultados.

image 

Como veis, el resultado que obtenemos es el mismo que obtendríamos con la búsqueda estándar de WSS 3.0 o MOSS, pero fuera de la plataforma.

SQL Server 2008: Creando informes con SSRS 2008 (III)!

Siguiendo con la serie de posts sobre creación de informes con SQL Server Reporting Services 2008 (SSRS 2008) que comenzamos con la parte I y II, la idea de esta tercera entrega es mostraros la capacidad que nos da SSRS 2008 para enriquecer nuestros informes con nuevos controles de tipo gráfico (chart). Empecemos.

Los nuevos controles gráficos de SSRS 2008: Controles propios de Office 2007

Para mostraros los nuevos controles gráficos de SSRS, partiremos del informe creado en el primer post previo sobre SSRS 2008 ( basado en la famosa BD AventureWorks). A este informe le vamos a añadir uno de estos nuevos controles gráficos. Como era de esperar, el Report Designer de SSRS 2008, no sólo tiene el look & feel de la suite de Microsoft Office 2008 sino que comparte muchas de sus funcionalidades. De hecho, a la hora de insertar gráficos nos permite las mismas posibilidades que tenemos disponibles con Microsoft Office Excel 2007. Para insertar un gráfico:

  • Agrandamos la superficie de diseño del informe.
  • A través del menú Insert, hacemos clic sobre el control Chart, de manera que se abrirá la correspondiente ventana en la que podremos elegir el tipo de gráfico a utilizar.
  • Seleccionamos uno de los tipos de gráficos que aparecen.
  • Tras pulsar OK, el gráfico aparecerá insertado en la superficie de diseño del informe.
  • Para configurar el control chart, tenemos que ponerlo en modo edición. A continuación, arrastramos campos del DataSet1 a las zonas de datos del gráfico:
    • Arrastramos el campo SalesAmount a Drop data fields here.
    • Arrastramos el campo OrderYear a Drop series fields here.
    • Arrastramos el campo Region a Drop category fields here.
image image  

image

  • Para configurar las propiedades del gráfico, hacemos clic con el botón derecho del ratón sobre el control, seleccionamos la opción Chart Properties y cambiamos las propiedades que consideremos.
  • Sin más, guardamos todos los cambios y hacemos un preview del informe. El gráfico aparecerá al final del informe.
image image  

Los nuevos controles gráficos de SSRS 2008: Controles Gauge

Aunque los gráficos propios de Office 2007 son por si sólos espectáculares, lo son más los gráficos de tipo Gauge. Estos controles, que Microsoft ha incluido en SSRS 2008, son los que ya conocemos de la empresa Dundas y ciertamente permiten implementar informes aun más espectaculares:

  • Insertamos un control Gauge a través del menú Insert y haciendo clic sobre dicho control.
  • Elegimos uno de los controles posibles.
  • Para configurar el control Gauge, no tenemos más que aprovecharnos del smart tag que nos ofrece.
  • Por ejemplo, añadimos el campo SalesAmount. Hacemos un preview del informe.
image image  

image

Por supuesto, el control Gauge lo podremos configurar para especificar los rangos mínimo y máximo, el layout, etc.

image

Sin más, hasta aquí llega la tercera entrega de la serie sobre SSRS 2008. En la próxima y última os hablaremos sobre el SQL Server Reporting Services Configuration Tool que viene con cambios significativos con respecto a su predecesor. Espero que el post os haya resultado interesante.

SQL Server 2008: Creando informes con SSRS 2008 (II)!

Hace un par de semanas explicábamos los principales cambios que aparecen en SSRS 2008 a la hora de crear y publicar informes. Veíamos que una de los principales cambios viene dado por la nueva apariencia del Report Designer que tiene un look & feel como el de la suite de Microsoft Office 2007. Pues bien, además de esto, SSRS 2008 es mucho más flexible que su predecesor en cuanto a que nos permite definir nuevos tipos de informes que antes no eran posibles, enriquecerlos con una serie de controles gráficos (DUNDAS), independencia con respecto a IIS a la hora de gestionar y configurar los distintos elementos de SSRS, etc.. En este post veremos el primer punto: definir nuevos tipos de informes que antes no eran posibles. En particular, vamos a ver una nueva y novedosa forma de organizar los datos: la funcionalidad Tablix. Empecemos.

Los orígenes de la funcionalidad Tablix

Antes de enseñaros un ejemplo de esta funcionalidad que nos permite construir informes todavía más flexibles, vamos a hacer un poco de memoria y buscar el origen de esta funcionalidad. Como sabéis, en versiones previas de SSRS, había dos tipos de regiones principales de datos: Table y Matrix. En una primera aproximación, una región Table se caracteriza porque tiene un número fijo de columnas y un número variable de columnas. En cambio, en una región Matrix el número de filas y columnas es variable. Esta es la primera gran diferencia (aunque hay más), de manera que a la hora de elegir si utilizar una u otra región nos podíamos guiar por la siguiente tabla (incluye también las pautas para determinar si necesitamos utilizar listas o gráficos):

Tipo de Estructura

Descripción

Niveles de Agrupación

Agrupación en base a ….

Table

Número fijo de columnas, con un número variable de filas (repeticiones)

De 0 a n

Columnas

Matrix

Crosstab fijo, con número variable de filas y columnas de datos agregados

De 0 a n

Filas o columnas

List

Layout con forma libre en el que se repiten una serie de elementos de informe

Uno

Lista

Chart

Visualización gráfica de los datos agregados de un cierto dataset

Categoría y Series

Depende del tipo de gráfico

¿Y por qué la funcionalidad Tablix? Esta es una buena pregunta a la vista de la tabla anterior, porque combinando las regiones anteriores tenemos cierta flexibilidad para crear tipos muy variados de informes…pero claro, ¿no tendríamos todavía más flexibilidad si existiera un mix entre Table y Matrix? Pues sí, y este es el quid de la cuestión. Por definición: Tablix = Table + Matrix, es decir se trata de una nueva forma de organizar los datos que coge lo mejor de Table y lo mejor de Matrix:

Como vemos, algunas novedades que se aprecian en esta funcionalidad y que no están disponibles en la región Matrix (Tablix viene a superar muchas de las limitaciones de Matrix) son:

  • Posibilidad de que distintos niveles de agrupamiento compartan la misma columna (en este caso categoría de producto y subcategoría).
  • Se puede definir niveles de agrupación extremo a extremo (agrupaciones por año y región).
  • y otras que no se aprecian en el pantallazo anterior como por ejemplo al posibilidad de definir grupos de agregación independientes

Bueno, aunque la teoría está muy bien, pasemos a la práctica.

Utilizando la funcionalidad Tablix en SSRS 2008

Para probar la funcionalidad Tablix, partiremos del informe creado en el post previo sobre SSRS 2008 (recordad que el informe está basado en la famosa BD AventureWorks :)):

  • Dentro del control Matrix del informe, borramos la columna de agrupamiento OrderYear. Para ello, seleccionamos la columna completa y pulsamos la opción Delete Columns.
  • Borramos la columna Total siguiendo el procedimiento anterior.
  • Borramos la fila Total que aparece encima de Product Category siguiendo el mismo procedimiento, pero aplicando la opción Delete Rows.El informe en vista de diseño debería tener el aspecto de la segunda figura.
  • Añadimos un nuevo grupo basado en el campo OderYear. Para ello, seleccionamos el campo Region del control Matrix, hacemos clic con el botón derecho del ratón y a continuación Region Group -> Add Group -> Adjacent Right.
image image  

image

  • En la ventana que se abre especificamos los siguientes parámetros:
    • Group name: Tablix1_Group1.
    • Group expression: Fields!OrderYear.Value
  • A través del smart tag de cada celda bajo la nueva columna que hemos creado, vamos añadiendo el campo SalesAmount.
  • Añadimos un subtotal para el campo Region. Lo añadiremos a partir de la celda Sum(SalesAmount) que aparece debajo de la columna Region. Para ello, hacemos clic con el botón derecho del ratón en esta celda y luego Add Total -> Column.
  • Por cada nueva celda que aparece bajo la columna que acabamos de crear, insertamos el campo SalesAmount utilizando el smart tag de cada celda.
  • Repetimos los dos pasos anteriores para añadir una nuevo subtotal (de tipo columna) para el campo OrderYear. En este caso, añadimos el subtotal haciendo clic sobre el campo Sum(SalesAmount) y utilizando el procedimiento estándar seguido en ocasiones anteriores.
image image  

image

  • Tras darle formato al informe, el aspecto en vista de diseño debería ser similar al siguiente:

image

  • Lo que hemos hecho en este informe es añadir dos columnas dinámicas e independientes, basadas en el mismo conjunto de valores…y esto es Tablix…algo que Matrix (parece que suena a película) no permitía hacer. Sin más, previsualizamos el informe:

image

Y esto era lo que os quería contar sobre Tablix. Sin duda esta es una de las grandes novedades de SSRS 2008 y una de las grandes revindicaciones tradicionales de los diseñadores de informes. En las próximas entregas veremos los nuevos controles y su uso al estilo Office, así cómo las novedades en cuanto a la configuración y gestión de SSRS 2008. Espero que el post os haya resultado interesante.

WSS 3.0 & MOSS: Recopilación de enlaces interesantes (XVII)!

Aunque cada vez es más difícil ponerse al día, sobre todo por la falta de tiempo, de vez en cuando consigo encontrar un hueco para revisar mis RSS (que siguen creciendo y creciendo…cualquier día de estos me deboran :PPPP) sobre SharePoint y otras hierbas y poneros al día con las novedades más destacadas desde el último recopilatorio. Empecemos.

Documentación, Recursos & Artículos

  • Post Jeremiah Clark sobre un tópico realmente interesante: el encriptado de información en SharePoint.
  • Desde hace unas semanas ya está disponible el nuevo libro de Patrick Tisseghem: Inside the Index and Search Engines: Microsoft Office SharePoint® Server 2007.
  • Artículo en MSDN sobre como crear Timer Jobs en SharePoint. Autor: Andrew Connell.
  • Otro recopilatorio más de recursos sobre SharePoint.
  • Más formación gratuita sobre búsquedas en plataforma SharePoint. Aquí tenéis otro enlace más de formación gratuita en búsquedas.
  • El próximo día 13 de mayo tendrá lugar la conferencia online virtual: SharePoint in Action – Develping and Deploying Real World Solutions. Entre los ponentes estará Andrew Connell.
  • ¿Quieres convertirte en un desarrollador profesional en plataforma SharePoint? Paul Andrew explica en esta entrada todo lo que se necesita para convertirse en un desarrollador en SharePoint.
  • Gracias a Sahil Malik, tenemos este resumen de los límites de capacidad de SharePoint que siempre hay que tener en cuenta:

    Object

    Guidelines

    Notes

    Site Collection

    50,000 per website

     

    Sub site

    2000 per top level site collection

     

    Documents

    10 million per document library

    This is a very arguable limit. This depends on the document size, and what specifically are you doing with the document. For instance, if you are checking-in, out and versioning – this limit is a lot lower.

     

    Also, no view must return more than 2000 documents unless you create logical indexes on certain columns, in which case you can go to about 20,000 documents per view.

    Items

    2000 per view

    As mentioned above, no view must return more than 2000 documents unless you create logical indexes on certain columns, in which case you can go to about 20,000 documents per view. This limit is common to lists and document libraries.

    Document File Size

    50MB recommended, no more than 2Gb ever.

    Anything greater than 2GB will error out. Anything greater than 50MB will severely negatively impact your database performance. Larger artifacts should be stored in a separate site in a separate content database on a separate spindle in the database server.

    Lists

    No more than 2000 lists per site

    In this case, Lists = Lists + document libraries. If you have 2000 of these in one site, the navigation will become extremely clumsy, so in most cases you wouldn’t hit this limit.

    Field Type

    256 per list

    No more than 256 fields in a list should be used. Crossing this limit causes severe performance degradation. Instead, you should use linked fields instead.

    Web Parts

    50 per page

    This is again a very arguable limit. SharePoint webparts out of the box unfortunately do not support asynchronous loading. It is possible to introduce this behavior by modifying the aspx page, but it requires developer intervention. You will note that page load times increase exponentially as the number of webparts on a page increase. This also depends on the complexity of the webpart. You should leverage AJAX for pages with a lot of webparts.

    Users

    No more than 2 million per website

    Also note, if the profile import of a user is too wide, this limit significantly affects both the shared service provider database, and the content database.

    Number of SSPs in a farm

    No more than 20. Recommended maximum = 3.

     

    Number of websites per SSP

    99

     

    Number of sites per content DB

    Less than 50,000

     

    Number of content databases

    100 per web application.

     

    Number of search indexes

    No more than 20 per farm, no more than one per SSP.

    Index propagation becomes horribly complex as a large number of SSPs are added. To avoid this scenario, you should leverage scoped searches instead.

    Content sources and start addresses

    500 hard limit

     

    Alerts

    1 million alerts on a site

    Your SMTP server will probably choke a long time before you hit this limit.

    Search scopes

    Less than 200 per site

     

     

  • Interesante artículo en el blog del equipo de SharePoint: Exchange Public Folders vs SharePoint.

Tips & Tricks

Novedades

 searchgadget.png

Y hasta aquí llega la 17º entrega del recopilatorio de recursos de SharePoint. Espero que la información os haya resultado interesante.