[Evento] Potencia tus desarrollos de SharePoint con Azure

Muy buenas a todos, y antes de nada Feliz Navidad a todos los que habitualmente entran a “El blog del programador”.

Hoy os quiero hablar del próximo evento que se va a celebrar, organizado por la comunidad de usuarios de SharePoint de Madrid, MadPoint, y que tendrá lugar el próximo 15 de Enero en Wayra en la calla Gran Via, 28.

En dicho evento, tendremos la oportunidad de profundizar en las oportunidades que nos ofrece Azure para usarla en nuestros desarrollos de SharePoint, tanto OnLine como OnPremise. Os dejo el enlace de la página de madpoint donde encontraréis toda la información sobre el evento

http://www.madpoint.net/2015/12/23/evento-potencia-tus-desarrollos-de-sharepoint-con-azure/

La agenda del evento, en donde tendré la oportunidad de aportar mi granito de arena, es la siguiente:

  • 16.00 – 16.15: Bienvenida e introducción
  • 16.15-16.50: Diseña tu propio Office 365 con Azure IaaS y PaaS
    • En esta sesión teórica, Miguel Tabera (MVP de Office Servers and Services) nos enseñará, desde el punto de vista de arquitectura, cómo es posible utilizar máquinas virtuales y servicios como las web apps, Azure Search, Media Services y Application Insights para diseñar nuestro propio servicio similar a Office 365 en el que proporcionemos SharePoint, Exchange, Office Vídeo, etc.
  • 17.00-17.50: Logic apps o el futuro de los flujos de trabajo
    • ¿Qué son las logic apps? ¿Cómo las podemos utilizar? ¿Se convertirá en el motor para desarrollar nuestras lógicas de negocio y flujos de trabajo en el futuro? En esta sesión, Jose Carlos Rodríguez Avilés, nos enseñará qué podemos hacer con este servicio de Azure y cómo podemos usarlo para implementar lógicas de negocio interconectando entre si la amplia gama de herramientas que usamos hoy en día.
  • 18.00-18.50: Timerjobs y eventos en SharePoint Online usando Hangfire
    • Cristian Ruiz nos mostrará un ejemplo práctico de cómo crear timerjobs y eventos remotos (evitando los límites de tiempo de SharePoint online) utilizando el motor de tareas programadas.

Como podéis ver, las ponencias son muy interesantes, os animo a seguir de cerca la información del evento, y a que participéis en el mismo. Poco a poco, desde MadPoint iréis recibiendo más información, desde twitter, linkedin y la página de web, para todos aquellos que podáis estar interesado.

Un saludo, y por si acaso que tengáis una muy buena entrada del año 2016.

 

Paginando en SharePoint 2013 con API REST

Muy buenas a todos,

Hoy quería contaros un tema con el que me he estado peleando hoy en mi proyecto con el que estoy haciendo un uso bastante intensivo de la API REST de SharePoint con AngularJS. Una experiencia que os debo decir, me está encantando.

El tema ha venido a la hora de “paginar” una consulta en SharePoint que estaba haciendo vía API REST. Según pensaba, usando las opciones $top y $skip se podría hacer la paginación sin problemas. Para ello pensaba usar el endpoint que habitualmente uso para trabajar con las listas de SharePoint

http://<misitio>/_api/web/lists/getbytitle(‘lista&#8217;)/items

Mi sorpresa ha sido darme cuenta que con este endpoint la opción $skip no funciona. Indagando un poco he descubierto que efectivamente, esta opción no funciona para lista de elementos, solo funciona para colecciones de datos como colecciones de listas.

http://sharepoint.stackexchange.com/questions/45719/paging-using-rest-odata-with-sp-2013

https://msdn.microsoft.com/en-us/library/office/fp142385.aspx

Para hacer la paginación via API REST podemos usar el antiguo endpoint OData V2 listdata.svc. Con esta versión si funciona la opción $skip correctamente:

http://<misitio>/_vti_bin/ListData.svc/<lista&gt;

Ejemplo: http://<misitio>/_vti_bin/ListData.svc/<lista&gt;?$top=2&$skip=2

Salvando este inconveniente sobre el endpoint que debemos usar, usando estas dos opciones $top y $skip, podremos paginar nuestras consultas de una forma muy sencilla.

Y esto es todo por hoy, espero que os resulte útil, como siempre, y que si os habéis encontrado con esta problemática, tardéis menos tiempo en resolverlo.

Saludos.

[OffTopic]

No suelo acostumbrar a hacer nada de esto. Pero hoy ha nacido mi sobrino, del que tendré la oportunidad además de ser su padrino. Así que esta entrada de hoy va dedicada a él y a sus padres. Aunque no lo podré conocer hasta dentro de 15 días porque me ha salido “conejero” y está en Lanzarote, que ilusión más grande.

Usando el nuevo modelo de desarrollo para SharePoint con PowerShell

Muy buenas a todos,

Hace unos meses, escribí acerca del nuevo modelo de desarrollo para SharePoint OnLine y SharePoint OnPremises propuesto por Microsoft e incluso una de mis colaboraciones en CompartiMOSS giró en torno a este tema.

Nuevo modelo de desarrollo de Sharepoint. Adaptando nuestras soluciones de granja

Número #24 de CompartiMOSS y nueva colaboración con la revista

Como ya decía en aquella ocasión, trabajar con las API cliente se podría hacer tanto por medio de la API REST, con la API CSOM o JSOM o incluso PowerShell. Tenía muchas ganas de ver cómo se podría trabajar con éste último y para un proyecto en el que estoy trabajando recientemente decidí que ya era el momento.

La primera intención, fue desarrollar mis propios Cmdlets o mis propios Scripts con funciones para realizar las distintas operaciones que fuese necesitando. Finalmente decidí previamente indagar por la cuenta de GitHub de OfficeDev/Pnp, donde encontré un gran trabajo especifico sobre PowerShell, proporcionando una gran cantidad de Cmdlets desarrollados que permiten trabajar con las API Cliente, concretamente CSOM, y PowerShell. Por supuesto, trabajo que decidí utilizar, y que os recomiendo a todos.

https://github.com/OfficeDev/PnP-powershell

En el artículo de hoy, os quiero mostrar cómo instalar y configurar en nuestro servidor estos Cmdlets y cómo se pueden usar algunos de ellos a modo de ejemplo. No obstante, en la página de Pnp hay una documentación muy buena sobre todas las opciones de que disponemos.

Instalando los Cmdlets en el servidor

Para poder tener disponibles todos los Cmdlets de PowerShell tendremos que seguir los siguientes pasos:

1.- En primer lugar descargar el proyecto Visual Studio desde la cuenta de GitHub de OfficeDev/Pnp-PowerShell.

download

2.- Para poder instalar el proyecto, es necesario descargar e instalar las WiX Tools en nuestro equipo.

wix

3.- A continuación abrimos la solución que descargamos anteriormente y para poder crear la solución, necesitamos agregar dos paquetes desde Nugets. El primero de ellos es el OfficeDev Core para SharePoint Online o OnPremises, en función de para qué lo vayamos a usar.

coreoffice

4.- El siguiente paquete es la librería ADAL para la autenticación.

identity

5.- Tras esto ya se puede hacer el build del proyecto lo cual dejará instalados los Cmdlets en nuestro equipo

install

Si ahora abrimos por ejemplo el IDE de PowerShell, en la lista de comandos disponibles, si ponemos en el cajón de búsqueda SPO, filtrará todos los comandos que se han añadido.

Usando los nuevo Cmdlets

A continuación voy a mostrar un breve ejemplo de cómo podríamos usar los nuevos Cmdlets con PowerShell.

Connect-SPOnline -Url $url -Credentials (Get-Credential)

Add-SPOField -DisplayName "Example" -InternalName "Example" -Group "ExampleSPO" -Type Integer
Add-SPOField -DisplayName "Example1" -InternalName "Example1" -Group "ExampleSPO" -Type Text
Add-SPOField -DisplayName "Example2" -InternalName "Example2" -Group "ExampleSPO" -Type Choice -Choices "uno","dos","tres"

Add-SPOContentType -Name "ContentTypeExample" -Description "Description to Content Type" -Group "ExampleSPO"

Add-SPOFieldToContentType -Field "Example" -ContentType "ContentTypeExample"
Add-SPOFieldToContentType -Field "Example1" -ContentType "ContentTypeExample"
Add-SPOFieldToContentType -Field "Example2" -ContentType "ContentTypeExample"

New-SPOList -Title "List Title" -Template GenericList -EnableContentTypes
Add-SPOContentTypeToList -List "List Title" -ContentType "ContentTypeExample" -DefaultContentType

En el código de ejemplo se muestra cómo conectarse a un sitio de SharePoint OnLine, crear columnas de sitio, un tipo de contenido y asociarlo a una lista que se ha creado previamente.

Y esto es todo por hoy, por si aún no lo conocíais, espero que os sea de utilidad y que en la medida de lo posible, podáis usarlo en vuestros proyectos.

Hasta la próxima.

Número #24 de CompartiMOSS y nueva colaboración con la revista

Muy buenas a todos,

El pasado miércoles durante el CEUS by Iberian Conference se presentó un nuevo número de la revista especializada en SharePoint CompartiMOSS. En esta ocasión, la revista trae mucho contenido interesante y cada vez más comienzan a aparecer artículos relacionados con Azure, Machine Learning, SharePoint OnLine y siempre sin olvidar la versión de SharePoint OnPremise.

numero24

Para acceder al número de la revista, podéis hacer click aquí. Os dejo también acceso a todo el contenido del número de la revista, para que os podáis hacer una idea del contenido de la misma.

Una vez más he dejado mi pequeña aportación a esta revista que espero que os guste. En esta ocasión os escribo sobre el nuevo modelo de desarrollo que se propone desde Microsoft para SharePoint, tanto la versión OnLine como la versión OnPremise. Algo ya he comentado por aquí, con algunos artículos sobre el tema que iban saliendo mientras preparaba el artículo.

Para terminar muchas gracias a todos los que hacen posible que esta revista siga adelante.

Un saludo y hasta la próxima

Timer jobs para SharePoint OnLine

Muy buenas a todos,

El modelo de desarrollo de SharePoint OnLine, como ya es conocido, no nos permite el uso de soluciones de granja en nuestros desarrollos, basado fundamentalmente en el modelo de objetos de servidor. Por contra, nos propone un modelo basado en los mejorados modelos de cliente.

A priori, puede parecer que le modelo de objetos de cliente tiene ciertas limitaciones por medio de las cuales no podremos cubrir requisitos que impliquen por ejemplo desarrollar TimerJobs. ¿Es esto cierto?, la realidad es que con el modelo de objetos de cliente, no vamos a poder crear TimerJobs tal y como estábamos acostumbrados con las soluciones de granja, ¿significa esto que no podremos crear tareas programadas en SharePoint OnLine?, aquí la respuesta es obviamente no.

Para poder hacerlo tendremos, eso si, que combinar alguno de los servicios que pone a nuestra disposición Microsoft, a través de Azure. Usaremos los WebJobs de Azure para, en combinación con el modelo de clientes, crear una tarea programada para SharePoint OnLine.

En la entrada de hoy vamos a ver un ejemplo de un WebJob de Azure que realiza un tarea en background sobre SharePoint OnLine, crearemos un TimerJob para SharePoint OnLine. Para llevarlo a cabo he seguido estos dos tutoriales que os recomiendo por si quereis echar un vistazo.

https://msdn.microsoft.com/en-us/library/office/dn986826.aspx

https://azure.microsoft.com/es-es/documentation/articles/websites-dotnet-deploy-webjobs/

Creando el proyecto en Visual Studio 2013

Voy a desarrollar un WebJob que escribe en una lista de SharePoint OnLine un registro cada cierto tiempo. Para ello vamos a crear en Visual Studio una aplicación de consola.

Una vez que hemos creado la aplicación de consola, vamos a añadir por medio de Nuget los siguientes paquetes:

  • App for SharePoint ToolKit (for SharePoint OnLine)
  • Microsoft.SharePoint.2013.Client.16

Esto nos añadirá todos los elementos necesarios para autenticarnos y acceder a SharePoint OnLine desde nuestra Aplicación de consola.

A continuación os comparto el código del WebJob que he desarrollado.

namespace AzureWebJobExample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ClientContext context = new ClientContext("https://<sharepointsiteurl>"))
            {
                // Use default authentication mode.
                context.AuthenticationMode = ClientAuthenticationMode.Default;
                context.Credentials = new SharePointOnlineCredentials(GetSPOAccountName(), GetSPOSecureStringPassword());

                try
                {
                    var list = context.Web.Lists.GetByTitle("Noticias");
                    context.Load(list);
                    context.ExecuteQuery();

                    var itemCreateInfo = new ListItemCreationInformation();
                    var listItem = list.AddItem(itemCreateInfo);
                    listItem["Title"] = "News Web Job Example Added " + DateTime.Now;
                    listItem["nwhz"] = "This is an example of a item added from a time job";
                    listItem.Update();
                    context.ExecuteQuery();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("ERROR: " + ex.Message);
                    Console.WriteLine("ERROR: " + ex.Source);
                    Console.WriteLine("ERROR: " + ex.StackTrace);
                    Console.WriteLine("ERROR: " + ex.InnerException);
                }
            }
        }
        private static SecureString GetSPOSecureStringPassword()
        {
            try
            {
                Console.WriteLine("Entered GetSPOSecureStringPassword.");
                var secureString = new SecureString();
                foreach (char c in ConfigurationManager.AppSettings["SPOPassword"])
                {
                    secureString.AppendChar(c);
                }
                Console.WriteLine("Constructed the secure password.");

                return secureString;
            }
            catch
            {
                throw;
            }
        }

        private static string GetSPOAccountName()
        {
            try
            {
                Console.WriteLine("Entered GetSPOAccountName.");
                return ConfigurationManager.AppSettings["SPOAccount"];
            }
            catch
            {
                throw;
            }
        }

    }
}

Como podéis ver el código es muy sencillo, tan solo hay que añadir dos funciones, tal y como nos cuentan en uno de los enlaces que os he indicado, para obtener del app.config el usuario y la contraseña para conectarnos a SharePoint OnLine. Tras esto, usando el modelo de objetos de cliente para C# se hacen las operaciones deseadas sobre SharePoint OnLine.

Lo que nos queda ahora, es publicar el WebJob en Azure para que funcione correctamente, que será lo que veremos en la próxima sección

Publicando el WebJob en Azure

Vamos a ver ahora los pasos para publicar nuestro WebJob en Azure y ver como funciona.

1.- Seleccionamos la opción de publicar la aplicación de consola como Azure WebJob

webjob1

2.- El siguiente es configurar el WebJob

webjob2

Es importante tener en cuenta una cosa. Este WebJob se crea en el modo gratuito (o yo no he encontrado la forma de crearlo desde visual studio en otro modo), y este modo tiene una limitación a la hora de la frecuencia permitida de como máximo una hora, por lo que si seleccionamos una frecuencia mayor, nos dará un error. Luego podremos modificar la frecuencia desde el portal de Azure si queremos hacerlo más frecuente, pero para empezar, debemos de tener en cuenta esa restricción.

3.- A continuación configuramos el website al que estará asociado el webjob

webjob3

webjob4

webjob5

webjob6

4.- Cambiando la configuración del WebJob en el portal de Azure

webjob7

En nuestro portal de Azure, en el Área de “Programador”, accedemos a la colección de trabajos que se ha creado por defecto y en “Escalar”, cambiamos del modo gratis a estándar y cambiamos el límite de frecuencia de ejecución. Tras esto entramos a trabajos y configuramos el que tenemos creado para que se ejecute a cada minuto (para poder ver como funciona el WebJob que hemos creado más rápidamente). No debemos olvidar guardar los cambios para que surtan efecto.

5.- Viendo los resultados del webjob.

webjob9

webjob10

Tanto en el sitio de SharePoint, como dentro de nuestro website de Azure se pueden ver los resultados de la ejecución del WebJob. Aquí se puede ver el resultado del TimerJob para SharePoint OnLine que hemos creado ;).

Y nada más por hoy, espero que como siempre os sea útil, evidentemente el ejemplo es muy sencillo, y podemos ir mucho más allá con esta técnica, pero simplemente os quería mostrar cómo hacerlo.

Un saludo a todos

Sharepoint-search: un componente Polymer para usar la búsqueda de SharePoint On-Line

Muy buenas a todos.

En una entrada anterior, os contaba cómo usar la búsqueda de SharePoint a través de su servicio API REST. El enlace ha dicha entrada es la siguiente:

Search Driven Development. Usando la API REST de SharePoint para el servicio de búsqueda

Al final de esta entrada os contaba que como objetivo, tenía el desarrollo de un componente Polymer que permitiera usar este servicio, algo que me resultaba muy útil e interesante. Después de varios días de trabajo, quería compartir con vosotros una primera “versión” de dicho componente. Aún queda trabajo por delante, pero ya hay un punto de partida funcional a partir del cual seguir trabajando.

El componente debía cumplir con los siguientes requisitos:

  • Permitir la posibilidad de usar refinadores.
  • Permitir la posibilidad de establecer opciones de ordenamiento.
  • Controlar la paginación y el número de resultados por página en los resultados de búsqueda.
  • Dar la posibilidad de estilar el contenido del componente en función de como se desee.

El componente “sharepoint-search” nos permite cubrir estos requisitos. Para usarlo, necesitaremos tener cargados los ficheros de Polymer y los propios del componente y utilizaremos la etiqueta de la siguiente manera:

<sharepoint-search tenant="organizer" clientid="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" refinement="author,size,created" ordination="size,created" pageElement="15">
</sharepoint-search>

Para que funcione correctamente, necesitaremos el clientID de una aplicación de Azure AD con los permisos para el servicio de búsqueda de SharePoint concedidos, y obviamente estar logado en nuestra aplicación contra esa aplicación de Azure AD, para ello podemos usar el componente para la autenticación que creé en una entrada anterior y seguir los correspondientes pasos para obtener el clientid:

Registrando una aplicación en Azure Active Directory

login-azuread: Un Componente Web con Polymer y Javascript ADAL

Además deberemos de configurar la aplicación de nuestra Azure AD para permitir el OAuth 2.0 implicit grant flow, lo haremos de la siguiente forma:

  • Acceder a la configuración de la aplicación de Azure AD correspondiente
  • Descargamos el manifiesto
  • Modificamos el parámetro oauth2AllowImplicitFlow dándole el valor true en lugar de false que tiene establecido inicialmente
  • Cargamos de nuevo el manifiesto modificado

El componente “sharepoint-search” permite configurar los siguientes parámetros:

  • tenant: Indicar el tenant al que nos queremos conectar
  • clientid: Clientid de la aplicación con los permisos adecuados
  • refinement: Propiedades para las cuales queremos obtener refinamiento
  • ordination: Propiedades por las que queremos hacer ordenación
  • pageElement: Número de elementos por página a mostrar

Una vez que lo añadimos, funciona como vemos a continuación.

sharepointsearchinit

sharepointsearchsearched

Por defecto, el componente nos muestra los resultados en una lista con el título del documento y la posibilidad de descargar el mismo. Aunque como se indica a continuación, en futuras mejoras, se trabajará en flexibilizar este aspecto del componente.

Os dejo el enlace a mi cuenta de GitHub donde podéis descargar el código del componente web y donde podréis encontrar información sobre cómo usarlo.

https://github.com/jcroav/sharepoint-search

Y esto es todo por hoy, este componente aún tiene áreas de mejora, a las que intentaré dedicarle tiempo en las próximas semanas. Os dejo algunas de las ideas:

  • El componente está pensado para que los resultados de búsqueda usen a su vez un subcomponente para ser mostrados, queda pendiente de desarrollar esa parte.
  • Completar el componente con la funcionalidad de sugerencias
  • Mejorar toda la parte de estilado
  • Refactorizar y trabajar sobre la optimiazación del código para hacer un componente más robusto

Como siempre, todo aquel que quiera colaborar, o quiera usar el componente que os he mostrado hoy para mejorarlo, es libre de hacerlo. El objetivo de estos desarrollos, a parte de para aprender a usar las funcionalidades de Office 365 y SharePoint On-Line, es contribuir a la comunidad.

Un saludo y espero que os sea útil

Search Driven Development. Usando la API REST de SharePoint para el servicio de búsqueda

Muy buenas a todos,

Recientemente he publicado algunos artículos sobre Search Driven Development para SharePoint Online y SharePoint 2013. Además hace poco tiempo tuve la oportunidad de participar en una mesa redonda con la gente de MadPoint en la que se hablaba de técnicas de desarrollo tanto para SharePoint OnLine como 2013 y Office 365. En esta mesa redonda salió la posibilidad de usar en los desarrollos la API REST del servicio de búsquedas de SharePoint y los nuevos frameworks de Javascript para crear nuestra propia solución basada en búsquedas.

Hasta entonces no había tenido la oportunidad de probar esta API REST, pero hoy os traigo una entrada en la que os quiero enseñar como usar las búsquedas a través de la API REST que nos proporciona esta versión de SharePoint.

En una primera parte de la entrada os enseñaré que consultas REST podemos hacer al servicio y lo que nos devuelve y a continuación veremos ejemplos de cómo utilizar el mismo en una app for SharePoint (o SharePoint Add-ins??).

Llamadas REST para el servicio de búsqueda

Uno de los aspectos importantes que tenemos que tener en cuenta cuando trabajamos con la API REST del servicio de búsqueda es que las consultas al mismo podemos hacerlas a través de peticiones GET y a través de peticiones POST. Esto es así, porque como es sabido por todos, las peticiones GET tienen limitaciones en cuanto al número de caracteres y puede ocurrir que una consulta que queramos hacer sobrepase ese límite, por lo que tendremos la oportunidad de usar las peticiones POST para superar dicha restricción.

A continuación voy a enseñar algunos ejemplos básicos de cómo utilizar ambos tipos de peticiones, aunque las posibilidades que tenemos a la hora de hacer las peticiones son muy extensas. Para esto os dejo el enlace a la referencia de la MSDN, donde podréis analizar al completo las opciones de la API.

https://msdn.microsoft.com/es-es/library/office/jj163876.aspx

Usando peticiones GET

Las peticiones GET se hacen a través de la siguiente URL:

http://servidor/_api/search/query

Consulta con un texto a buscar
/_api/search/query?querytext=’textoAconsultar’
Consulta usando una query
/_api/search/query?querytemplate='{searchterms} FileExtension: doc’
Usando la ordenación
/_api/search/query?querytext=’textAconsultar’&sortlist=’created:ascending,rank:descending,’
Indicando los refinadores a utilizar
/_api/search/query?querytext=’textAconsultar’&refiners=’author,size,fileExtension’
Filtrando la consulta usando el refinamiento
/_api/search/query?querytext=’textAconsultar’&refinementfilters=’fileExtension:equals(“docx”)’

Usando peticiones POST

Las peticiones POST se hacen a través de la siguiente URL:

http://servidor/_api/search/postquery

Consulta con un texto a buscar
{'request':
    { 
      '__metadata' : {'type' : 
                   'Microsoft.Office.Server.Search.REST.SearchRequest'},
      'Querytext' : 'ejemploAconsultar'
    }
}

Son muy importantes las mayúsculas y minúsculas en las consultas usando el método POST. Por ejemplo, en la consulta anterior, tenemos que usar ‘Querytext’ y no ‘QueryText’ o ‘querytext’ como hacemos en la consulta GET.

Estas consultas devuelven un JSON con toda la información necesaria de la búsqueda. Al igual que el resto de servicios REST, este de búsquedas es compatible con JSON Light, por lo que si lo deseeamos podemos obtener respuestas más ligeras y rápidas. Los resultados se encuentran en el objeto devuelto en:

data.body.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results (para GET)
data.body.d.postquery.PrimaryQueryResult.RelevantResult.Table.Rows.results (para POST)

Este elemento “results” json es un array que contiene una fila para cada uno de los resultados, en cada columna o celda de esta fila tenemos una de las propiedades del objeto devuelto. Para simplificar un poco la tarea, os voy a indicar en qué posición se encuentra la información que habitualmente es más importante de los objetos devueltos.

  • Posición 3: Title
  • Posición 4: Author
  • Posición 5: Size
  • Posición 6: Path
  • Posición 7: Description
  • Posición 8: Write
  • Posición 11: HitHighlightedSummary
  • Posición 18: FileExtension
  • Posición 31: FileType

El mismo servicio, si le hemos indicado para qué propiedades queremos obtener refinadores, nos devuelve los refinadores para esa búsqueda en:

data.body.d.query.PrimaryQueryResult.RefinementResults.Refiners.results

Esto nos devuelve, para cada refinador, un objeto de la siguiente forma:

{'Entries':
    {'results'
         {
             RefinementCount: count,
             RefinementToken: token,
             RefinementName: name,
             RefinementValue: value
         },
         {
             RefinementCount: count,
             RefinementToken: token,
             RefinementName: name,
             RefinementValue: value
         }
    },
  'Name':Refiner
}

Cada uno de los elementos contenidos en el objeto Entries, nos indica una opción de refinamiento para el refinado correspondiente. A continuación vamos a ver cómo he usado este servicio en una aplicación para SharePoint a modo de prueba.

Usando la API REST para búsquedas en una App for SharePoint

El ejemplo lo he realizado usando JQuery para manipular el DOM de la página del mismo. En primer lugar vamos a ver una captura de la aplicación para explicar que hace cada sección de la misma:

appsearchexample

En la aplicación que os quiero enseñar he hecho 5 ejemplos de como usar la API REST del servicio de búsqueda:

  • El primero ejemplo es una consulta usando GET a partir de una cadena de texto.
  • El segundo ejemplo es una consulta por POST a partir de una cadena de texto.
  • El tercer ejemplo usar una query para hacer la llamada al servicio de búsqueda por GET.
  • En el cuarto ejemplo se usa la ordenación.
  • El quinto ejemplo utiliza los refinadores.

Código del primer ejemplo

$("#searchbutton1").click(function () {

        var searchText = $("#searchbox1").val();

        executor.executeAsync({
            method: "GET",
            url: appweburl + "/_api/search/query?querytext='" + searchText + "'",
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose"
            },
            success: function (data) {

                var element = document.getElementById("resultsList1");

                $('#resultsList1 > li').remove();

                var jsonObject = JSON.parse(data.body);

                console.log(jsonObject);

                var results = jsonObject.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;

                for(var i = 0; i < results.length; i++)
                {
                    var li = document.createElement("li");

                    li.innerText = results[i].Cells.results[3].Value + "
" + results[i].Cells.results[5].Value + "&nbsp;" + results[i].Cells.results[4].Value + "
" + results[i].Cells.results[11].Value;

                    element.appendChild(li);
                }
            },
            error: function (data) {
            }
        });

        return false;

    });

Código del segundo ejemplo

$("#searchbutton2").click(function () {

        var searchText = $("#searchbox2").val();

        executor.executeAsync({
            method: "POST",
            url: appweburl + "/_api/search/postquery",
            body: "{'request': { '__metadata' : {'type' : 'Microsoft.Office.Server.Search.REST.SearchRequest'}, 'Querytext' : '" + searchText + "' }}",
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose"
            },
            success: function (data) {

                var element = document.getElementById("resultsList2");

                $('#resultsList2 > li').remove();

                var jsonObject = JSON.parse(data.body);
                var results = jsonObject.d.postquery.PrimaryQueryResult.RelevantResults.Table.Rows.results;

                for (var i = 0; i < results.length; i++) {
                    var li = document.createElement("li");

                    li.innerText = results[i].Cells.results[3].Value;

                    element.appendChild(li);
                }
            },
            error: function (data) {
            }
        });

        return false;

    });

Código del tercer ejemplo

executor.executeAsync({
        method: "GET",
        url: appweburl + "/_api/search/query?querytemplate='{searchterms} FileExtension: doc'",
        headers: {
            "accept": "application/json;odata=verbose",
            "content-type": "application/json;odata=verbose"
        },
        success: function (data) {

            var element = document.getElementById("resultsList3");

            $('#resultsList3 > li').remove();

            var jsonObject = JSON.parse(data.body);

            var results = jsonObject.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;

            for (var i = 0; i < results.length; i++) {
                var li = document.createElement("li");

                li.innerText = results[i].Cells.results[3].Value;

                element.appendChild(li);
            }
        },
        error: function (data) {
        }
    });

Los tres primeros ejemplos son muy similares. Entre el primero y el segundo la única diferencia es que una petición se hace por GET y otra petición se hace por POST. En el tercero, se vuelve a usar una consulta por GET pero en este caso en lugar de un texto lo que se pasa es una consulta como tal. Todos los ejemplos usan una lista para representar los resultados devueltos.

Código del cuarto ejemplo

$("#searchbutton4").click(function () {

        var searchText = $("#searchbox4").val();

        executor.executeAsync({
            method: "POST",
            url: appweburl + "/_api/search/postquery",
            body: "{'request': { '__metadata' : {'type' : 'Microsoft.Office.Server.Search.REST.SearchRequest'}, 'Querytext' : '" + searchText + "' }}",
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose"
            },
            success: function (data) {

                var element = document.getElementById("resultsList4");

                $('#resultsList4 > li').remove();

                var jsonObject = JSON.parse(data.body);
                var results = jsonObject.d.postquery.PrimaryQueryResult.RelevantResults.Table.Rows.results;

                for (var i = 0; i < results.length; i++) {
                    var li = document.createElement("li");

                    li.innerText = results[i].Cells.results[3].Value;

                    element.appendChild(li);
                }
            },
            error: function (data) {
            }
        });

        return false;

    });

    $("#orderbutton4").change(function () {

        var searchText = $("#searchbox4").val();
        var value = $("#orderbutton4").val();
        var order = "";

        if(value == "asc")
        {
            order = "created:ascending";
        }
        else
        {
            order = "created:descending";
        }

        executor.executeAsync({
            method: "GET",
            url: appweburl + "/_api/search/query?querytext='" + searchText + "'&sortlist='" + order + "'",
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose"
            },
            success: function (data) {

                var element = document.getElementById("resultsList4");

                $('#resultsList4 > li').remove();

                var jsonObject = JSON.parse(data.body);

                var results = jsonObject.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;

                for (var i = 0; i < results.length; i++) {
                    var li = document.createElement("li");

                    li.innerText = results[i].Cells.results[3].Value + " " + results[i].Cells.results[8].Value;

                    element.appendChild(li);
                }
            },
            error: function (data) {
            }
        });

        return false;
    });

Básicamente este ejemplo funciona de la misma forma que los anteriores. Lo único extraordinario, tiene que ver con que queremos usar la ordenación por la propiedad “size” de los resultados. Hemos creado un combo donde podemos seleccionar, si queremos, orden ascendente y descendente para el tamaño. Lo que se ha hecho es crear otro evento que se dispara cuando cambia el valor seleccionado del combo para que se haga la ordenación. Para ello se añade a la consulta la parte correspondiente (sortlist) en función de si hemos seleccionado ascendente o descendente.

Código del último ejemplo

$("#searchbutton5").click(function () {

        var searchText = $("#searchbox5").val();

        executor.executeAsync({
            method: "GET",
            url: appweburl + "/_api/search/query?querytext='" + searchText + "'&refiners='author,size'",
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose"
            },
            success: function (data) {

                var element = document.getElementById("resultsList5");

                $('#resultsList5 > li').remove();

                var jsonObject = JSON.parse(data.body);

                console.log(jsonObject);

                var refiners = jsonObject.d.query.PrimaryQueryResult.RefinementResults.Refiners.results;

                $("#titlerefiner").text(refiners[0].Name);

                $.each(refiners[0].Entries.results, function (i, item) {
                    $('#valuerefiners').append($('<option>', {
                        value: refiners[0].Name + ":" + item.RefinementToken,
                        text: item.RefinementName
                    }));
                });

                var results = jsonObject.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;

                for (var i = 0; i < results.length; i++) {
                    var li = document.createElement("li");

                    li.innerText = results[i].Cells.results[3].Value;

                    element.appendChild(li);
                }
            },
            error: function (data) {
            }
        });

        return false;

    });

    $("#valuerefiners").change(function () {

        var searchText = $("#searchbox5").val();
        var value = $("#valuerefiners").val();

        executor.executeAsync({
            method: "GET",
            url: appweburl + "/_api/search/query?querytext='" + searchText + "'&refinementfilters='" + value + "'",
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose"
            },
            success: function (data) {

                var element = document.getElementById("resultsList5");

                $('#resultsList5 > li').remove();

                var jsonObject = JSON.parse(data.body);

                var results = jsonObject.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;

                for (var i = 0; i < results.length; i++) {
                    var li = document.createElement("li");

                    li.innerText = results[i].Cells.results[3].Value;

                    element.appendChild(li);
                }
            },
            error: function (data) {
            }
        });

        return false;
    });

Este ejemplo igualmente es similar en cuanto a la forma en la que se representan los datos a los anteriores. La diferencia aquí es que a la petición le estamos indicando las propiedades de los resultados obtenidos por las que queremos poder refinar (Línea 7). Una vez que se obtienen los resultados, se carga uno de los refinadores en un combo preparado para tal efecto (Líneas 22 a 31), este combo se carga automáticamente con los valores de refinamiento obtenidos.

Además se añade otro evento para cuando ese combo cambia, con la selección de un refinamiento, de manera que se haga el filtrado correspondiente. Para ello, solo se añade a la petición el refinementfilters con el contenido a filtrar (Línea 58).

Conclusiones

Trabajar con las búsquedas de SharePoint OnLine y 2013 nos ofrece una gran cantidad de oportunidades a la hora de desarrollar de una forma sencilla, la API REST del servicio de búsqueda es muy potente y merece la pena tenerla en cuenta. En la entrada de hoy, os he introducido al uso de la misma y os he mostrado un ejemplo de cómo usarla en una ejemplo de una aplicación para SharePoint.

Y esto es todo por hoy, espero que os resulte interesante. ¿Cuál es el siguiente paso?, bueno, como sabéis me gusta mucho Polymer, y creo que podría ser interesante crear un componente Polymer que use este servicio de búsqueda de forma completa. Espero en breve compartirlo con vosotros y ponerlo disponible para que lo podáis utilizar.

Un saludo y hasta la próxima