Skip to main content

Facets

Introduction​

A facet is a collection of values that a user can pick to narrow down a search. For example, a 'Creator' facet holds the creators of heritage objects, a 'Place of birth' facet holds the birth places of persons.

In this specification, the word facet always means a facet collection: a list of categorised values for narrowing a search. The 'Creator' facet is the list of all creators, not one creator. A facet is a collection of facet items; one facet item is one option in that list, such as 'Jan de Vries', and the thing a facet item points to is its value, such as an entity. Search engines use other words for the same things. Elasticsearch calls a facet an aggregation and the facet items buckets. Apache Solr calls a facet a facet field.

A presentation layer can browse the facet items in a facet collection, filter them, and ask for suggestions for them — just like any other collection. What makes a facet collection special is what its items hold: each item points to a value a user can use to narrow a search.

Facets are tied to a particular collection — the context collection — ensuring that results remain within the context of that collection. An integration layer MAY offer facets for any collection, except for a facet collection itself.

Facets are optional. An integration layer MAY implement them, depending on its requirements. An integration layer advertises the facet collections it supports for a collection in the generic facets property of that collection. The property points to the list of facet collections, which a presentation layer retrieves with the endpoint Retrieve the facet collections of a collection.

Data model​

NameDescription
CollectionAn ordered list of resources. See Resources.
Facet CollectionAn ordered list of facet items. Specialisation of Collection.
Facet PageAn ordered sublist of facet items within a Facet Collection. Specialisation of Page.
Facet ItemA selectable option within a Facet Collection or Facet Page, pointing to a resource.
ResourceA 'thing' of a certain type. Every value of a facet item is a resource. See Resources.
Facet ValueA value of a facet item, e.g. a value with the name 'Jan de Vries'. An integration layer uses a Facet Value to group several resources that have the same name into one facet item. It has no identity of its own.
Facet Range ValueA value of a facet item that covers a range, such as the years 1900 to 1950 or an age from 30 to 40. It has a name, a lower bound min and an upper bound max. It has no form of its own: a range value is either a Facet Date Range Value or a Facet Number Range Value. Specialisation of Facet Value.
Facet Date Range ValueA range of dates, such as the years 1900 to 1950. Its min and max are dates in ISO 8601. Specialisation of Facet Range Value.
Facet Number Range ValueA range of numbers, such as an age from 30 to 40. Its min and max are numbers. Specialisation of Facet Range Value.
EntityAn identifiable 'thing' relevant to heritage. See Entities.

The following class diagram visualises the data model:

Facet collections​

The integration layer determines which facet collections to support. For example, an integration layer that exposes information about...

  1. paintings defines the facet collection 'Technique', to categorise the techniques used for creating the works of art;
  2. military personnel defines the facet collection 'Military rank', to categorise the ranks of the persons;
  3. cars defines the facet collection 'Colour', to categorise the primary colours of the cars.

The following table lists some common facet collections:

For heritage objects:

NameDescription
TypeThe types of heritage objects, e.g. 'Building', 'Painting'.
CreatorThe creators of heritage objects, e.g. 'Vincent van Gogh', 'Rembrandt'.
Made in centuryThe dates of creation of heritage objects grouped by century, e.g. '17th century', '18th century'.
Made in placeThe places of creation of heritage objects, e.g. 'Amsterdam', 'The Hague'.
PublisherThe heritage institutions that publish information about heritage objects, e.g. 'Rijksmuseum', 'National Archives'.
Part of collectionThe collections a heritage object belongs to, e.g. 'Masterpieces', 'Great for kids'.

For persons:

NameDescription
Place of birthThe places of birth of persons, e.g. 'Amsterdam', 'The Hague'.
OccupationThe occupations of persons, e.g. 'Blacksmith', 'Mayor'.
PublisherThe heritage institutions that publish information about persons, e.g. 'Rijksmuseum', 'National Archives'.
Part of collectionThe collections a person belongs to, e.g. 'Notable Dutch artists'.

Facet items​

A facet item points to a value. The value is a resource, e.g. an entity, a collection, or a FacetValue. Every value has a name, but not every value has an id. That is what tells the kinds apart.

A resource with an id stands for one single thing. For example, an entity has an id, and that id belongs to one thing only. The count of the facet item is the number of occurrences of that one thing. In a 'Creator' facet, the entity 'Jan de Vries' with the ID https://example.org/v1/entities/1234 has a count of 12: twelve heritage objects have this person as their creator.

A FacetValue does not have an id: it does not stand for one single thing but for several. All of them have the same name. The count of the facet item is the total number of occurrences of all of them together.

This difference matters when several resources have the same name. In a 'Creator' facet, three different persons can all be called 'Jan de Vries'. An integration layer has two choices:

  • Use an entity for every person. The facet has three items. All three items have the name 'Jan de Vries'. Every item has its own id and its own count. A user sees the same name three times, and sees nothing that tells the three items apart.
  • Use one FacetValue for the name. The facet has one item, called 'Jan de Vries'. The item has no id. Its count is the sum of the occurrences of the three persons together.

An integration layer can make this choice for each facet collection separately. The choice is the same for every kind of value, since collections can have the same name as well.

A facet collection MUST use the same kind of value for all its items. This makes sure a presentation layer can present every item in the same way. A 'Creator' facet therefore uses either entities or FacetValues, not a mixture of the two.

A presentation layer shows the name of the value. It uses the id when it needs to point to one specific thing and to retrieve more information about it, e.g. about an entity or about a collection.

Range values​

A FacetRangeValue is a FacetValue that covers a range instead of one value. For example, it can cover the years 1900 to 1950 or an age from 30 to 40. It has a name for display, a lower bound min, and an upper bound max.

The type of a value says how to read its bounds. The min and max of a FacetDateRangeValue MUST be dates in ISO 8601. For example, '1900', '1900-01' and '1900-01-01' are all dates. The min and max of a FacetNumberRangeValue MUST be numbers.

A range value MUST include its lower bound, but not its upper bound. For example, the value '1900–1909' has a min of 1900-01-01 and a max of 1910-01-01: it covers 1 January 1900 up to, but not including, 1 January 1910. A range value MAY have no lower bound or no upper bound: the value 'before 1500' has no upper bound, and the value 'after 1900' has no lower bound. The integration layer MAY leave out values that have no occurrences.

A facet collection MUST NOT mix range values of different types, for example FacetDateRangeValues together with FacetNumberRangeValues. This makes sure a presentation layer can display the range values correctly. A facet collection MAY return its range values in any order. If the client does not ask for a particular order, the integration layer SHOULD return them from the lowest to the highest, so that a presentation layer can place them in the right order, for example on the axis of a histogram.

The name is the label of the value. The integration layer may use any text in it, such as '1900–1950', 'between 1900 and 1950', or '1900 to 1950'. A presentation layer MUST NOT read the bounds out of the name — it must use the min and max properties. A presentation layer can use the bounds to place every facet value on a scale. It can draw a histogram, with one bar per value from its lower bound to its upper bound. It can draw a timeline, with one band per value that shows how long the range lasts.

Endpoint: Retrieve the facet collections of a collection​

The endpoint retrieves all facet collections of a collection. The integration layer MUST implement this endpoint if it supports facets.

HTTP request​

GET /{version}/{...collections}/facets

Path parameters​

NameData typeCardinalityDescription
versionstring1The version of the API. Example: v1.
...collectionsstring1 or moreThe path identifier(s) of the context collection, including the collections it is part of or that it contains. Example: collections/objects.

Query parameters​

None.

Request body​

None.

Response body​

The response body MUST contain at least the following properties:

NameData typeCardinalityDescription
@contextstring1The URI of the JSON-LD context that maps the terms in the response to URIs.
idstring1The identifier of the collection.
typestring1The type of the collection. It MUST be Collection or a specialisation.
namestring1The name of the collection.
totalItemsnumber0 or 1The exact total number of facet collections. Not set if the exact total is too costly to calculate. Mutually exclusive with estimatedTotalItems.
estimatedTotalItemsnumber0 or 1An estimate of the total number of facet collections. It can be higher or lower than the real total. Not set if there is no estimate. Mutually exclusive with totalItems.
itemsarray1A list of the facet collections of the context collection.
items[*]FacetCollection1A facet collection.
items[*].idstring1The identifier of the facet collection.
items[*].typestring1The type of the facet collection. It MUST be FacetCollection or a specialisation.
items[*].namestring1The name of the facet collection.
belongsToCollection1The context collection that this is the list of facet collections for.
belongsTo.idstring1The identifier of the collection.
belongsTo.typestring1The type of the collection. It MUST be Collection or a specialisation.
belongsTo.namestring1The name of the collection.

The integration layer MUST NOT divide this collection into pages. The response body therefore does not contain first or last.

Example​

An example request from a presentation layer:

GET /v1/collections/objects/facets
Host: example.org

The request indicates that the integration layer should return all facet collections of a curated collection (objects).

An example of the response body of the integration layer:

{
"@context": "https://example.org/v1/context.json",
"id": "https://example.org/v1/collections/objects/facets",
"type": "Collection",
"name": "Facets",
"totalItems": 4,
"items": [
{
"id": "https://example.org/v1/collections/objects/facets/creators",
"type": "FacetCollection",
"name": "Creator"
},
{
"id": "https://example.org/v1/collections/objects/facets/centuries",
"type": "FacetCollection",
"name": "Made in century"
},
{
"id": "https://example.org/v1/collections/objects/facets/subjects",
"type": "FacetCollection",
"name": "Subject"
},
{
"id": "https://example.org/v1/collections/objects/facets/collections",
"type": "FacetCollection",
"name": "Part of collection"
}
],
"belongsTo": {
"id": "https://example.org/v1/collections/objects",
"type": "CuratedCollection",
"name": "Objects"
}
}

Endpoint: Retrieve a facet collection​

The endpoint retrieves a facet collection. The integration layer MUST implement this endpoint if it supports facets.

HTTP request​

GET /{version}/{...collections}/facets/{facet}

Path parameters​

NameData typeCardinalityDescription
versionstring1The version of the API. Example: v1.
...collectionsstring1 or moreThe path identifier(s) of the context collection, including the collections it is part of or that it contains. Example: collections/objects.
facetstring1The path identifier of the facet collection. Example: creators, centuries.

Query parameters​

NameData typeCardinalityDescription
qstring0 or 1A keyword query for filtering the facet items. Minimum length: defined by the integration layer (e.g. 3 characters). Maximum length: defined by the integration layer (e.g. 25 characters).
sizenumber0 or 1The maximum number of facet items to retrieve. Minimum: 1. Default: 10. Maximum: defined by the integration layer (e.g. 100).
orderBystring0 or 1The sorting order of the facet items. One of count, value. Default: count:desc (most frequent item first). The integration layer defines which property of the value it sorts by (e.g. the name). For a range value, the integration layer sorts by its min.
filterstring0 or moreThe rules for filtering the facet items. See Filters.
To do

Add the query parameters that carry the "search context" of the context collection, so that the facet items reflect the same search as the page in that collection (q and filter of endpoint Retrieve a page in a collection).

Request body​

None.

Response body​

The response body MUST contain at least the following properties:

NameData typeCardinalityDescription
@contextstring1The URI of the JSON-LD context that maps the terms in the response to URIs.
idstring1The identifier of the collection.
typestring1The type of the collection. It MUST be FacetCollection or a specialisation.
namestring1The name of the collection.
totalItemsnumber0 or 1The exact total number of facet items in the collection. Not set if the exact total is too costly to calculate. Mutually exclusive with estimatedTotalItems.
estimatedTotalItemsnumber0 or 1An estimate of the total number of facet items in the collection. It can be higher or lower than the real total. Not set if there is no estimate. Mutually exclusive with totalItems.
itemsarray0 or 1A list of the facet items in the collection. Not set if the collection is divided into pages.
items[*]FacetItem1A facet item.
items[*].typestring1The type of the facet item. It MUST be FacetItem.
items[*].countnumber1The number of occurrences of the value. If the value is a FacetValue, the count covers all resources that the integration layer grouped into it.
items[*].valueResource1The value of the facet item: an entity, a collection, or a facet value that groups several resources.
items[*].value.typestring1The type of the value of the facet item. It MUST be Resource or a specialisation. See Facet items.
items[*].value.idstring0 or 1The identifier of the value. Not set if the value is a FacetValue; a facet value has no identity.
items[*].value.namestring1The name of the value.
items[*].value.minstring0 or 1The lower bound of the range. Not set if the range has no lower bound, such as in a facet value 'before 1500'.
items[*].value.maxstring0 or 1The upper bound of the range. Not set if the range has no upper bound, such as in a facet value 'after 1900'.
firstFacetPage0 or 1The first page in the collection. Not set if the collection is empty or if it is not divided into pages.
first.idstring1The identifier of the first page in the collection.
first.typestring1The type of the first page in the collection. It MUST be FacetPage or a specialisation.
lastFacetPage0 or 1The last page in the collection. Not set if the collection is empty, if the collection is not divided into pages, or if the last page is unknown (e.g. in case of cursor navigation).
last.idstring1The identifier of the last page in the collection.
last.typestring1The type of the last page in the collection. It MUST be FacetPage or a specialisation.
partOfCollection1The collection that lists the facet collections of the context collection, including this one. See the response body of endpoint Retrieve the facet collections of a collection.
partOf.idstring1The identifier of the collection.
partOf.typestring1The type of the collection. It MUST be Collection or a specialisation.
partOf.namestring1The name of the collection.
suggestionsCollection0 or 1The list of this facet collection's suggestion collections. MUST be omitted if the facet collection does not offer suggestions. See Suggestions.
suggestions.idstring1The identifier of the list.
suggestions.typestring1The type of the list. It MUST be Collection or a specialisation.
suggestions.namestring1The name of the list.
capabilitiesarray0 or 1The URIs of the capabilities the integration layer implements for this collection. MUST be omitted if the collection supports no capabilities. See Capability discovery.

Example​

An example request from a presentation layer:

GET /v1/collections/objects/facets/centuries
Host: example.org

The request indicates that the integration layer should return a facet collection (centuries) of a curated collection (objects).

An example of the response body of the integration layer if the facet collection lists date ranges as its items:

{
"@context": "https://example.org/v1/context.json",
"id": "https://example.org/v1/collections/objects/facets/centuries",
"type": "FacetCollection",
"name": "Made in century",
"totalItems": 3,
"items": [
{
"type": "FacetItem",
"count": 41,
"value": {
"type": "FacetDateRangeValue",
"name": "17th century",
"min": "1601-01-01",
"max": "1701-01-01"
}
},
{
"type": "FacetItem",
"count": 27,
"value": {
"type": "FacetDateRangeValue",
"name": "18th century",
"min": "1701-01-01",
"max": "1801-01-01"
}
},
{
"type": "FacetItem",
"count": 12,
"value": {
"type": "FacetDateRangeValue",
"name": "19th century",
"min": "1801-01-01",
"max": "1901-01-01"
}
}
],
"partOf": {
"id": "https://example.org/v1/collections/objects/facets",
"type": "Collection",
"name": "Facets"
}
}

An example request from a presentation layer:

GET /v1/collections/objects/facets/creators
Host: example.org

The request indicates that the integration layer should return a facet collection (creators) of a curated collection (objects).

An example of the response body of the integration layer if the facet collection is not divided into pages and lists entities as its items:

{
"@context": "https://example.org/v1/context.json",
"id": "https://example.org/v1/collections/objects/facets/creators",
"type": "FacetCollection",
"name": "Creator",
"totalItems": 3,
"items": [
{
"type": "FacetItem",
"count": 12,
"value": {
"id": "https://example.org/v1/entities/1234",
"type": "Person",
"name": "Arno Haag"
}
},
{
"type": "FacetItem",
"count": 10,
"value": {
"id": "https://example.org/v1/entities/5678",
"type": "Person",
"name": "Arno Haag"
}
},
{
"type": "FacetItem",
"count": 8,
"value": {
"id": "https://example.org/v1/entities/9012",
"type": "Person",
"name": "Hans de Haan"
}
}
],
"partOf": {
"id": "https://example.org/v1/collections/objects/facets",
"type": "Collection",
"name": "Facets"
}
}

An example of the response body of the integration layer for the request above, if the facet collection lists FacetValues as its items:

{
"@context": "https://example.org/v1/context.json",
"id": "https://example.org/v1/collections/objects/facets/creators",
"type": "FacetCollection",
"name": "Creator",
"totalItems": 2,
"items": [
{
"type": "FacetItem",
"count": 22,
"value": {
"type": "FacetValue",
"name": "Arno Haag"
}
},
{
"type": "FacetItem",
"count": 8,
"value": {
"type": "FacetValue",
"name": "Hans de Haan"
}
}
],
"partOf": {
"id": "https://example.org/v1/collections/objects/facets",
"type": "Collection",
"name": "Facets"
}
}

An example request from a presentation layer:

GET /v1/collections/objects/facets/collections
Host: example.org

The request indicates that the integration layer should return a facet collection (collections) of a curated collection (objects).

An example of the response body of the integration layer if the facet collection lists collections as its items:

{
"@context": "https://example.org/v1/context.json",
"id": "https://example.org/v1/collections/objects/facets/collections",
"type": "FacetCollection",
"name": "Part of collection",
"totalItems": 2,
"items": [
{
"type": "FacetItem",
"count": 80,
"value": {
"id": "https://example.org/v1/collections/masterpieces",
"type": "CuratedCollection",
"name": "Masterpieces"
}
},
{
"type": "FacetItem",
"count": 34,
"value": {
"id": "https://example.org/v1/collections/great-for-kids",
"type": "CuratedCollection",
"name": "Great for kids"
}
}
],
"partOf": {
"id": "https://example.org/v1/collections/objects/facets",
"type": "Collection",
"name": "Facets"
}
}

The response indicates that a user can narrow the search down to the resources in one of the collections.

An example of the response body of the integration layer for the request above, if the facet collection is divided into pages:

{
"@context": "https://example.org/v1/context.json",
"id": "https://example.org/v1/collections/objects/facets/creators",
"type": "FacetCollection",
"name": "Creator",
"totalItems": 195,
"first": {
"id": "https://example.org/v1/collections/objects/facets/creators?page=1",
"type": "FacetPage"
},
"last": {
"id": "https://example.org/v1/collections/objects/facets/creators?page=20",
"type": "FacetPage"
},
"partOf": {
"id": "https://example.org/v1/collections/objects/facets",
"type": "Collection",
"name": "Facets"
},
"capabilities": ["https://specs.nde.nl/rest/v1/page-pagination"]
}

Endpoint: Retrieve a page in a facet collection​

The endpoint retrieves a page in a facet collection. The integration layer MUST implement this endpoint for every facet collection it divides into pages.

HTTP request​

GET /{version}/{...collections}/facets/{facet}?page={page}

Path parameters​

NameData typeCardinalityDescription
versionstring1The version of the API. Example: v1.
...collectionsstring1 or moreThe path identifier(s) of the context collection, including the collections it is part of or that it contains. Example: collections/objects.
facetstring1The path identifier of the facet collection. Example: creators, centuries.

Query parameters​

NameData typeCardinalityDescription
pagestring1The identifier of the page: a page number or cursor, depending on the pagination strategy of the integration layer.
qstring0 or 1A keyword query for filtering the facet items. Minimum length: defined by the integration layer (e.g. 3 characters). Maximum length: defined by the integration layer (e.g. 25 characters).
sizenumber0 or 1The maximum number of facet items to retrieve. Minimum: 1. Default: 10. Maximum: defined by the integration layer (e.g. 100).
orderBystring0 or 1The sorting order of the facet items. One of count, value. Default: count:desc (most frequent item first). The integration layer defines which property of the value it sorts by (e.g. the name). For a range value, the integration layer sorts by its min.
filterstring0 or moreThe rules for filtering the facet items. See Filters.
To do

Add the query parameters that carry the "search context" of the context collection, so that the facet items reflect the same search as the page in that collection (q and filter of endpoint Retrieve a page in a collection).

Request body​

None.

Response body​

The response body MUST contain at least the following properties:

NameData typeCardinalityDescription
@contextstring1The URI of the JSON-LD context that maps the terms in the response to URIs.
idstring1The identifier of the current page.
typestring1The type of the page. It MUST be FacetPage or a specialisation.
namestring1The name of the page.
itemsarray1A list of facet items.
items[*]FacetItem1A facet item.
items[*].typestring1The type of the facet item. It MUST be FacetItem.
items[*].countnumber1The number of occurrences of the value. If the value is a FacetValue, the count covers all resources that the integration layer grouped into it.
items[*].valueResource1The value of the facet item: an entity, a collection, or a facet value that groups several resources.
items[*].value.typestring1The type of the value of the facet item. It MUST be Resource or a specialisation. See Facet items.
items[*].value.idstring0 or 1The identifier of the value. Not set if the value is a FacetValue; a facet value has no identity.
items[*].value.namestring1The name of the value.
items[*].value.minstring0 or 1The lower bound of the range. Not set if the range has no lower bound, such as in a facet value 'before 1500'.
items[*].value.maxstring0 or 1The upper bound of the range. Not set if the range has no upper bound, such as in a facet value 'after 1900'.
prevFacetPage0 or 1The previous page in the collection. Not set if there is no previous page.
prev.idstring1The identifier of the previous page in the collection.
prev.typestring1The type of the previous page in the collection. It MUST be FacetPage or a specialisation.
nextFacetPage0 or 1The next page in the collection. Not set if there is no next page.
next.idstring1The identifier of the next page in the collection.
next.typestring1The type of the next page in the collection. It MUST be FacetPage or a specialisation.
partOfFacetCollection1The collection to which the items contained by the page belong. See the response body of endpoint Retrieve a facet collection.
partOf.idstring1The identifier of the facet collection.
partOf.typestring1The type of the facet collection. It MUST be FacetCollection or a specialisation.
partOf.namestring1The name of the facet collection.
partOf.totalItemsnumber0 or 1The exact total number of items in the facet collection. Not set if the exact total is too costly to calculate. Mutually exclusive with partOf.estimatedTotalItems.
partOf.estimatedTotalItemsnumber0 or 1An estimate of the total number of items in the facet collection. It can be higher or lower than the real total. Not set if there is no estimate. Mutually exclusive with partOf.totalItems.
partOf.firstFacetPage1The first page in the collection.
partOf.first.idstring1The identifier of the first page in the collection.
partOf.first.typestring1The type of the first page in the collection. It MUST be FacetPage or a specialisation.
partOf.lastFacetPage0 or 1The last page in the collection. Not set if the last page is unknown (e.g. in case of cursor navigation).
partOf.last.idstring1The identifier of the last page in the collection.
partOf.last.typestring1The type of the last page in the collection. It MUST be FacetPage or a specialisation.

Example​

An example request from a presentation layer:

GET /v1/collections/objects/facets/creators?page=3
Host: example.org

The request indicates that the integration layer should return page 3 in a facet collection (creators) of a curated collection (objects).

An example of the response body of the integration layer:

{
"@context": "https://example.org/v1/context.json",
"id": "https://example.org/v1/collections/objects/facets/creators?page=3",
"type": "FacetPage",
"name": "Creator",
"items": [
{
"type": "FacetItem",
"count": 12,
"value": {
"id": "https://example.org/v1/entities/1234",
"type": "Person",
"name": "Arno Haag"
// Optionally: other properties...
}
},
{
"type": "FacetItem",
"count": 8,
"value": {
"id": "https://example.org/v1/entities/9012",
"type": "Person",
"name": "Hans de Haan"
// Optionally: other properties...
}
}
// Other items...
],
"prev": {
"id": "https://example.org/v1/collections/objects/facets/creators?page=2",
"type": "FacetPage"
},
"next": {
"id": "https://example.org/v1/collections/objects/facets/creators?page=4",
"type": "FacetPage"
},
"partOf": {
"id": "https://example.org/v1/collections/objects/facets/creators",
"type": "FacetCollection",
"name": "Creator",
"totalItems": 195,
"first": {
"id": "https://example.org/v1/collections/objects/facets/creators?page=1",
"type": "FacetPage"
},
"last": {
"id": "https://example.org/v1/collections/objects/facets/creators?page=20",
"type": "FacetPage"
}
}
}