SPFx Developer: Working with MMS Values in SharePoint Search Results
Tags: sharepoint, spfx
Published: 2019-07-09 20:16:27

In this article I'll show how to use the Sharepoint Search Service to query by an MMS column. Then how to process the raw search result value in code in order to perform further logic on it (ex: group by tagged categories).

## Managed Property Setup
Assumes... 
 - You've already created a Managed Metadata Site Column
 - You've tagged some items with that Site Column
 - You've given the Search Service time to crawl that tagged content.

To query by our MMS column we need to setup the Search Managed Property in the SharePoint Admin Center.

```
https://YOURTENANT-admin.sharepoint.com/_layouts/15/searchadmin/ta_listmanagedproperties.aspx?level=tenant
```
Find and "Edit" the Managed Property that was automatically created when you setup your MMS Site Column. It should named like,`owstaxIdCOLUMN_NAME`.

Once you are on the Edit Screen for your Managed Property we need to:

1. Make it "Searchable", "Queryable" and "Retrievable"
2. Map the Crawled Properties
3. Setup an Alias

We typically map multiple crawled properties, one for just the term labels, and one for the full serialized term (GUID, label, termset etc...).  

![](https://thepracticaldev.s3.amazonaws.com/i/effcd8h9i36ety4wjtd0.PNG)

We also usually setup an alias that matches the Site Column's internal name. This makes the Search KQL query more readable.

![](https://thepracticaldev.s3.amazonaws.com/i/cnjx6g1vmir0iikfygkq.PNG)


## Searching by an MMS Value

You can use the SharePoint Search Service REST api to query data.  At [Skyline](https://www.skylinetechnologies.com/), we use the [SPScript](https://spscript.com/#/search) library to help us make calls to SharePoint Rest endpoints.  

If we had a managed property with an alias of `AssetLocation`, we can query for items with: 

``` typescript
import SPScript from "spscript"

async function fetchByAssetLocation(location: string) {
  let ctx = SPScript.createContext();
  let queryText = "AssetLocation:" + location;
  let queryOptions: any = {
    selectProperties: ["AssetLocation", "Path", "Title"],
  };
  let searchResponse = await ctx.search.query(queryText, queryOptions);
  return searchResponse.items;
}
```

## Parsing the Search Result MMS Value 

Assuming...
- The Managed Property was setup properly
- The data tagged with the MMS value has been crawled
- We've explicitly asked the Search Service to return our Managed Property via the `selectproperties` query option

We should expect to see our Search Result items having data for our MMS column in the following format.

*Here is an item tagged with 2 categories, Leadership and Solving Smarter*
```
Leadership

Solving Smarter

GP0|#0054b39a-0866-4e42-a1ba-ead38182a532

L0|#00054b39a-0866-4e42-a1ba-ead38182a532|Leadership

GTSet|#c69ac715-7977-4b11-8fdc-cf735f574607

GP0|#73bf5891-287a-498d-8e40-d8f7fafcf1e0

L0|#073bf5891-287a-498d-8e40-d8f7fafcf1e0|Solving Smarter
```

The data we need is all there but it is a little messy. It's just one big giant string. If we want to use tagged terms to perform any logic in code (ex: group items by their tag), we'll need to parse that raw string.

This is where regular expressions come in handy. I always get intimidated by Regex so I lean heavily on [https://regex101.com](https://regex101.com/).

Here is the thought process I used to build a regular expression that parses the raw search result MMS string.

Each line that starts with "L0" contains the Term Guid and the Term label. I want to just target those lines.  Each "L0" line is in this format:
```
L0|#0<TERM_GUID>|<TERM_LABEL>
```

To build up the Regular expression...

First target the beginning of the line, `LG|#0`. 
- The `|` is a special character so we have to escape it with a backslash.
- We add the `/g` flag so can grab all the matches instead of stopping once we find our first match.

``` javascript
let regex = /L0\|#0/g
```
![](https://thepracticaldev.s3.amazonaws.com/i/per8758wzt5t82hbixvn.PNG)

Next grab everything up to the next '|' character. 

- `[^\|]` means *a character that that ISN'T `|`*
- Adding a `+`, means *if there are multiple characters in a row like the one previously described, grab them all*.

``` javascript
let regex = /L0\|#0[^\|]+/g
```

![](https://thepracticaldev.s3.amazonaws.com/i/woqy7yvggmbv1jg75eq8.PNG)

To get the end of the line we expect a `|` and then a Term Label.

- `\|[^\n]+` means look for the pipe, followed by grabbing all characters that AREN'T a newline, `\n`

``` javascript
let regex = /L0\|#0[^\|]+\|[^\n]+/g
```

![](https://thepracticaldev.s3.amazonaws.com/i/tx6fe3nmya09d7kpooea.PNG)

The last thing to do is add "groups" to our Regular Expression so that for each match we can quickly get the Term Guid and Term Label part of the match.
- To add groups, we just wrap a section of the Regex with parenthesis

``` javascript
let regex = /L0\|#0([^\|]+)\|([^\n]+)/g
```

![](https://thepracticaldev.s3.amazonaws.com/i/4kg47n2npyqj4yxw0fat.PNG)
![](https://thepracticaldev.s3.amazonaws.com/i/9mpiabjasbcsnjom1zcf.PNG)

Now that we have a working Regular Expression, we can write a javscript function to execute our regular expression against the raw string value. 

For each match, we want to create a new "Term" object in the shape of `{ id, label }`.

``` typescript
function parseSearchResultMMSValue(value) {
  if (!value) return [];
  let regex = /L0\|#([^\|]+)\|([^\n]+)/g;
  let terms = [];
  let match;
  // repeatedly call 'exec' until we don't have anymore matches
  while ((match = regex.exec(value))) {
    // once we have a match, get at the group for the id and the label
    // index 0 is the full match, group1 is the guid, group2 is the label
    terms.push({
      id: match[1],
      label: match[2],
    });
  }
  return terms;
}
```