Details
Let's say you want to index the posts of a WordPress blog.
The following is a truncated response example from the WordPress API.
The main object represents a blog post.
```json
{
"id": 12345,
"date_gmt": "2024-03-11T15:43:43",
"slug": "coveo-test-page-wp",
"status": "publish",
"link": "https://example.com/coveo-test-page-wp/",
"title": {
"rendered": "Coveo Test Page WP Edited Content"
},
"content": {
"rendered": "This is a sample wordpress page to test integration with Coveo.
\nHeklsjfldsfj;ds
\ndsfjlkdskjafklds
\nlkfjdslkfjldk
\n",
"protected": false
},
"author": 4321,
"_embedded": {
"author": [
{
"id": 4321,
"name": "John Smith",
"url": "",
"description": "",
"link": "https://example.com/author/00u1o7cop0d6hhecl0h8/",
"slug": "00u1o7cop0d6hhecl0h8",
}
],
"wp:term": [
[
{
"id": 73,
"link": "https://example.com/category/docs-testing/",
"name": "DocsTesting",
"taxonomy": "category"
}
],
[
{
"id": 242,
"link": "https://example.com/tag/abc-xyz/",
"name": "abc-xyz",
"taxonomy": "post_tag"
},
{
"id": 243,
"link": "https://example.com/tag/coveo-test-wp/",
"name": "coveo-test-wp",
"taxonomy": "post_tag"
}
],
]
}
}
```
In your search interface, you want to be able to filter posts by tag.
So, when indexing a blog post, you also want to index a list of its tags as metadata.
To do so, you use a JSONPath expression, in which you add a filter expression based on the `taxonomy` property to exclude category names.
In your source JSON configuration, the [`Metadata`](https://docs.coveo.com/en/1525#metadata-object) object may therefore look as follows:
```json
"Metadata": {
"author": "%[_embedded.author[0].name]",
"date": "%[date_gmt]",
"wordpress_status": "%[status]",
"id": "%[id]",
"wordpress_tag_names": "%[_embedded.wp:term..[?(@.taxonomy=='post_tag')].name]"
}
```
As a result, Coveo will index the tags associated with each blog post in the `wordpress_tag_names` field.
In the [**Content Browser**](https://platform.cloud.coveo.com/admin/#/orgid/content/browser/) ([platform-ca](https://platform-ca.cloud.coveo.com/admin/#/orgid/content/browser/) | [platform-eu](https://platform-eu.cloud.coveo.com/admin/#/orgid/content/browser/) | [platform-au](https://platform-au.cloud.coveo.com/admin/#/orgid/content/browser/)), this should look as follows:

However, since you want Coveo to consider these values separately, you must [enable the **Multi-value facet** option](https://docs.coveo.com/en/1833#facet-and-multi-value-facet) for the `wordpress_tag_names` field.
As a result, the tags will be displayed separately in the facet, like so:

Otherwise, you'll see the tags as a single string in the facet, for example: `abc-xyz;coveo-test-wp`.
> **Tip**
>
> Use the [JSONPath Online Evaluator](https://jsonpath.com/) to test your JSON paths.
If an application field targeted by your JSON path is missing or empty, Coveo ignores it.
The rest of the content to index, if applicable, is indexed normally.
For example, let's say your source JSON configuration contains the following property: `"blogpostcomment": "%[subject] - %[message]"`.
If, in an item, the `subject` field contains `Hello world!` and the `message` field is empty, the content indexed in the `blogpostcomment` Coveo field will be: `Hello world! - `.
### `coveo_parent`
`coveo_parent` always appears with a [JSON path](#json-path).
In a [source configuration](https://docs.coveo.com/en/1525/), it's used to instruct the Coveo crawler to refer to the parent item `Metadata` properties, and then to retrieve the value of the desired property.
So, when writing the JSON configuration to retrieve data regarding a sub-item, if you want to get a metadata that you specified earlier in your JSON configuration for the parent object, your dynamic value syntax should be the following:
`"CoveoFieldName": %[coveo_parent.MetadataFieldName]`
**Example**
You use the Vimeo API to index user profiles, and then the videos uploaded by each user as sub-items associated to their profile.
You therefore first make an API call to retrieve user profiles, and use it to build your [endpoint](https://docs.coveo.com/en/1525#endpoints-array-required) configuration.
The API response contains, among other pieces of metadata, the user's ID: `"userid": "jsmith01"`.
In your user profile endpoint configuration, you therefore indicate:
```json
"username": "%[userid]"
```
As a result, for user profiles, the `userid` provided by the Vimeo API is stored in the Coveo index as the value of the `username` field.
Then, you make another API call to retrieve videos, and use the API response to build the endpoint `Subitems` section of your JSON configuration.
You would like to index, for each video, the username of the user who originally uploaded it, but the video endpoint doesn't provide this information.
You therefore need to instruct the Coveo crawler to retrieve it from the parent item configuration, that is, the user profile endpoint configuration.
In your `Subitems` configuration, you include:
```json
"uploadedby": "%[coveo_parent.username]"
```
This instructs the Coveo crawler to refer to the `Metadata` of the parent item in your JSON configuration, to retrieve the value of the `username` property, and to index it as the value of the `uploadedby` field for videos.
In Coveo, user IDs will then appear as the `username` value of user profiles and as the `uploadedby` value of videos.
A dynamic value can also contain more than one `coveo_parent`, if needed.
**Example**
`"CoveoFieldName": %[coveo_parent.coveo_parent.MetadataFieldName]` refers to the parent of the parent of a sub-item.
> **Note**
>
> `coveo_parent` alone followed by a JSON path indicates to the Coveo crawler that it must go back up in your JSON configuration, under the `Metadata` object, to find the specified property.
> However, if you didn't specify the desired property in the `Metadata` of the parent object, you can use [`raw`](#raw) to retrieve it from the API response you obtained before for the parent item.
In a [permission configuration](https://docs.coveo.com/en/3303/), `coveo_parent` instructs the Coveo crawler to retrieve the value of the desired property from the [permission subquery](https://docs.coveo.com/en/1525#permissionsubqueries-array) [`AdditionalInfo`](https://docs.coveo.com/en/1525#additionalinfo-object) properties.
So, once you've retrieved the security identities associated with each item through the permission subquery, you must use the [permission configuration](https://docs.coveo.com/en/3303/) and `coveo_parent` to extract the identities' [relationships](https://docs.coveo.com/en/1618/).
### `raw`
In the metadata of a sub-item, you may want to refer to a property that was returned in the API JSON response for the parent item.
If you specified this property before in your JSON configuration for the parent item metadata, you can retrieve it from your own configuration with [`coveo_parent`](#coveo_parent) alone.
If you didn't specify it, however, you must instruct Coveo to retrieve it from the API response returned for the parent item by adding `raw` in the JSON path leading to the desired property.
**Example**
When implementing [result folding](https://docs.coveo.com/en/1884/), you want a sub-item to have its parent item URI in the `foldingparent` field.
In your sub-item configuration, you therefore write:
```json
"foldingparent": "%[coveo_parent.uri]"
```
However, you didn't include the `uri` property in the parent item endpoint configuration, as it was irrelevant at this point.
So, you must add `raw` to your sub-item property value:
```json
"foldingparent": "%[coveo_parent.raw.uri]"
```
This instructs Coveo to retrieve the parent item URI from the metadata provided in the API JSON response rather than from the parent item endpoint configuration you wrote earlier.
> **Note**
>
> When a JSON path leading to the desired property in the API response is too long or complicated, you can choose to avoid using the raw metadata of the parent item, and to rather include the property in the parent item endpoint configuration to give it a more suitable JSON path.
> In your child item `Metadata`, you can then use `coveo_parent` and the new JSON path.
>
> **Example**
>
> The API JSON response provides the following user profile metadata:
>
> ```json
{
"uri": "/users/jsmith01",
"name": "John Smith",
"location": "Winnipeg, Canada",
"bio": "I am a tax expert and I like to share videos of my lovely dog!",
"personalwebsite": {
"name": "John Smith's Personal Website",
"link": "http://www.johnsmith.ca",
"description": "Visit my personal website for funny photos of my basset hound running!"
},
"professionalwebsite": {
"name": "John Smith's Professional Website",
"link": "http://www.taxseasonsgreetings.ca",
"description": "Visit my professional website to discover my tips and tricks for filing taxes. Happy tax filing!"
}
}
```
>
> You choose not to index website links in user profiles.
> Your configuration therefore includes:
>
> ```json
"title": "%[name]",
"uri": "%[uri]",
"location": "%[location]",
```
>
> You also want to index videos as sub-items of user profiles.
> In Coveo, you want the metadata of video items to include the author's personal website.
> Your sub-item endpoint configuration should therefore include:
>
> ```json
"website": "%[coveo_parent.raw.personalwebsite.link]"
```
>
> However, you think that this dynamic value is too long and prefer to avoid it so that reading and interpreting your JSON configuration remains effortless.
> So, you add `"website": "%[personalwebsite.link]"` to the user profile endpoint JSON configuration, that is, the parent item configuration, and, in the video item configuration, you include:
>
> ```json
"website": "%[coveo_parent.website]"
```
>
> By doing so, you change the JSON path leading to the desired value for a more simple one.
> Although not necessary, it may be more convenient.
### Whitespace characters
Depending on the content repository to index, your dynamic values may contain whitespace characters.
In such case, the syntax to use is slightly different: the property name must be enclosed in simple quotes and square brackets in addition to the regular syntax, for example, `"%[['property with whitespace']]"` as opposed to `"%[property]"` in the regular syntax.
The following table shows the syntax to use based on the scenario.
[%header,cols="~,~"]
|===
|Scenario
|Syntax to use
|No whitespace (regular syntax)
|`"%[property]"`
|Single property
|`"%[['property with whitespace']]"`
|Property with whitespace [nested within another property](#json-path)
|`"%[property.['property with whitespace']]"`
|Property with whitespace [nested within another property with whitespace](#json-path)
|`"%[['property with whitespace'].['property with whitespace']]"`
|Complex expression and property with whitespace
|`"%[['property with whitespace'][?(@.type=='CATEGORY')].name]"`
|===
### Multi-value fields
The examples above show JSON paths leading to properties that have a single value.
However, the API's JSON response may contain arrays in which several objects have a property in common.
In such a case, you may want to retrieve some or all of the values associated to this property and [enable the **Multi-value facet** option](https://docs.coveo.com/en/1833#facet-and-multi-value-facet) to populate a single Coveo field with these values.
You must therefore write your source configuration accordingly, so that Coveo indexes the desired content.
To populate a Coveo field with many values, use the [dynamic value](#dynamic-values) syntax (`"CoveoFieldName": "%[DynamicValue]"`) with, inside the square brackets, a [JSONPath expression](https://github.com/dchester/jsonpath#jsonpath-syntax).
When writing a typical [JSON path](https://github.com/dchester/jsonpath#jsonpath-syntax) to populate a Coveo field with many values:
* Specify the objects to take into account between square brackets next to the array name.
* Use a `*` character to represent all objects in an array, even if there's only one object.
* If you want to specify certain objects only, decrement the desired object place by one, that is, use `0` to refer to the first object in the array, use `1` to refer to the second object, and so on.
* Use commas to separate the values to which you want to refer.
As a result, `"CoveoFieldName": "%[path.object1[*].object2.property]"` would populate the `CoveoFieldName` field with the `property` value of all `object1` objects found under `path`.
**Example**
The API returns the following JSON response, providing four sizes of the same picture.
```json
{
...
"name": "Vimeo Holiday Videos!",
"pictures": {
"uri": "/videos/148903960/pictures/548505676",
"active": true,
"type": "custom",
"sizes": [
{
"width": 100,
"height": 75,
"link": "https://i.vimeocdn.com/video/54855654705676_100x75.jpg?r=pad"
},
{
"width": 200,
"height": 150,
"link": "https://i.vimeocdn.com/video/5485567705676_200x150.jpg?r=pad"
},
{
"width": 295,
"height": 166,
"link": "https://i.vimeocdn.com/video/1231234_295x166.jpg?r=pad"
},
{
"width": 640,
"height": 360,
"link": "https://i.vimeocdn.com/video/548545654605676_640x360.jpg?r=pad"
}
]
}
...
}
```
In the Coveo `width` field, you want to list the first two available widths for this picture.
Under `metadata`, your JSON configuration therefore contains the following metadata:
```json
"width": "%[pictures.sizes[0,1].width]"
```
In Coveo, the `width` field will contain the following information: `100;200`.
However, in the Coveo `pictureuri` field, you only want to have the link to the largest version of the picture.
Your JSON configuration therefore contains the following metadata:
```json
"pictureuri": "%[pictures.sizes[3].link]"
```
**Example**
The API returns a JSON response containing the `websites` array, which includes only one website object.
```json
{
...
"websites": [
{
"name": null,
"link": "http://www.canadashistory.ca",
"description": null
}
]
...
}
```
You want a website link to appear in the Coveo `website` field.
Since there's only one link value in your JSON response, your JSON configuration therefore contains the following metadata.
You can't omit the `[*]`, even if there's only one object in the array.
```json
"website": "%[websites[*].link]"
```
If there were more than one object in the websites array, and you wanted to index only one of the `link` property values, you would have to specify the object of which you want to index the property value.
To index the first property, your JSON configuration would therefore need the following metadata:
```json
"website": "%[websites[0].link]"
```
## Dynamic time expressions
Dynamic time expressions are placeholders for dates in your source JSON configuration.
These expressions contain at least a token representing a specific date and time.
They may also include a mathematical operator and a number of months, days, hours, or minutes.
When indexing or re-indexing your source content, Coveo computes the time expression and retrieves the content matching your date criterion.
Allowed tokens are `@Now` and `@RefreshDate`.
`@Now` represents the start date of the source update operation, while `@RefreshDate` represents the date of the last source [refresh](https://docs.coveo.com/en/2710/).
Allowed units are `M` (months), `d` (days), `h` (hours), and `m` (minutes).
Only whole values are supported.
Space characters are supported, but not recommended.
When using a dynamic time expression with a date token, make sure to provide the date format with the [`DateFormat`](https://docs.coveo.com/en/1525#dateformat-string) parameter.
**Examples**
When performing a source refresh, Coveo retrieves all items published after the last update operation start date:
```json
"RefreshEndpoints": [
{
"DateFormat": "\'yyyy-MM-dd\',\'hh:mm:ss\'",
"QueryParameters": {
"workflow_state": "published",
"since": "@RefreshDate"
}
}
]
```
When indexing a Slack channel, Coveo retrieves all messages written in the last 6 months:
```json
"Endpoints": [
{
"Method": "GET",
"Path": "/api/conversations.history/",
"QueryParameters": {
"token": "@ApiKey",
"channel": "AD8GFL97BFG",
"oldest": "@Now-6M",
"latest": "@Now"
},
"DateFormat": "UnixEpoch",
...
}
...
]
```
## Inheritable properties
Some properties of your source JSON are _inheritable_.
Inheritable properties are passed down from a higher level in the JSON hierarchy to all lower levels unless overridden.
You can take advantage of inheritable properties to avoid redundancy in your JSON configuration.
[`SkippableErrorCodes`](https://docs.coveo.com/en/1525#skippableerrorcodes-string), the [`Paging` object](https://docs.coveo.com/en/1525#paging-object), and the [`Authentication` object](https://docs.coveo.com/en/1525#authentication-object) are inheritable properties, among others.
For example, let's say you want your source to ignore 404 errors when crawling your content.
If you specify the `SkippableErrorCodes` property at the service level, it will apply to all endpoints and sub-queries underneath this service.
As a result, you don't need to specify the `SkippableErrorCodes` property in each endpoint or sub-query configuration of this service.
Conversely, if you specify a different `SkippableErrorCodes` value at the endpoint level, the endpoint value will override the service value.
**Example**