SharePoint y Azure - El Microsoft Bing Search API

Escrito por  Gustavo Velez

Que es el Microsoft Bing Search API

El servicio de búsqueda de Bing en Azure proporciona una experiencia similar a las búsquedas que se pueden realizar con Bing.com, devolviendo los resultados de una consulta que Bing determina son relevantes para el usuario. Los resultados incluyen páginas web, imágenes, vídeos y mucho más, como por ejemplo noticias, consultas relacionadas, corrección de errores de escritura, conversión de unidades, traducciones y cálculos matemáticos. El servicio forma parte del conjunto de Cognitive Services de Azure, que es una colección de algoritmos de Inteligencia Artificial que se pueden utilizar en la nube de Microsoft.

El Bing API es un servicio que consiste en un servicio general, el Bing Web Search, que devuelve resultados generales, de noticias, imágenes, videos, etc. relacionados con la consulta del cliente, y servicios especializados para cada una de esas partes que se pueden llamar por separado y retornan solamente los resultados relevantes (solo noticias, o solo videos, etc.). El servicio está basado en consultas REST, en donde la consulta se envía a un URL especializado (el Endpoint de Bing Search, https://api.cognitive.microsoft.com/bing/v7.0/search), con un parámetro "q" en el Query String con la cadena de consulta, y la llave del usuario de Azure en la cabecera del URL. La respuesta retorna en formato JSON.

Adicionalmente es posible refinar los resultados especificando un país, lo que devuelve los resultados de la búsqueda basándose en los intereses de ese país (para la parte de noticias, por ejemplo). La API de Bing Web Search soporta más de tres docenas de países, muchos con más de un idioma. El país se especifica mediante el parámetro de consulta "cc". Si se especifica un país, también se debe especificar uno o varios códigos de idioma mediante el encabezado HTTP Accept-Language. Los lenguajes soportados varían según el país, y se indican para cada país según su "mercado", que a su vez se puede especificar utilizando el parámetro de consulta "mkt" y un código especifico. Utilizando un mercado, simultáneamente se especifica un país y un idioma preferido. El parámetro de consulta "setLang" puede establecerse en un código de idioma en este caso; usualmente este es el mismo idioma especificado por "mkt" a menos que el usuario prefiera ver Bing en otro idioma. En castellano se pueden utilizar, por ejemplo, los países Argentina (AR), Chile (CL), México (MX) y España (ES), y los mercados Argentina-Español (es-AR), Chile-Español (es-CL), México-Español (es-MX) y España-Español (es-ES).

El Azure Bing Search API se puede utilizar en SharePoint para enriquecer automáticamente la información en el sistema. Aunque SharePoint dispone de su propio motor de búsqueda, él se reduce a indexar información dentro de SharePoint mismo, dentro de SharePoint OnPrem y un tenant de SharePoint 365 (en un sistema hibrido), o indexar sistemas conectados por medio del Business Connectivity Service (y, en ciertos casos, información en file-shares), pero no es capaz de relacionar información interna con información disponible en internet. Por medio del Bing Search API es posible crear este tipo de relaciones y, si es necesario, agregarle también funcionalidad adicional, como por ejemplo hacer cálculos matemáticos.

En el ejemplo que se va a desarrollar enseguida se utiliza el Bing Search API para mostrar información adicional sobre un nombre de un personaje. Una Lista Personalizada de SharePoint dispone de tres campos: uno (el Titulo por defecto) para el nombre del personaje, otro para mostrar un resumen de información al respecto, y un tercero con el vinculo correspondiente a la Wikipedia.

Configuración del Azure Bing Search API

Para utilizar el Bing Search API es necesario crear primero el servicio en Azure, aunque también es posible utilizar una cuenta temporal de prueba desde la página de Microsoft https://azure.microsoft.com/en-us/try/cognitive-services/#search. Para crear un servicio (de pago) en Azure:

1.       Entre al portal de manejo de Azure (https://portal.azure.com) utilizando sus credenciales.

2.       Vaya a la sección de "Resource Groups" y cree un nuevo Grupo de Recursos (también es posible reutilizar un grupo ya existente).

3.       Cree un servicio de "Bing Search API":

a.       En el Resource Group, utilice el botón de "+Add" para crear un recurso, busque por "Bing Search" en la casilla de búsqueda y seleccione "Bing Search v7 APIs" en los resultados.

b.       Asígnele un nombre al servicio y utilice el Grupo de Recursos deseado. En la casilla de "Pricing tier" seleccione un servicio dependiendo de la cantidad de consultas a esperar por segundo, lo que determina el precio del servicio (por mil consultas). Acepte el anuncio de privacidad que aparece en la configuración (Microsoft utilizara los datos enviados para mejorar automáticamente los algoritmos de Bing).

Imagen 1.- Creación del servicio de Bing Search API.

4.       Una vez creado el servicio, haga clic sobre su nombre en la lista de recursos del Resource Group, vaya a "Keys" y copie el valor de "Key 1"

Utilizando el Azure Bing Search API con SharePoint

En el siguiente ejemplo, como se indicó anteriormente, se va a utilizar una Lista Personalizada de SharePoint en donde el campo de "Titulo" de un elemento nuevo creado en la Lista, inicia una consulta de Bing. Cuando se introduce un nuevo elemento con el nombre de una personalidad en el Titulo de un elemento de la Lista, un WebHook hace que una Función de Azure comience a funcionar, utilice el texto del "Titulo" como consulta para hacer una llamada al Azure Bing Search API y modifique el elemento en la Lista agregándole el resumen de la información y el URL del articulo correspondiente de Wikipedia.

Nota: la creación y configuración de una Función de Azure se puede encontrar el en artículo "SharePoint y Azure – Azure Functions" (http://www.compartimoss.com/revistas/numero-30/sharepoint-y-azure-azure-functions). La configuración y utilización de WebHooks de SharePoint se puede encontrar en el artículo "Eventos sobre SharePoint Online con Webhooks" (http://www.compartimoss.com/revistas/numero-32/eventos-sobre-sharepoint-online-con-webhooks).

5.       Cree una cuenta de Funciones básica en el Grupo de Recursos, asignándole un nombre, Plan de Servicios y cuenta de Azure Storage.

6.       Utilizando Visual Studio 2017 (o Visual Studio 2016 con el AddIn para programar Funciones de Azure), cree una nueva solución del tipo "Azure Function". Una vez creada la solución, agréguele una Función del tipo "Http Trigger" con derechos de acceso anónimo.

7.       Agréguele a la solución el paquete NuGet "AppForSharePointOnlineWebToolkit".

8.       Reemplace toda la rutina "Run" con el siguiente código:

1[FunctionName("SearchBingWeb")]
2
3public static asyncTask<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
4
5{
6
7log.Info("\*\*\* SearchBingWeb function processed a request \*\*\*");
8
9
10
11// Registration
12
13stringvalidationToken = GetValidationToken(req);
14
15if (validationToken != null)
16
17{
18
19log.Info($"---- Processing Registration");
20
21var myResponse = req.CreateResponse(HttpStatusCode.OK);
22
23myResponse.Content = new StringContent(validationToken);
24
25return myResponse;
26
27}
28
29
30
31// Changes
32
33var myContent =
34await req.Content.ReadAsStringAsync();
35
36varallNotifications = JsonConvert.DeserializeObject<ResponseModel<NotificationModel>>(myContent).Value;
37
38
39
40if (allNotifications.Count > 0)
41
42{
43
44log.Info($"---- Processing Notifications");
45
46string siteUrl = ConfigurationManager.AppSettings["whSiteListUrl"];
47
48foreach (var oneNotification in allNotifications)
49
50{
51
52// Login in SharePoint
53
54ClientContext SPClientContext = HelpFunctions.LoginSharePoint(siteUrl);
55
56
57
58// Get the Changes
59
60GetChanges(SPClientContext, oneNotification.Resource, log);
61
62}
63
64}
65
66
67
68return newHttpResponseMessage(HttpStatusCode.OK);
69
70}
71
72
73
74return newHttpResponseMessage(HttpStatusCode.OK);
75
76}
77
78

Esta rutina primero se encarga de hacer el registro del WebHook (si la consulta contiene un parámetro "validationtoken" en el Query String) utilizando la rutina "GetValidationToken":

1public static stringGetValidationToken(HttpRequestMessage req)
2
3{
4
5string strReturn =
6string.Empty;
7
8
9
10strReturn = req.GetQueryNameValuePairs()
11
12.FirstOrDefault(q => string.Compare(q.Key,
13"validationtoken", true) == 0)
14
15.Value;
16
17
18
19return strReturn;
20
21}
22

En el código, después de registrado el WebHook, cada consulta es procesada para extraer las notificaciones que contiene. En cada notificación de la colección de notificaciones se hace un logeo en SharePoint para obtener los cambios detectados en la Lista (por medio de la rutina "GetChanges"). En la variable "whSiteListUrl" del App Settings de la función se encuentra el URL del sitio en donde se encuentra la Lista a examinar ("https://[Dominio].sharepoint.com/sites/[NombreSitio").

10.       La rutina "GetChanges" recibe el contexto de SharePoint y el identificador de la Lista, y tiene la forma:

1static voidGetChanges(ClientContext SPClientContext, string ListId, TraceWriter log)
2
3{
4
5// Get the List
6
7Web spWeb = SPClientContext.Web;
8
9List myList = spWeb.Lists.GetByTitle(ConfigurationManager.AppSettings["whListName"]);
10
11SPClientContext.Load(myList);
12
13SPClientContext.ExecuteQuery();
14
15
16
17// Create the ChangeToken and Change Query
18
19ChangeQuery myChangeQuery = GetChangeQueryNew(ListId);
20
21
22
23// Get all the Changes
24
25var allChanges = myList.GetChanges(myChangeQuery);
26
27SPClientContext.Load(allChanges);
28
29SPClientContext.ExecuteQuery();
30
31
32
33foreach (Change oneChange in allChanges)
34
35{
36
37if (oneChange is ChangeItem)
38
39{
40
41int myItemId = (oneChange as ChangeItem).ItemId;
42
43
44
45// Get what is changed
46
47log.Info($"---- Changed ItemId : " + myItemId);
48
49ListItem myItem = myList.GetItemById(myItemId);
50
51SPClientContext.Load(myItem);
52
53SPClientContext.ExecuteQuery();
54
55
56
57// Search result in Bing Web
58
59SearchBingWebResult myResultBing = GetBingWebSearch(myItem["Title"].ToString()).Result;
60
61
62
63// Insert the values back in the List
64
65int indexWeb = 0;
66
67for (int oneWeb = 0; oneWeb < myResultBing.webPages.value.Count(); oneWeb++)
68
69{
70
71if(myResultBing.webPages.value[oneWeb].name.Contains("Wikipedia") == true)
72
73{
74
75indexWeb = oneWeb;
76
77break;
78
79}
80
81}
82
83myItem["LinkWikipedia"] = myResultBing.webPages.value[indexWeb].displayUrl;
84
85myItem["Description"] = myResultBing.webPages.value[indexWeb].snippet;
86
87myItem.Update();
88
89SPClientContext.ExecuteQuery();
90
91log.Info($"---- Search Bing Web added to SharePoint Item");
92
93}
94
95}
96
97}
98

Primero se crea un objeto que contienen la Lista a utilizar en SharePoint. Luego se crea una consulta de cambio (variable "myChangeQuery") que especifica que se requieren los cambios ocurridos en el ultimo minuto, que ocurren en elementos de la Lista y que sean del tipo "Add", es decir, elementos nuevos:

1public static ChangeQuery GetChangeQueryNew(string ListId)
2
3{
4
5ChangeToken lastChangeToken = newChangeToken();
6
7lastChangeToken.StringValue = string.Format("1;3;{0};{1};-1", ListId, DateTime.Now.AddMinutes(-1).ToUniversalTime().Ticks.ToString());
8
9ChangeToken newChangeToken = newChangeToken();
10
11newChangeToken.StringValue = string.Format("1;3;{0};{1};-1", ListId, DateTime.Now.ToUniversalTime().Ticks.ToString());
12
13ChangeQuery myChangeQuery = newChangeQuery(false, false);
14
15myChangeQuery.Item = true;  // Get only Item changes
16
17myChangeQuery.Add = true;   // Get only the new Items
18
19myChangeQuery.ChangeTokenStart = lastChangeToken;
20
21myChangeQuery.ChangeTokenEnd = newChangeToken;
22
23
24
25returnmyChangeQuery;
26
27}
28

Luego de ejecutar la consulta, se examina cada uno de los cambios y se obtiene un objeto con el Elemento agregado. En la misma rutina se llama a "GetBingWebSearch", la que se encarga de hacer la consulta en Azure Bing, utilizando como parámetro de entrada el nombre del personaje (el Titulo del Elemento). Esta rutina entrega de regreso un objeto del tipo "SearchBingWebResult" que contiene los resultados de la consulta y que tiene la forma:

1public class SearchBingWebResult
2
3{
4
5public Webpages webPages { get; set; }
6
7}
8
9
10
11public class Webpages
12
13{
14
15public stringwebSearchUrl { get; set; }
16
17public inttotalEstimatedMatches { get; set; }
18
19public Value[] value { get; set; }
20
21}
22
23
24
25public class Value
26
27{
28
29public string id { get; set; }
30
31public string name { get; set; }
32
33public string url { get; set; }
34
35public About[] about { get; set; }
36
37public boolisFamilyFriendly { get; set; }
38
39public string displayUrl { get; set; }
40
41public string snippet { get; set; }
42
43public Deeplink[] deepLinks { get; set; }
44
45public DateTime dateLastCrawled { get; set; }
46
47public string language { get; set; }
48
49public stringthumbnailUrl { get; set; }
50
51}
52
53
54
55public class About
56
57{
58
59public string name { get; set; }
60
61}
62
63
64
65public class Deeplink
66
67{
68
69public string name { get; set; }
70
71public string url { get; set; }
72
73}
74

Como Bing retorna un resultado con varios elementos, por medio de un loop se busca el resultado que se refiere a la Wikipedia. Finalmente se utilizan los valores de "snipped" y "displayUrl" para insertarlos en los campos de "Description" y "LinkWikipedia" del Elemento.

11.       La rutina "GetBingWebSearch " recibe como parámetros de entrada el texto del título con el nombre del personaje y retorna un objeto con los valores encontrados por Bing:

1public static asyncTask<SearchBingWebResult> GetBingWebSearch(stringmySearchQuery)
2
3{
4
5SearchBingWebResult resultReturn = newSearchBingWebResult();
6
7// Construct the URI of the search request. Using the "en-US" market to force responce in english only
8
9var uriQuery = ConfigurationManager.AppSettings["azSearchBingWebEndpoint"] + "?mkt=en-US&q=" + Uri.EscapeDataString(mySearchQuery);
10
11stringcontentString = string.Empty;
12
13
14
15// Perform the Web request and get the response
16
17WebRequest request = HttpWebRequest.Create(uriQuery);
18
19request.Headers["Ocp-Apim-Subscription-Key"] = ConfigurationManager.AppSettings["azBingSearchApiServiceKey"];
20
21HttpWebResponse response = (HttpWebResponse)request.GetResponseAsync().Result;
22
23string json = newStreamReader(response.GetResponseStream()).ReadToEnd();
24
25
26
27// Create result object for return
28
29varsearchResult = new SearchResult()
30
31{
32
33jsonResult = json,
34
35relevantHeaders = newDictionary<String, String>()
36
37};
38
39
40
41// Extract Bing HTTP headers
42
43foreach (String header in response.Headers)
44
45{
46
47if(header.StartsWith("BingAPIs-") || header.StartsWith("X-MSEdge-"))
48
49searchResult.relevantHeaders[header] = response.Headers[header];
50
51}
52
53
54
55resultReturn = JsonConvert.DeserializeObject<SearchBingWebResult>(searchResult.jsonResult);
56
57returnresultReturn;
58
59}
60
61
62
63// Used to return search results including relevant headers
64
65struct SearchResult
66
67{
68
69public String jsonResult;
70
71publicDictionary<String, String> relevantHeaders;
72
73}
74

Cada consulta al Bing API se realiza por medio de una llamada REST a un URL pre-especificado del servicio de búsqueda (dado en el valor de la App Settings "azSearchBingWebEndpoint" y que es "https://api.cognitive.microsoft.com/bing/v7.0/search"), utilizando como parámetros en el QueryString el "Mercado" ("mkt", si es necesario forzar resultados en un idioma determinado) y la cadena de la consulta ("q"). En la App Settings "azBingSearchApiServiceKey" se mantiene el valor de la llave mencionada en el punto 4.

El Bing Web API es mucho más completo de lo que se muestra en el ejemplo. Por medio de los parámetros de la llamada REST se pueden especificar mucho más finamente los resultados, incluyendo determinar qué tan "fresca" es la información, filtrar el tipo de resultados, etc. Igualmente, el resultado que se usa en el ejemplo es solamente el objeto "Webpage", pero la respuesta puede contener muchos otros tipos incluyendo imágenes, videos, noticias, etc. Toda la definición de los parámetros de entrada y los objetos de salida se puede encontrar en la información de referencia de Microsoft en https://docs.microsoft.com/en-us/rest/api/cognitiveservices/bing-web-api-v7-reference. Para utilizar otros parámetros de entrada, modifique la consulta en la variable "uriQuery" de la rutina "GetBingWebSearch". Para entregar otros tipos de objetos de respuesta, modifique la clase "SearchBingWebResult" para que el objeto pueda capturar los valores deseados; la rutina "GetBingWebSearch" se encarga de deserializarlos automáticamente.

12.       Otras tres clases definen objetos utilizados por el WebHook:

1public class ResponseModel<T>
2
3{
4
5[JsonProperty(PropertyName = "value")]
6
7public List<T> Value {
8get; set; }
9
10}
11
12
13
14public class NotificationModel
15
16{
17
18[JsonProperty(PropertyName = "subscriptionId")]
19
20public stringSubscriptionId { get; set; }
21
22
23
24[JsonProperty(PropertyName = "clientState")]
25
26public string ClientState { get; set; }
27
28
29
30[JsonProperty(PropertyName = "expirationDateTime")]
31
32public DateTimeExpirationDateTime { get; set; }
33
34
35
36[JsonProperty(PropertyName = "resource")]
37
38public string Resource { get; set; }
39
40
41
42[JsonProperty(PropertyName = "tenantId")]
43
44public string TenantId { get; set; }
45
46
47
48[JsonProperty(PropertyName = "siteUrl")]
49
50public string SiteUrl { get; set; }
51
52
53
54[JsonProperty(PropertyName = "webId")]
55
56public string WebId { get; set; }
57
58}
59
60
61
62public class SubscriptionModel
63
64{
65
66[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
67
68public string Id { get; set; }
69
70
71
72[JsonProperty(PropertyName = "clientState", NullValueHandling = NullValueHandling.Ignore)]
73
74public string ClientState { get; set; }
75
76
77
78[JsonProperty(PropertyName = "expirationDateTime")]
79
80public DateTimeExpirationDateTime { get; set; }
81
82
83
84[JsonProperty(PropertyName = "notificationUrl")]
85
86public stringNotificationUrl { get; set; }
87
88
89
90[JsonProperty(PropertyName = "resource", NullValueHandling = NullValueHandling.Ignore)]
91
92public string Resource { get; set; }
93
94}
95

13.       Registre el WebHook en la Lista de SharePoint y cree un Elemento indicando en el titulo el nombre de un personaje. El WebHook hará que la Función realice su trabajo, entregue los resultados de Bing y muestre la descripción y el URL del artículo de la Wikipedia:

Imagen 2.- Personajes y su información utilizando el servicio de Bing API.

Conclusiones

El servicio de Bing de Azure permite enriquecer la información que los usuarios guardan en SharePoint. El Azure Bing API es fácil de utilizar desde cualquiera lenguaje de programación, y produce resultados confiables rápida y seguramente. El API utiliza algoritmos de Inteligencia Artificial que se mejoran con el uso, por lo no es necesario crear ni entrenar algoritmos propios.

Gustavo Velez MVP Office Servers and Services gustavo@gavd.net http://www.gavd.net

Siguemos en LinkedInSiguemos en Twitter
Powered by  ENCAMINA