Tuesday, May 31, 2016

Improve SharePoint 2013 search relevance

You want to ensure that the search results that are returned to the user match what the user wanted to find and that the results that are returned on the first page are the most relevant, so the user does not have to look through several pages of results to find the best matches for their search ?

This is called Search Relevancy!

In this post we are going to learn how to customize SharePoint 2013 search relevance. We will see how to create and deploy custom ranking models and how to test them.

1 - What you need to do this tutorial
You need:
  • A SharePoint 2013 Enterprise on premise environment
  • Visual Studio 2013 installed on this environment

2 - Definition
The ranking model is an XML file that SharePoint uses to assign a rank for each search result . The display of results is made according to this rank. To adjust the relevance of research in SharePoint 2013 you thus need to write a custom ranking model and deploy it so that it takes into account the preferences of the user in matter of display of results .

3 - Start your ranking model customization based on a SharePoint ranking model template

To get the default ranking model template, log on any server in the SharePoint 2013 server farm with Administrator rights.
Create a directory, for example c: \ rankModel
Open a Powershell for SharePoint command prompt and enter the following lines:

$ssa = Get-SPEnterpriseSearchServiceApplication
$owner = Get-SPenterpriseSearchOwner -Level ssa
$defaultRankingModel = Get-SPEnterpriseSearchRankingModel -SearchApplication $ssa -Owner $owner | Where-Object { $_.IsDefault -eq $True }
$twoLinearStagesRankingModel.RankingModelXML > c:\rankmodel\defaultRankingModel.xml
Note: Microsoft recommends to use a two linear ranking model (it is a different and newer xml structure). To use it, you have, at least, to update your SharePoint Farm with the SharePoint Server 2013 cumulative update that was published in March 2014.

After having executed the previous SherPoint Powershell instructions, you will see appear the requested ranking model as an xml file in the c:\rankmodel folder.
Then, open the defaultRankingModel.xml, xml file into a text editor.

Note that the ranking model template is identified by an unique GUID.
4 - Test the ranking model template (first tool)
I developed a tool, the SPsearchRankingModelTester that can test the relevance of the search results according to a given ranking model (Thanks to Johan Olivier).
It is a console application.
This tool allows to easily test an application after having deployed a custom ranking model without having to register each time the new rankig model in search results web part which is tedious for testing.
You can find the package ready to use in CodePlex: SPSearchRankingModelTester-package .
You can find the source code of this application in the codePlex project, or in my GitHub repositery: SP2013searchRankingModelTester

4.1 Deploying SP2013searchRankingModelTester

Copy the downloaded package on a server of the SharePoint 2013 Farm, unzip it.
Modify the exe.config file: change the "value" attribute of the SPsiteAddress key and use the site collection where you want to test the search with a specific ranking model. For exemple, you can use the Url of a your SharePoint 2013 search center.

4.2 Using the SP2013searchRankingModelTester

Double click the .exe file.
The console application is opening and displaying your site collection Url (the one referenced in the exe.config file).
You are then, prompted for a request. Type your request.
Then, you are prompted for the GUID of your ranking model.

Make a right-click on the top of the window to paste the GUID you have previously copied from the xml file:

In the next screen shot, I use the GUID of the SharePoint 2013 default ranking model.
Once the ranking model GUID typed and the "enter" key pressed, the console application displays the 50th first relevant results according to the ranking model you specified.

5 - Tune your ranking model with rank features

There is several ways to customize a ranking model. The xml syntax and the ways to focus on a special point for relevance is not easy to understand. You can refer first to the official Microsoft documentation. I am first giving a quick overview of it, then I will explain how to test your changes with several tools.

Roughly you can act on:
  • BM25 rank feature ranks items
    You specify a property like "body" (the content of the crawled or indexed item) or "Title" (the title of the crawled or indexed item) and give that property a weight in order the relevance to more or less take into account this property.
    You can also specify the document length normalization for each property: a title is usually composed with few words although the body is rather long and the query is also usually composed with a few words. So you can tell your ranking model that a single occurence of one of the keywords of the query in the body has the same importance than a single occurence of one of the keywords of the query in the title

  • Weight groups
    You tune this in the Advanced Search Settings, using the search schema feature in the Search service application.
    For example, you create a new managed property of the type string that contains about ten words or less. You consider this new managed property to be about as important as the existing managed property Title. In that case, you should map the new managed property to context 1.
    Another example. You create a managed property of the type string that contains lots of words, for example a description of something. You should map this new managed property to context 7 because it is similar to the managed property Body, both in length as well as in importance.

  • static rank feature ranks items
    The static rank feature ranks items based on numeric managed properties that are stored in the search index. The numeric managed properties used for relevance rank calculation in static rank features must be of type Integer and set to Refinable or Sortable in the search schema. You can’t use multivalued managed properties in combination with the static rank feature.
    For example, UrlDepth is a typical static rank: the less url to reach the result is long, the more that result is relevant.
    Another example: you can make your content to be rated by users and make a static rank with this rating.

  • Bucketed static
    The bucketed static rank feature ranks documents based on their file type and language. You can tell your model that a .pdf in english is more relevant than a .docx in french.

  • Proximity rank feature
    The proximity rank feature ranks items depending on the distance between query terms inside the full-text index. The rank score is boosted if two query terms appear in the same managed properties within the full-text index. Proximity calculations are expensive in terms of disk activity and CPU consumption; as a result, proximity boost is carried out only during the second stage of the default SharePoint Server 2013 rank model (if available).

  • The dynamic rank feature
    It seems to use this if you want to boost a specific property:
    The dynamic rank feature ranks an item depending on whether the query property matches a given managed property. If there is a match, the item’s rank score is multiplied with a specific value to distinguish that particular item. The weight attribute is used to control how much this feature affects the overall rank score.
    The dynamic rank feature is not customizable; it’s for internal use only. However, if you install the SharePoint Server 2013 cumulative update of August 2013, the AnchortextComplete rank feature is a customizable dynamic rank feature that is part of the default ranking model.

  • Freshness
    The default SharePoint 2013 ranking model doesn’t boost the rank of search results based on their freshness. You can achieve this by adding a new static rank feature that combines information from the LastModifiedTime managed property with the DateTimeUtcNow query property, using the freshness transform function. The freshness transform function is the only transform that you can use for this freshness rank feature, because it converts the age of the item from an internal representation into days.

6 - Testing a custom ranking model

6.1 Description of the custom ranking model

In the Microsoft documentation, you will find two samples of customized ranking model. Let's show how to deploy and test the second one.

This ranking model with one linear stage contains these four rank features:
  • BM25 This rank feature is based on managed properties Title and body; the w attribute for title is set so that hits of query terms in Title are two times (2x) more important than hits of query terms in body.

  • UrlDepth This rank feature is based on the UrlDepth managed property, which is available by default in SharePoint installations. UrlDepth contains the number of backslashes (\) in the URL of a document. The inverse rational (InvRational) transform ensures that documents with shorter URLs receive higher rank scores.

  • TitleProximity This rank feature boosts documents if some of the query terms occur close to each other in the title of these documents.
    InternalFileType This rank feature boosts documents of type HTML, DOC, XLS, or PPT. The names of the buckets in the definition of the rank model are provided for readability only.
<?xml version="1.0"?>
<rankingmodel2stage name=" RankModel2"
                    description="Rank model -- example 2"
    <rankingmodel2nn id="A0A030D1-805D-437E-A001-CC151ED7473A" precalcEnabled="0">
        <hiddennodes count="1">
            <bm25main name="BM25" k1="1">
                    <property name="Title" propertyName="Title" w="2" b="0.5" />
                    <property name="body" propertyName="body" w="1" b="0.5" />
            <static name="UrlDepth" propertyName="UrlDepth" default="1">
                <transform type="InvRational" k="1.5"/>
            <minspan name="TitleProximity" propertyName="Title" default="0" maxMinSpan="1" isExact="0" isDiscounted="0">
                <normalize SDev="1" Mean="0"/>
                <transform type="Linear" a="1" b="-0.5" maxx="2"/>
            <bucketedstatic name="InternalFileType" propertyName="InternalFileType" default="0">
                <bucket name="http" value="0">
                <bucket name="doc" value="1">
                <bucket name="ppt" value="2">
                <bucket name="xls" value="3">

6.2 Custom ranking model deployment

Copy the previous xml in a file and name this file RankingModel2.xml. Copy this file in the previous folder c:\rankmodel.
Then, execute the following Powershell for SharePoint instructions:

$myRankingModel = Get-Content c:\rankmodel\ RankingModel2.xml
$myRankingModel = [String]$myRankingModel
$ssa = Get-SPEnterpriseSearchServiceApplication
$owner = Get-SPenterpriseSearchOwner -Level ssa
$newrm = New-SPEnterpriseSearchRankingModel -SearchApplication $ssa -Owner $owner -RankingModelXML $myRankingModel

Here is the screenshot for control:

6.3 testing the custom ranking model

To test the custom ranking model, we just have to use the previous tool. So double click on the .exe and this time after having typed the query, type the GUID of the custom ranking model:


You will see that the results are sorted in a completely different way accorded to the new and custom ranking model:

As I wrote before, understanding the syntax of the ranking model is a difficult work to achieve. By being able to test quickly the modifications of a custom ranking model you can improve your knowledge of the syntax, rules, formula and so on.

We are going to see more tools to do this...

7 - More tools to learn how to customize a SharePoint 2013 search ranking model

7.1 Tools overview and prerequisites

The two tools that we are going to demonstrate are Add-Ins for SharePoint ( formerly called Apps) .
I will not deny that the requirements to use these tools are difficult to carry out even for a SharePoint developer.
It takes a very good knowledge of SharePoint administration to use these tools because:
  • The Apps Settings for a SharePoint environment is difficult (especially on a local environment because of, for example, the creation the subdomain) and does not always work at the first time. You have sometimes to delete the service and recreate it for making it work.
  • One of these tools requires at least ( without it being mentioned in its documentation!), the activation of the User Profile Service Application that requires a lot of expertise ( especially for a local SharePoint 2013 environment)
However, here is a demonstration of tools and assets that can be drawn.

7.2 Mavention Explain Rank

SharePoint 2013 has the ExplainRank.aspx page which is a truly hidden gem, that displays the rankdetail for a single item in a more comprehensible way. However, this page is not working! (see Waldek Mastykarz's post on the subject.
Fortunately, there is a similar and free(!) tool (a SharePoint Add-In) that allows you to do the same.
This Add-In allows to query and for each result to understand the different values ​​assigned by the ranking model and explains its place in the ordering of results.
This is very educational when we want to understand how the ranking model is working.

Log in as Administrator on the research center of the SharePoint environment. Use the upper left menu to add the Add-In (app)

In the "Apps" search box enter the name of the publisher " Mavention " to display the catalog.

Locate « Explain Rank » Add-In.

Click the Add-In to deploy it and give it the appropriate permissions.

Once deployed, the add-in appears in usable the Add-In ( Apps) and can be used .

Using the "Explain Rank" Mavention Add-In

Open the Add- In home page and enter a query.

Start the query

Before each result a radio button helps to understand how the ranking model has awarded this rank for this result : what used properties and weight related to the presence of a particular word of the request in a particular property.

This allows us to better understand how a standard or a custom ranking model is acting in the ordering of results.

7.3 Ranking Model Tuning


This Add-in is more difficult to use than the previous one. It helps doing what we have done previously that is to mean "creating a custom ranking model", but in a guided manner , and without having to manipulate the xml.


Proceed as for the Add-In " Explain Rank " of Mavention , but instead of entering Mavention in the search box, enter the name of the Add-In " Ranking Model Tuning ".
Then install the Add-In .

Using the "Ranking Model Tuning" Add-In

The home page of the Add-In presents different ranking models that can serve as a basis to create a customized one. You just have to copy one to work on it.

Once the copy made we can start customizing the copied ranking model.

There are two macro- features : "Adding a judgment sets " or "Adding a rank features"

When you copy an existing ranking model, the new ranking model contains the same rank features and weights as in the base model. You can add more managed properties as additional rank features, remove existing features, or tune the weight of existing features.


This tool helps improve understanding of the xml of a ranking model since he automatically generates the xml based on instructions given by the Add-In UI. Then simply retrieving the new ranking model in xml format to understand how to transform the rules in a well formatted and consistent ranking model xml.
At best, the tool may be suitable for customisations needs of a ranking model without any need to work on the xml.
These various tools and practices can be used to define several approaches to constitute the ranking model appropriate for your needs, although it seems to require a large amount of time for testing and researching to achieve real expertise and mastery of the subject.


Saturday, November 7, 2015

Embed an Office Group Conversation into SharePoint Online using a JavaScript App, CORS, and the Office 365 Unified API

I am very happy to publish a post where I can use the brand new Office 365 unified API to embed a conversation of the brand new Office Group into a SharePoint Online page!

1 - Architecture
A SharePoint Online html page sends an Ajax request to the Office 365 Unified API and get the data of the Office Group.(The SharePoint Online site and the Office Group belong of course to the same Office 365 tenant).
It seems simple at first but it wasn't possible since recently. Why?
Because of the different hostheaders of the different part of Office 365, You can check while navigating through Office 365. For example, recently I created an Office 365 environment and first registered as contososoftware. My domain was contososoftware.onmicrosoft.com. Then I had to buy a domain to activate Yammer (You should have a verified domain in Office 365 to have Yammer activated to Enterprise. The "onmicrosoft.com" is not a domain but an Office 365 tenant name or a default domain and cannot be used to activate Yammer Enterprise. So I bought marccharmois.com.
(roughly 14 US$ a year, not a big deal). After that, when I go to
  • My mail, My calendar, My contacts (people), My Tasks, the Url starts with https://outlook.office.com/owa/?realm=marccharmois.com#path=
  • NewsFeed, OneDrive, Sites, Delve, Video, the Url starts with https://contososoftware-my.sharepoint.com/
  • Yammer, the Url starts with https://www.yammer.com/marccharmois.com/
  • a SharePoint Online site, the Url starts with https://contososoftware.sharepoint.com/
So if I want to deploy an html page somewhere Online in Office 365 and make an Ajax request to a part that has a different hostheader (for example displaying some of my mails into a SharePoint Online page), it normally doesn't work because crosss domain Ajax requests are forbidden by the browsers. It is worse if my HTML page is deployed on a server.
Cross-origin resource sharing (CORS) is a mechanism that allows that. CORS defines a way in which a browser and server can interact to safely determine whether or not to allow the cross-origin request. It allows for more freedom and functionality than purely same-origin requests, but is more secure than simply allowing all cross-origin requests. It is a recommended standard of the W3C. (Wikipedia)
Of course Microsoft sets up Cors within Office 365 in order developers to be allowed to performed Ajax requests without being bothered by the different hostheaders of Office 365.
The CORS of Office 365 is based on Microsoft Azure:
  • You register an App in Azure and you are provided with ID's that you place in your code.
  • In the Azure App, you also reference the Urls of the pages that are planned to call the Office 365 API with cross domain requests
This double registration allows the CORS of Office 365 to properly work and also manage the oauth authentication.

That leads to the fact that the term "Application" or App has a double meaning. It could be:
  • The pages that you have deployed somewhere on a server or within the Office 365 cloud (for example SharePoint Online) and that performs the Ajax requests and displays the data. Let's call this the physical part of the App, or the physical App
  • The Application that you had to add to Azure for getting the Office 365 CORS and the oauth authentication to work. Let's call that the virtual part of the App, or the virtual App
In the case of this tutorial, my physical App is just an html page added to a SharePoint Online team site using explorer mode (WebDAV protocol)
It is very similar of what I did when I embedded a Yammer conversation within SharePoint Online. This is super convenient because I can program and deploy quickly a physical App in Office 365 based on an html page that uses only Javascript!
It could seem a little bit difficult to understand at this point of the post, but I will explain all the operations to do it step by step with all the detailed screenshots as usual, so don't worry. I also did a diagram that can summarize all this in a clearer way:

2 - What you need to do this tutorial
You need:
Once you can access to an Office 365 environment using a business account with global administrator privileges, the first thing you need to do is register your application with Azure AD.

3 - Creating the (virtual) App within Microsoft Azure
3.1 Create a new subscription to Microsoft Azure
To Navigate to the Azure Portal you can use this link: https://manage.windowsazure.com/
you can also access to it within 0ffice 365:
Log on to Office 365. From the Home page, select the Admin icon to open the Office 365 admin center.

In the menu page along the left side of the page, scroll down to Admin and select Azure AD.

If prompted, log in using the credentials you created for your O365 subscription.

After logging in, you should see a screen notifying you that you do not have a subscription

Create a new subscription.
If you're using a trial version of Office 365, you'll see a message telling you that Azure AD is limited to customers with paid services. You can still create a trial 30-day Azure subscription at no charge, but you'll need to perform a few extra steps:
Select your country or region, and then choose Azure subscription.
Enter your personal information. For verification purposes, enter a telephone number at which you can be reached, and specify whether you want to be sent a text message or called.
Once you've received your verification code, enter it and choose Verify code.
Enter payment information, check the agreement, and select Sign up.
Your credit card will not be charged.
Do not close or refresh your browser while your Azure subscription is being created.
Once your Azure subscription is created, choose Portal.
The Azure Tour appears. You can view it, or choose X to close it.
You should now see all items in your Azure subscription. It lists a directory with the name of your Office 365 tenant.

3.2 Register your App in Azure Management Portal

Once signed in, follow these instructions:
Click the Active Directory node in the left column and select the directory linked to your Office 365 subscription.

Select the Applications tab and then Add at the bottom of the screen.

On the pop-up, select Add an application my organization is developing.

Choose an explicit name for your app,(I took Embed-OfficeGroup), and select Web application and/or web API as its Type. Then click the arrow to continue.

The value of Sign-on URL is the URL where your application will be hosted. As I will call the App using JavaScript within a SharePoint page, I put the Url of my SharePoint site.
The value of App ID URI is a unique identifier for Azure AD to identify your app. You can use http://{your_subdomain}/{YourAppName}, where {your_subdomain} is the subdomain of .onmicrosoft you specified while signing up for your Office 365 Developer Site. Then click the check mark to provision your application. For example, my Office 365 global admin account is, in this tutorial case:


Now that your app has been provisioned, select the Configure tab.

Scroll down to the permissions to other applications section and click the Add application button.

In this tutorial, we want our App to read the conversation of an Office group. To access to the Office Group we have to use the new Office 365 Unified API.
Click the plus sign in the application's row and then click the check mark at the top right to add it. Then click the check mark at the bottom right to continue.

In the Office 365 Unified API row, select Delegated Permissions, and in the selection list, choose Read All Groups.

Click Save to save the app's configuration.

3.3 Configure your app to allow the OAuth 2.0 implicit grant flow

In order to get an access token for Office 365 API requests, your application will use the OAuth implicit grant flow. You need to update the application's manifest to allow the OAuth implicit grant flow because it is not allowed by default.
Select the Configure tab of your application's entry in the Azure Management Portal.

Using the Manage Manifest button in the drawer, download the manifest file for the application and save it to your computer.

Open the manifest file with a text editor. Search for the oauth2AllowImplicitFlow property. By default it is set to false; change it to true and save the file.

Using the Manage Manifest button, upload the updated manifest file.

You've now successfully registered your application with Azure AD.

4 - Coding the SharePoint page (physical App) that's using the Azure App (Virtual App)

4.1 Creating the app.html page

In your 0ffice 365 environment, create a new site collection. Once it's done, navigate to the "Site Assets" library. Open the library in Explorer mode

and using the window navigate to the root folder of your site. Create an html page within the folder and name it app.html

You can now open this page with Visual Studio or Notepad ++ and start to programm the displaying of the Office Group conversation

You can find the complete app.html page in the dedicated Github repository

4.2 Determine the resource endpoint

In order to make any API requests, you'll need to determine the correct endpoint of the resource you want to use. The endpoint you'll use is determined by what information you want from Office 365. Refer to the API reference documentation to get the endpoint you want.
Mail API reference
Contacts API reference
Calendar API reference
Files API reference
Alternatively, you can take advantage of the Office 365 unified API (preview) to access all of the APIs from a single endpoint, https://graph.microsoft.com. Refer to the Office 365 unified API reference to browse all of the supported endpoints.
For this sample, we will use the Office 365 unified API to get a conversation of an Office Group. The endpoint for this operation is
https://graph.microsoft.com/beta/{your_domain}/groups, where {your_domain} is the domain you specified while signing up for your Office 365 Developer Site. In my case marccharmois.onmicrosoft.com.

My endpoint is: https://graph.microsoft.com/beta/marccharmois.onmicrosoft.com/groups

4.3 Get an access token from Azure

Office 365 uses OAuth 2.0 tokens issued by Azure AD to authenticate JavaScript clients. Tokens are obtained using the OAuth 2.0 implicit grant flow. Using implicit grant, your application requests an access token from Azure AD for the currently signed-in user by sending the user to an authorization URL where the user signs in with their Office 365 credentials and then is redirected back to the app with the access token in the URL.
The following function builds the authorization URL and navigates to it to begin the authentication process.

function requestToken() { 
  // Change clientId and replyUrl to reflect your app's values 
  // found on the Configure tab in the Azure Management Portal. 
  // Also change {your_subdomain} to your subdomain for both endpointUrl and resource. 
  var clientId    = 'e77659cc-bf72-4276-bd33-bdd876660a74';//ID of your App in Azure
  var replyUrl    = 'https://marccharmois.sharepoint.com/sites/intranet/app.html'; //my sharepoint page that requests an oauth 2 authentification and data
  //It is also referenced in the REPLY URL field of my App in Azure
  var endpointUrl = 'https://graph.microsoft.com/beta/marccharmois.onmicrosoft.com/groups';
  //var endpointUrl = 'https://marccharmois-my.sharepoint.com/_api/v1.0/me/files';//getting files from SharePoint
  //var endpointUrl = 'https://outlook.office.com/api/V1.0/me/messages';//getting messages  from outlook
  var resource = "https://graph.microsoft.com/";
  //var resource = "https://marccharmois-my.sharepoint.com"; //getting files from SharePoint
  //var resource = "https://outlook.office.com"; //getting messages  from outlook
  var authServer  = 'https://login.windows.net/common/oauth2/authorize?';  
  //var authServer  =  'https://login.microsoftonline.com/common/oauth2/authorize?';//this works either
  var responseType = 'token'; 

  var url = authServer + 
            "response_type=" + encodeURI(responseType) + "&" + 
            "client_id=" + encodeURI(clientId) + "&" + 
            "resource=" + encodeURI(resource) + "&" + 
            "redirect_uri=" + encodeURI(replyUrl); 

  window.location = url; 

At this point what's going on?
When I use this function, I am redirected to the same page, but with the acces token in the Url as a parameter:

I can even display the token

var urlParameterExtraction = new (function () { 
  function splitQueryString(queryStringFormattedString) { 
    var split = queryStringFormattedString.split('&'); 
    // If there are no parameters in URL, do nothing.
    if (split == "") {
      return {};
    var results = {}; 
    // If there are parameters in URL, extract key/value pairs. 
    for (var i = 0; i < split.length; ++i) { 
      var p = split[i].split('=', 2); 
      if (p.length == 1) 
        results[p[0]] = ""; 
        results[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " ")); 
    return results; 
  // Split the query string (after removing preceding '#'). 
  this.queryStringParameters = splitQueryString(window.location.hash.substr(1)); 

function displayToken(){
// Extract token from urlParameterExtraction object.
var token = urlParameterExtraction.queryStringParameters['access_token'];
alert('token : \n'+ token);

Now that I have the token, here is the code to request the Office 365 Unified API to get all the specific conversation of an Office Group:
function getToken(){

var token = urlParameterExtraction.queryStringParameters['access_token'];
return token;

function getGroupsFromO365() { 
    //var endpointUrl = 'https://graph.microsoft.com/beta/marccharmois.onmicrosoft.com/groups'; //getting all groups to get the ID of the group you want
 //var endpointUrl = 'https://marccharmois-my.sharepoint.com/_api/v1.0/me/files';//getting files from SharePoint
 //var endpointUrl = 'https://outlook.office.com/api/V1.0/me/messages';//getting messages from SharePoint
 var endpointUrl = "https://graph.microsoft.com/beta/contoso.com/groups('4eba7454-b490-46ae-bb5e-774efaec7c6f')/conversations('AAQkADZjOTdkMTIwLWFjNTItNDUyYy05MTc4LTg1NmJmMDk1MjYxOQAQAD_Ql6WI-GlHs3VmVZsV3QA=')/threads('AAQkADZjOTdkMTIwLWFjNTItNDUyYy05MTc4LTg1NmJmMDk1MjYxOQMkABAAP5CXpYj8aUezdWZVmxXdABAAP5CXpYj8aUezdWZVmxXdAA==')/Posts";

    var xhr = new XMLHttpRequest(); 
    xhr.open("GET", endpointUrl); 
    var myToken = getToken();
    // The APIs require an OAuth access token in the Authorization header, formatted like this: 'Authorization: Bearer '. 
    xhr.setRequestHeader("Authorization", "Bearer " + myToken); 

    // Process the response from the API.  
    xhr.onload = function () { 
      if (xhr.status == 200) { 
     //alert('data received');
  var message="";  
  var object = JSON.parse(xhr.response); 
  message+='From: ' + object.value[i].From.EmailAddress.Name + '<BR>';
  message+='At: ' + object.value[i].CreatedDateTime + '<BR>';  
  message+= object.value[i].Body.Content + '<BR>';  
        //var formattedResponse = JSON.stringify(JSON.parse(xhr.response), undefined, 2);
        document.getElementById("results").innerHTML = message;
      } else { 
        document.getElementById("results").textContent = "HTTP " + xhr.status + "<BR>" + xhr.response; 
    // Make request.
  catch (err) 
    document.getElementById("results").textContent = "Exception: " + err.message; 
Now, you can display this app.html into any SharePoint page by calling it within an iframe tag...
<span style="display:block;margin-bottom:3px;font-size;13px;">Embedded Office Group Conversation:</span> <iframe src="https://marccharmois.sharepoint.com/sites/intranet/app.html" style="height:400px;width=300px;border:solid 1px silver"></iframe>
As I did for this Web Part page;
5 - Aknowledgements