Using Content Types in SharePoint with Search-Driven User Interfaces
Content Types are a fundamental building block in SharePoint. Every object you upload or create in a SharePoint list or library gets a Content Type assigned to it, whether you realize it or not. If you create an item in a list, by default, it gets the Item Content Type. If you upload a file to a library, it gets the Document Content Type. (Sure, there are some exceptions to this, but it applies to the majority of the content we add to SharePoint.)
If your information architecture (IA) is more mature (See: Management of Content in the Maturity Model for Microsoft 365), you will create your own Content Types, which represent the important business objects in your organization. Those Content Types should be named for the things you use every day and have conversations about: “We need to get a new Employment Contract in place with Isabel.” or “How many Company Policies need to be reviewed this month?”
Once you’ve built out your IA and have started assigning Content Types to your content, you’ll undoubtedly want to build some search-driven user interfaces (UIs). When we decide to include Site Columns in our Content Types, we should only add those Site Columns which will:
- Support process execution effectively, or
- Provide us with ways to display similar content which is stored across lists and libraries or sites.
When we do this, we often – in fact, almost always – turn to the PnP Modern Search Web Parts. For years, these community-built and supported Web Parts have been more powerful than the out of the box search experience in SharePoint. I install this solution for every single client I work with.
When we use PnP Modern Search, we often want to filter, retrieve, or group by Content Type in the UIs we build. Due to the vagaries of the underlying SharePoint Search engine, we may need to take some foundational steps to make Content Type as useful as we’d like it to be.
Filtering by Content Type Name
If you’d just like to filter using the value of Content Type, you should use the SPContentType
Managed Property. We can use SPContentType
in the query template we create to request the subset of content we’d like it to return.
Here’s an example:
{searchTerms} SPContentType:'Internal Policy'
This query goes in the Query template field in the PnP Search Results Web Part.
Content Type names are a bit more “brittle” (meaning the name of the Content Type could change over time) than the underlying ContentTypeId
s, but it’s a lot easier to read the query and know what we meant when we set things up.
Filtering by Content Type Id
When we first create a Content Type, it gets a ContentTypeId
which is unique to that particular Content Type. The ContentTypeId
is sort of like the InternalName
of a list column, but its Id
is constructed differently in that the ContentTypeId
is built up based on its ancestry. There’s an older article in Microsoft Learn which describes this in intricate detail, so I won’t rehash it here. See: Content Type IDs | Microsoft Learn.
For the purposes of this article, though, your query template might look something like this:
{searchTerms} ContentTypeId:0x010100AE45373F3F70FE4695EFCE3CCC53FD6E07*
If you deconstruct this ContentTypeId
, we can tell it represents a Content Type with Document as its parent, because it starts with 0x0101
.
Using the ContentTypeId
is most useful when you want to make your query less brittle (albeit less intelligible) and/or you’d like to take advantage of the Content Type inheritance model. In the example above, we use an asterisk in the query template so we can retrieve content with that ContentTypeId
or longer – the asterisk is a wildcard character. Each inheritance tacks on an additional unique section of characters, as shown in this image borrowed from the Microsoft Learn article above.
Displaying or Grouping by the Content Type
Sooner or later, you will want to display or group by the Content Type name. We can surely just display the SPContentType
, since we used it to filter above, right? Well, no. If you switch your PnP Search Results Web Part to the debug layout, you’ll see that SPContentType
always returns null
.
How does that make any sense? It doesn’t, but thanks to search guru Mikael Svenson and his brief but pithy article When query by content type name use SPContentType and not ContentType, at least we know why. If we look at the out -of-the-box set up for SPContentType
, we can see that it is Query-able, but NOT Retrieve-able. That means filtering works great, but when we try to get the value for other uses, we can’t.
Surely, there’s another existing Managed Property we can use, right? Well, no. Here are a few candidates, but you can probably see why none of them get us there: they are descriptive, but they aren’t the actual Content Type name. We strike out with contentClass
and ContentType
, though we can see they indicate similar information about the object. And we know that ContentTypeId
isn’t human-readable.
The workaround for this is to map the ows_ContentType
Crawled Property to one of the RefinableStringXX
Managed Properties. Here, I’ve chosen to use the ever-mnemonic RefinableString122
. Note that this Managed Property is Retrieve-able, but not Search-able! (Sometimes one wonders if they thought all this through when they first built it, eh?)
Once we have this Managed Property set up and percolated, we can display or group by the Content Type name.
Here’s what we see in the debug layout:
And here’s an example us grouping by the Content Type name value using RefinableString122
:
Giving us this nice result:
Summary
Content Types are indeed the best thing since sliced bread, but sometimes you need to be a master baker to get the most out of them.
One Comment