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:

dm-conceptual-diagram.png

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!

light bulb
Note
If you want to get started building a directory site, check out this module .


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:

  1. 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.
  2. On the left side of your page, open up pages > directory-manager, and click on your desired DM configuration file.
  3. Update your file with the desired configuration updates.
  4. Click the “Apply” button in the top right corner of your window and follow the prompts to apply the resources.
  5. 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:
      1. yext init
      2. yext dm run --cacId="[REPLACE_ME]"
  6. 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:

  1. 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.
    • Directory Children (dm_directoryChildren)
    • Directory Children Count (dm_directoryChildrenCount)
    • Directory Manager ID (dm_directoryManagerId)
    • Child Entity IDs (dm_childEntityIds)
  2. Generates your directory entities.

  3. 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.

directory-before-after-new-location.png

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.

dm-ongoing-update.png

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.

directory-before-after-deletion.png


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.

Multi-level directory example

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:

  1. Create a directory entity based on the grouping field specified at each level

    • address.city first
    • address.region second
    • address.country third
  2. Relate the entity with applicable entities up and down the tree

  3. 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:

  1. stateTemplate (applied to each state entity)
  2. 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:

  1. stateTemplate - Sets the description field to “Welcome to [[name]], located in the great nation of [[c_addressCountryDisplayName]]!”

    state-template.png

  2. cityTemplate - Sets the description field is set to “Welcome to [[name]], located in the great state of [[c_addressRegionDisplayName]]!”

    city-template.png

light bulb
Note
These examples leverage embedded fields to make the descriptions dynamic per entity. Learn more about how embedded fields work in this module .

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:

state-city-entities.png

Putting this all together, these dynamic description fields can then easily power your Pages frontend!

dm-description-fields.png


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:

multi-language-dm.png

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:

overlapping-directories.png

To accomplish this use case, you could do the following:

  1. Create two saved filters (assume that there are entities that meet the criteria for both saved filters; i.e. there are “overlapping” entities):
    1. One for “banks”
    2. One for “ATMs”
  2. Create two Directory Manager configurations:
    1. One for “banks”: banks-directory
    2. One for “ATMs”: atms-directory
  3. Run the Directory Manager
    1. As a result, the DM will create both of your directories.
    2. Your base entities will have two parent fields:
      1. dm_directoryParents_banks-directory
      2. dm_directoryParents_atms-directory
      3. 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.

flat-directory.png

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}}"
          }
        }
      }
    }
  ]
}