Find Assets By Metadata
How to find Content browser assets using Metadata
This is a more complicated topic, but one of the most powerful asset management tools available in Unreal is the ability to search for assets using Metadata and the Asset Registry. This allows us to use more than just asset paths to manage our assets, making tools more flexible and robust in production.
Understanding the Asset Registry
One of the events that runs during the Unreal Editor startup process is a scan of the project for all of the available assets. This information is collected in the Asset Registry and is relied upon by a number of systems, such as the dropdown menu when changing a Material property:

What it Tracks
The Asset Registry collects the header information for all of the assets in the Unreal Project using the AssetData class. It doesn't load the entire asset, it just tracks some of the essentials:
The Asset Class
The Asset Name
The Asset Path
The Asset Metadata
That last bullet point is what we care about here: if an asset has metadata on it, and that metadata key is registered in the Asset Registry, we can search for assets using that metadata.
Getting Assets by Class
Because this is a bit of a complicated topic, we're going to first start with an asset search function that just returns all of the assets of the given class type:
asset_registry_helper = unreal.AssetRegistryHelpers()
asset_registry = asset_registry_helper.get_asset_registry()
def find_assets_by_class(class_names=["object"]):
"""List all of the available assets matching the provided class names"""
# create a basic Asset Registry filter based on the class_names
base_filter = unreal.ARFilter(
class_names=class_names,
recursive_classes=True
)
# Get the class-based search results
results = asset_registry.get_assets(base_filter) or []
return results
This is our baseline setup, asking the Asset Registry for a list of assets of the given class.
While there are a few similar functions available in the Asset Registry, I prefer this function because it has a lot of flexibility thanks to the ARFilter it uses, which will be important as we expand our function.
How to Filter by Metadata
Once we have a list of assets from the Asset Registry as AssetData objects we can use the following operation to only get those matching the desired metadata values:
query = [unreal.TagAndValue("METADATA_KEY", "VALUE_TO_LOOK_FOR")]
base_filter = unreal.ARFilter(class_names=["object"], recursive_classes=True)
meta_filter = asset_registry_helper.set_filter_tags_and_values(base_filter, query)
# reduce the results to only those matching the given metadata
results = asset_registry.run_assets_through_filter(results, meta_filter) or []
The set_filter_tags_and_values() function is responsible for most of the magic here - it will create an ARFilter based on the provided metadata key:value pair.
Using an example asset from the Getting Metadata page we can use this to find any assets with "asset_name"
set to "eugene"
:

Search by Metadata
In a production setting we can often find ourselves wanting to find a file based on multiple metadata values, such as getting version 10 of an particular asset. Let's set up our function to expect our metadata query as a dictionary:
metadata_search_data = {
"asset_name": "Eugene",
"asset_version": 10
}
Expanding the earlier function, once we get the list of results from the base_filter we want to loop over each metadata pair, further reducing the results to only those that match:
asset_registry_helper = unreal.AssetRegistryHelpers()
asset_registry = asset_registry_helper.get_asset_registry()
def find_assets_by_metadata(meta_dict, class_names=["object"]):
"""
Find assets in the Content Browser based on
a given dict of metadata {key:value} pairs
"""
# create a basic Asset Registry filter based on the class_names
base_filter = unreal.ARFilter(
class_names=class_names,
recursive_classes=True
)
# Get the initial class-based search results
results = asset_registry.get_assets(base_filter) or []
for key, value in meta_dict.items():
# Create a new ARFilter based on the {key:value} pair
query = [unreal.TagAndValue(key, str(value))]
meta_filter = asset_registry_helper.set_filter_tags_and_values(base_filter, query)
# reduce the results to only those matching the given metadata
results = asset_registry.run_assets_through_filter(results, meta_filter) or []
# return the results as a Python list as it might currently be an unreal.Array
return list(results)
To simplify the function, here's essentially what it's doing:
Get a list of assets in the project of the given class(es)
Create an Asset Registry Filter for each metadata
key:value
pairPass the list of assets through each Metadata Filter, removing any assets that don't match
With this function we now have a simplified method of searching for assets based on metadata:

Making Metadata Searches Faster
To test the performance of this function I created an Unreal Project with nearly 40,000 tagged assets in it (37,880 to be precise!).
Using the following logic, I tested from zero metadata queries up to two metadata queries timing the function:
start = time.time()
query = {"name": "b"}
results = find_assets_by_metadata(query)
end = time.time()
print(f"took {end-start:.2f} seconds to find {len(results)} matches for {query}!")
This was the result:

An interesting observation here is that the first Asset Registry interaction collecting the list of assets isn't the most expensive check — it's the second interaction, which performs the first Metadata filter.
Something we can try in our function is to start with the first metadata query:
first_filter = base_filter
if meta_dict:
first_key = list(meta_dict.keys())[0]
first_value = meta_dict.pop(first_key)
query = [unreal.TagAndValue(first_key, str(first_value))]
first_filter = asset_registry_helper.set_filter_tags_and_values(base_filter, query)
results = asset_registry.get_assets(first_filter) or []
Incredibly, this actually made it faster to search by metadata:

That's 0.03
seconds to find a specific tagged asset in the Unreal Project of 37,800 assets.
Expanding the Function
In a production environment we might want more than just the metadata in our asset search function. One example of note is to also filter results based on a Parent Path, only returning assets that live under a specific folder path.
Thankfully this isn't too hard to add, the ARFilter class has a property called package_paths
that can help us here. This property along with recursive_paths
allow us to filter assets based on the folder they live in.
And here is an updated function with a new parent_path parameter:
asset_registry_helper = unreal.AssetRegistryHelpers()
asset_registry = asset_registry_helper.get_asset_registry()
def find_assets_by_metadata(meta_dict, class_names=["object"], parent_path = ""):
"""
Find assets in the Content Browser based on
a given dict of metadata {key:value} pairs
use 'parent_path' to only return results that live under that asset path
"""
# create a basic Asset Registry filter based on the class_names
# include the parent path if provided
base_filter = unreal.ARFilter(
class_names=class_names,
recursive_classes=True,
package_paths=[parent_path] if parent_path else [],
recursive_paths=True,
)
# Start with the first Metadata Query if provided
first_filter = base_filter
if meta_dict:
first_key = list(meta_dict.keys())[0]
first_value = meta_dict.pop(first_key)
query = [unreal.TagAndValue(first_key, str(first_value))]
first_filter = asset_registry_helper.set_filter_tags_and_values(base_filter, query)
# Get the initial search results
results = asset_registry.get_assets(first_filter) or []
for key, value in meta_dict.items():
# Create a new ARFilter based on the {key:value} pair
query = [unreal.TagAndValue(key, str(value))]
meta_filter = asset_registry_helper.set_filter_tags_and_values(base_filter, query)
# reduce the results to only those matching the given metadata
results = asset_registry.run_assets_through_filter(results, meta_filter) or []
# return the results as a Python list as it might currently be an unreal.Array
return list(results)
Without a parent_path
we get every Level Sequence in the project:

With a parent_path
we only get the Level Sequences found in /Game/dev
:

This function is now capable of looking for assets based on:
Metadata
Asset Class
Asset Path
Summary
The Asset Registry is quite powerful in Unreal. With this approach our tools can be a lot more robust and no longer need to rely on strict folder pathing or naming conventions, we can treat and manage our assets like a database service.
There's also a lot more functionality that can be added to the function examples above, I definitely encourage giving metadata a try in Unreal
Last updated