Directory Management | Yext Hitchhikers Platform
The Directory Manager (DM) helps you create a directory website by generating and grouping Content entities into a tree of parent-child relationships. Conceptually, the resulting entities for a U.S.-based location directory could look something like this:
Once you have an entity directory tree set up in Content, it’s really easy to use Pages to develop a directory website based on that data!
Directory Manager Terminology
Before we dive in, here is a list of common Directory Manager terms used throughout the documentation:
Term | Definition |
---|---|
Directory | A website for which pages are organized in a hierarchical structure. Example: locations.tacobell.com |
Directory Tree | A tree of entities in Content, from which a directory website is built from. |
Directory Manager (DM) | The application responsible for creating and maintaining a directory tree in Content. |
Directory Entities | The set of entities programmatically created by the DM. Example: For a location directory, directory entities would be the cities, states/regions, countries, and the root entity. |
Base Entities | The set of entities from which the directory entities are derived. You can think of this set as the “base” for your directory; the DM does not create these entities, it uses them in order to create entities. These are also known as “leaf” entities. Example: For a location directory, the base entities are the locations. The directory entities are generated based on address information from the base entities. |
Root | The single entity that sits at the top of your tree. All other entities in your directory are related to this entity, hence it is known as the “root”. |
Directory Entity Types | The set of entity types that comprise your base entities. This controls what types of entities are allowed to be processed by the Directory Manager. |
Directory Saved Filter | A
saved filter
generated by the DM to represent the set of entities generated for a particular level of a directory. Each saved filter only includes entities that have Directory Children. There is always one saved filter per level of a directory tree. Example: Given a directory with cities, states, and a root, the DM will create three saved filters. |
Directory Parents | For a given node, parents refers to the full set of entity ancestry above it in the directory tree. Example: Given a directory with cities, states, countries, and a root, the parents for a New York (city) entity would be New York (state), United States, and root. |
Directory Children | For a given entity, children refers to the set of entities related one-level below it in the directory tree. Example: Given a location directory, the children for New York (state) would be New York (city) and Brooklyn. |
Directory Children Count | For a given entity, this count refers to the total number of base entities related to it in a directory tree. Example: Assume there are five locations based in Manhattan and five based in Brooklyn. The children count would be 10 entities for New York (state): five entities for Manhattan and five entities for Brooklyn. |
Directory Manager Schema
The Directory Manager is powered by a JSON configuration file. Here is the full list of available properties that can be configured in your file:
Property | Type | Details | Validation |
---|---|---|---|
$id |
string | Configuration identifier. Unique within account. | Required |
name |
string | Display name for the Directory Manager. | Required |
baseEntities |
object | The set of base entities from which the directory will be derived. You must provide a value for entityTypes and can use one or more savedFilterIds in conjunction. |
Required |
baseEntities.EntityTypes |
array | Array of entity type IDs that comprise your base entities. | Required |
baseEntities.savedFilterIds |
array | Array of saved filter IDs that comprise your base entities. | |
localization |
object | Configures the language profiles that are in scope for your directory. | Required |
localization.locales |
array | Array of locale codes (e.g. “en”, “es”, “fr”). For each locale specified, the Directory Manager will create a language profile of that locale for relevant directory entities. Refer to the Multi-Language Directories section below for more information. | Required |
root |
object | Used to configure the “root” entity for your directory tree. When included, all entities in your tree are related to this entity. | Required |
root.entityType |
string | The ID of the entity type used for the root entity. This must already exist. | Required |
root.slug |
string | Configures the slug value for the entity. | Required |
root.entityTemplateId |
string | Specifies an entity template ID to apply to this entity upon creation. | |
root.localeOverrides |
object | Configures field overrides to be used when creating alternate language profiles. | |
root.localeOverrides.[localeCode] |
object | Object that specifies the localeCode to configure overrides for. Any localeCode specified here must be listed in localization.locales , or else the overrides will be ignored. |
|
root.localeOverrides.[localeCode].slug |
string | Configures the slug value for the root entity. | |
root.localeOverrides.[localeCode].entityTemplateId |
string | Specifies an entity template ID to apply to this entity language profile upon creation. | |
levels |
array | An array of objects. Each object represents a level in the directory. The last element in the array configures the level that points to the base entities. If omitted, all base entities in your directory will be related directly to your root entity. This is known as a “flat directory” . |
Min 0, Max 3 |
levels.entityType |
string | The ID of the entity type used for each level. Must already exist. | Required |
levels.field |
string | The field ID from the base entity used to generate entities at this level. | Required |
levels.slug |
string | Configures the slug value for the entity. | |
levels.entityTemplateId |
string | Specifies an entity template ID to apply to this entity upon creation. | |
levels.fieldMappings |
object | Allows you to map fields from the base entity to fields on the directory entity. Also allows you to populate “display name” translations for address.region and address.countryCode abbreviations. For example, “NY” would map “New York”, and “US” would map “United States” to your entity. To use these, configure your mapping value as "{{#regionDisplayName}}" and "{{#countryDisplayName}}" respectively. |
|
levels.localeOverrides |
object | Configures field overrides to be used when creating alternate language profiles. | |
levels.localeOverrides.[localeCode] |
object | Object that specifies the localeCode to configure overrides for. Any localeCode specified here must be listed in the localization.locales , or else the overrides will be ignored. |
|
levels.localeOverrides.[localeCode].slug |
string | Configures the slug value for the entity on the relevant language profile. | |
levels.localeOverrides.[localeCode].entityTemplateId |
string | Specifies an entity template ID to apply to entities generated at this level on the relevant language profile. | |
levels.localeOverrides.[localeCode].fieldMappings |
object | Defines mappings for fields on a base entity to fields on a directory entity. Supports display name translations for region and country abbreviations. | |
usePrimaryLocaleOnlyForSlug |
boolean | When set to true, the Directory Manager will only use the primary language profile from your base entities when populating the slug field on your directory entities. Defaults to false. This is useful if you are creating multi-language directory entities, and want to construct entities using data from one language profile only. |
|
collapseSlugs |
boolean | When set to true, the Directory Manager will collapse the slug value it creates for your Directory entities in scenarios where a level is “skipped”. When set to false, the Directory Manager will denote any “skips” within the slug using a hyphen ( - ). This is useful in scenarios where your region and city may result in slug collisions: such as Barcelona “region” and Barcelona “city”. Assuming the region and city slugs are defined as Barcelona, their slugs would be outputted as barcelona and -/barcelona respectively. |
Updating a Directory Manager Configuration
After reading our advanced use cases section, you may have decided you want to add a new feature to your existing directory. Don’t worry, upgrading your DM configuration is easy!
Follow the steps below to upgrade your configuration:
- Go to your Yext account, and open up Admin Console.
- You can find this in the Developer section of your left-nav bar, or by using Quick Find.
- On the left side of your page, open up
pages
>directory-manager
, and click on your desired DM configuration file. - Update your file with the desired configuration updates.
- Click the “Apply” button in the top right corner of your window and follow the prompts to apply the resources.
Navigate to the Directory Manager page in your Yext account. Click the dropdown next to your DM and select “Run”, which will instruct the DM to recreate your tree using your updated configuration.
- Alternatively, you can re-run the Directory Manager using the CLI, if that is preferable for your workflow. To do so, run:
yext init
yext dm run --cacId="[REPLACE_ME]"
- Alternatively, you can re-run the Directory Manager using the CLI, if that is preferable for your workflow. To do so, run:
Navigate back to Content. You should see the updated configuration changes reflected in your data!
Directory Manager Execution Process
Once the Directory Manager has been executed, there’s a lot that happens under the hood. Specifically, the Directory Manager:
Adds the following custom fields to the entity types in your Yext account:
- Directory Parents (
dm_directoryParents_[dm_directoryManagerId]
)- The
dm_directoryManagerId
is appended to disambiguate the parents field, in case you have multiple active directories.
- The
- Directory Children (
dm_directoryChildren
) - Directory Children Count (
dm_directoryChildrenCount
) - Directory Manager ID (
dm_directoryManagerId
) - Child Entity IDs (
dm_childEntityIds
)
- Directory Parents (
Generates your directory entities.
Creates a saved filter for each level of your directory.
Saved Filters
The Directory Manager creates a saved filter for each level of your tree, which includes all entities that were created at that particular level. These filters are generated automatically, to help you pair those sets of entities with templates for Pages development.
Importantly, part of the criteria for these filters is to only include entities that have directory children. If at any point your Directory Entity no longer has any child entities, it will no longer meet the criteria for your saved filters.
Field Updates
Critically, the Directory Manager is also responsible for creating directory-specific fields and adding them to your entities. It will also populate the built-in slug field for each level of your tree. These include:
- Slug (slug)
- Directory Parents (list of entities related to an entity one-level up the directory tree)
- Directory Children (list of entities related to an entity one-level down the directory tree)
- Directory Children Count (the count of base entities related to an entity)
- Child Entity IDs (a list used by the Directory Manager to help power the Directory Children Count)
- Directory Manager ID (the DM resource ID associated with the creation of an entity)
Ongoing Directory Maintenance
The Directory Manager’s work isn’t finished after initialization. The DM will remain active in your account and actively listen for ongoing CRUD (create, read, update, delete) operations involving the set of base entities.
Entity Creation
For example, imagine your business has expanded to a new location - Chicago, Illinois - so you add a new location entity to your graph. You don’t need to re-run the Directory Manager in order to update your directory; the new entities are generated automatically!
In the example below:
- The user adds location 5 - based in Chicago, IL, a city and state that aren’t in the directory yet.
- As such, the DM creates all of the following:
- Chicago city entity
- Illinois state entity
- Relationships pointing to and from them
- The directory children count for the root entity changes from four to five. It is set to one for both the Illinois state entity and the Chicago city entity.
Entity Updates
The same is true for updates to existing entities as well.
In the example below:
- The user moves location 2 from New York, NY to Brooklyn, NY.
- As such, the DM creates a new Brooklyn city entity and relates it to the NY state entity.
- The DM changes the related city entity for location 2 from New York (city) to Brooklyn.
- The directory children count is still two for New York state, changes from two to one for New York (city), and it is set to one for Brooklyn.
Entity Deletions
The same is true for entity deletions. For example, if you were to delete a location from your directory, the DM will ensure any relevant directory entities are no longer related to it (and are removed from the directory entirely, if necessary). Observe the example below:
In the example below:
- The user deletes location 1 in Brooklyn, NY.
- After deletion, the Brooklyn city entity no longer has any related children entities.
- The Brooklyn entity is removed from the directory by the Directory Manager.
- Because Brooklyn has no children, the DM removes it as a child of NY state.
- The directory children count changes for NY state from three to two and changes for the root entity from four to three.
Advanced Configuration Use Cases
Multi-Level Directories (aka “Skip” Level Directory)
The Directory Manager supports configurations where base entities may have differing lengths of parent ancestry. In other words, some base entities may not have a value for one or more levels of grouping logic at a particular level. For example, refer to the image below, which shows a directory tree based on entities from the U.S. and United Kingdom.
For simplicity, assume the DM performs operations starting at the base entities, repeats the same logic at each level of the tree, and completes when it reaches the root. Below is the order of operations for its logic:
Given a single base entity from the example above, the DM attempts to:
Create a directory entity based on the grouping field specified at each level
address.city
firstaddress.region
secondaddress.country
third
Relate the entity with applicable entities up and down the tree
When the DM cannot find a value provided for a level, the system will:
- automatically skip operations at that level
- proceed to the next level of the tree
This is useful in scenarios where you may have different level “depths”, depending on where geographically your entities are located. For example, in the U.K., it may be typical that the address.region
property is not populated in entity addresses. As such, there would be zero U.K. entities at the state level. Instead, relationships from the city level “skip” straight to the country level. In the example below, you could configure your directory levels to contain every level you would be expecting; if a value isn’t found for a particular base entity, the DM will simply skip that level.
{
"$id": "multi-level-directory",
"$schema": "https://schema.yext.com/config/pages/directory-manager/v1",
"name": "Multi-Level directory",
"baseEntities": {
"entityTypes": ["location"],
"savedFilterIds": [
"[savedFilterId]"
]
},
"localization": {
"locales": [
"en",
]
},
"root": {
"entityType": "ce_root",
"slug": "index.html"
},
"levels": [
{
"entityType": "ce_country",
"field": "address.countryCode",
"slug": "{{address.countryCode}}",
"fieldMappings": {
"c_addressCountryDisplayName": "{{#countryDisplayName}}"
}
},
{
"entityType": "ce_region",
"field": "address.region",
"slug": "{{address.countryCode}}/{{address.region}}",
"fieldMappings": {
"c_addressCountryDisplayName": "{{#countryDisplayName}}"
}
},
{
"entityType": "ce_city",
"field": "address.city",
"slug": "{{address.countryCode}}/{{address.region}}/{{address.city}}",
"fieldMappings": {
"c_addressRegionDisplayName": "{{#regionDisplayName}}"
}
}
]
}
It is possible that, in some scenarios, you may encounter slug
collisions between levels if they share the same name. For example:
- New York (city) exists within New York (state)
- Barcelona (city) exists within Barcelona (region)
If you run into this scenario, you can use set the collapseSlugs
boolean to false
in your Directory Manager configuration; this ensures that, in cases where a level is skipped, the DM places a hyphen within the slug to denote when a value is missing. For example, if your city
level slug is defined as {{address.region}}/{{address.city}}
, the slug for a city entity with a region-level-skip would be -/{{address.city}}
.
Entity Templates
An entity template is a tool in Content that allows you to define a “template” of field values and apply it to a set of entities. Templates are extremely useful for applying standardized content at scale, and can save you a ton of time (refer to the Entity Templates module to learn more).
The Directory Manager extends this functionality by allowing you to apply specific templates any time a directory entity is generated! To accomplish this, use the entityTemplateId
property.
Let’s look at a concrete use case to see why this is useful:
Example DM Configuration
Per the configuration below, there are two entity templates in use by the DM:
stateTemplate
(applied to each state entity)cityTemplate
(applied to each city entity){ "$id": "us-directory", "$schema": "https://schema.yext.com/config/pages/directory-manager/v1", "name": "US Directory", "baseEntities": { "entityTypes": ["location"], "savedFilterIds": ["685381454"] }, "root": { "entityType": "ce_root", "slug": "index.html" }, "localization": { "locales": ["en"] }, "levels": [ { "entityType": "ce_state", "field": "address.region", "slug": "{{address.region}}", "entityTemplateId": "stateTemplate", "fieldMappings": { "c_addressRegionDisplayName": "{{#regionDisplayName}}", "c_addressCountryDisplayName": "{{#countryDisplayName}}" } }, { "entityType": "ce_city", "field": "address.city", "slug": "{{address.region}}/{{address.city}}", "entityTemplateId": "cityTemplate", "fieldMappings": { "c_addressRegionDisplayName": "{{#regionDisplayName}}" } } ] }
Templates
Now, let’s look at what each entity template does:
stateTemplate - Sets the
description
field to “Welcome to[[name]]
, located in the great nation of[[c_addressCountryDisplayName]]
!”cityTemplate - Sets the
description
field is set to “Welcome to[[name]]
, located in the great state of[[c_addressRegionDisplayName]]
!”
Tying it All Together
Once the Directory Manager runs, it will apply the relevant template to each entity that it generates. As a result, you can observe how the description field is automatically populated for both the New York (state)
and New York (city)
entities below:
Putting this all together, these dynamic description fields can then easily power your Pages frontend!
Multi-Language Directories
For each base entity, the DM will generate equivalent language profiles for each entity in its parent ancestry. Refer to the screenshot below:
Location 1, based in New York, NY, has language profiles defined for [“en”, “es”]. This language profile cardinality is respected across its parent ancestry - meaning the DM creates an “en” and “es” profile for the New York city, New York state, and root entities.
By contrast, Location 3 does not have an “es” profile, meaning its ancestry only has “en” profiles generated for it. Furthermore, when it comes to entity relationships, Streams respects language profile cardinality - meaning that the “es” language profile for the root entity will not have “California” as a child entity (because it does not exist).
Refer to the example configuration below to create a multi-language directory in your own account, which includes multiple languages, and entityTemplate
overrides on a per-locale basis:
{
"$id": "us-directory",
"$schema": "https://schema.yext.com/config/pages/directory-manager/v1",
"name": "US Diretory",
"baseEntities": {
"entityTypes": [
"location"
]
},
"localization": {
"locales": ["en", "es", "fr"]
},
"root": {
"entityType": "ce_root",
"slug": "index.html",
"entityTemplateId": "rootTemplate",
"localeOverrides": {
"es": {
"entityTemplateId": "rootTemplate-spanish",
"slug": "index/{{localeCode}}"
},
"fr": {
"entityTemplateId": "rootTemplate-french",
"slug": "index/{{localeCode}}"
}
}
},
"levels": [
{
"entityType": "ce_state",
"field": "address.region",
"slug": "{{address.region}}",
"entityTemplateId": "stateTemplate",
"fieldMappings": {
"c_addressRegionName": "{{#regionDisplayName}}",
"c_addressCountryDisplayName": "{{#countryDisplayName}}"
},
"localeOverrides": {
"es": {
"entityTemplateId": "stateTemplate-spanish",
"slug": "{{localeCode}}/{{address.region}}"
},
"fr": {
"entityTemplateId": "stateTemplate-french",
"slug": "{{localeCode}}/{{address.region}}"
}
}
},
{
"entityType": "ce_city",
"field": "address.city",
"slug": "{{address.region}}/{{address.city}}",
"entityTemplateId": "cityTemplate",
"fieldMappings": {
"c_addressRegionName": "{{#regionDisplayName}}"
},
"localeOverrides": {
"es": {
"entityTemplateId": "cityTemplate-spanish",
"slug": "{{localeCode}}/{{address.region}}/{{address.city}}",
"fieldMappings": {
"c_addressRegionName": "{{#regionDisplayName}}"
}
},
"fr": {
"entityTemplateId": "cityTemplate-french",
"slug": "{{localeCode}}/{{address.region}}/{{address.city}}",
"fieldMappings": {
"c_addressRegionName": "{{#regionDisplayName}}"
}
}
}
}
]
}
“Overlapping” Directories
In some scenarios, you may want to create multiple directories that share the same set of base entities. For example, you may have one dedicated directory for banks with ATMs; and a separate directory for ATMs only. Refer to the image below:
To accomplish this use case, you could do the following:
- Create two
saved filters
(assume that there are entities that meet the criteria for both saved filters; i.e. there are “overlapping” entities):
- One for “banks”
- One for “ATMs”
- Create two Directory Manager configurations:
- One for “banks”:
banks-directory
- One for “ATMs”:
atms-directory
- One for “banks”:
- Run the Directory Manager
- As a result, the DM will create both of your directories.
- Your base entities will have two parent fields:
dm_directoryParents_banks-directory
dm_directoryParents_atms-directory
- Any overlapping base entity will be incorporated into each relevant directory; the parents field includes the Directory Manager ID, to help you disambiguate which directory frontend the entity can be used in.
“Flat” Directory
In some cases, you may want to quickly create a directory that has no levels. In other words, you just want to relate a set of base entities to a singular “root”. This is known as a “flat” directory.
This can be useful in scenarios where you want to keep a full set of data actively related to one entity, most likely for the purposes of entity relationships .
To create a directory of this nature, you simply need to omit the levels
array in your DM configuration. Refer to the configuration below:
{
"$id": "flat-directory",
"$schema": "https://schema.yext.com/config/pages/directory-manager/v1",
"name": "Flat directory",
"baseEntities": {
"entityTypes": ["location"],
"savedFilterIds": [
"[savedFilterId]"
]
},
"localization": {
"locales": [
"en",
]
},
"root": {
"entityType": "ce_root",
"slug": "index.html"
}
}
“Primary-Only” Language Directories
If you are managing a highly-complex multi-language set of entities in Content, it is possible for you to configure the Directory Manager to populate the slug
field for your DM entities using data exclusively from your primary language profiles (refer to the
Language Profiles
unit for more information).
To use this mode, set the usePrimaryLocaleOnlyForSlug
boolean in your DM configuration to true
. Using the configuration example below, assuming English is primary, this means the values for {{address.region}}
and {{address.city}}
in any slug
will be populated based on the values from the English base entity language profiles.
{
"$id": "us-directory-primary-only",
"$schema": "https://schema.yext.com/config/pages/directory-manager/v1",
"name": "US Directory (Primary Only)",
"usePrimaryLocaleOnlyForSlug": true,
"baseEntities": {
"entityTypes": [
"location"
]
},
"localization": {
"locales": ["en", "es"]
},
"root": {
"entityType": "ce_root",
"slug": "index.html",
"localeOverrides": {
"es": {
"slug": "index/{{localeCode}}"
}
}
},
"levels": [
{
"entityType": "ce_state",
"field": "address.region",
"slug": "{{address.region}}",
"fieldMappings": {
"c_addressRegionName": "{{#regionDisplayName}}",
"c_addressCountryDisplayName": "{{#countryDisplayName}}"
},
"localeOverrides": {
"es": {
"slug": "{{localeCode}}/{{address.region}}"
}
}
},
{
"entityType": "ce_city",
"field": "address.city",
"slug": "{{address.region}}/{{address.city}}",
"fieldMappings": {
"c_addressRegionName": "{{#regionDisplayName}}"
},
"localeOverrides": {
"es": {
"slug": "{{localeCode}}/{{address.region}}/{{address.city}}",
"fieldMappings": {
"c_addressRegionName": "{{#regionDisplayName}}"
}
}
}
}
]
}