docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Use case: Querying metadata

    Before you start

    Before you start, install the following Unity Cloud package dependency from the registry by name:

    • unity.cloud.identity

    For more information on how to install packages by name, see Installing a package by name

    How do I...?

    Upload an asset

    To upload an asset, follow these steps

    Set up a IMetadataRepository

    
    public IMetadataRepository CreateMetadataRepository(IDataset dataset, IServiceHttpClient serviceHttpClient, IServiceHostResolver serviceHostResolver)
    {
        var factory = new MetadataRepositoryFactory();
        return factory.Create(dataset, serviceHttpClient, serviceHostResolver);
    }
    
    

    IMetadataRepository is the first entry point for any metadata query. The constructor of the default implementation requires:

    • An instance of IServiceHttpClient and IServiceHostResolver. See Best practices: dependency injection page of the Identity package documentation for more information.
    • A projectId, assetId and datasetId. See Unity Cloud Common documentation for more information.

    Query metadata

    
    public async Task QueryingMetadata(IMetadataRepository metadataRepository)
    {
        // Perform the query.
        var request = metadataRepository.Query();
    
        // Iterate through the query result.
        await foreach (var each in request)
        {
            var id = each.Id;
            var name = each.Name;
            var hasChildren = each.HasChildren;
    
            var ancestors = each.AncestorIds;
            var rootParent = ancestors[0];
            var directParent = ancestors[^1];
    
            // Each entry in the result is attached to a single ownerId, and contains key / values associated with it.
            foreach (var key in each.Properties.Keys)
            {
                var value = each.Properties[key];
            }
        }
    }
    
    

    With a IMetadataRepository, you can perform a query by combining the Query and iterate its result.

    For each item part of the IAsyncEnumerable, you can access data through the MetadataInstance object. This object contains these properties:

    • Id is the unique identifier associated with the metadata.
    • Properties expose the queried metadata associated with the Id.
    • Name is the name of the instance as it is in the source file.
    • HasChildren is a boolean indicating if the instance has children or is a leaf.
    • AncestorIds is a list of the ancestor ids of the instance where the first element is the top level parent (root) of the instance and the last element is the direct parent.

    Use filters to build advanced queries

    
    public async Task QueryingMetadataAdvanced(IMetadataRepository metadataRepository, IEnumerable<string> topLevelFieldsToQuery, InstanceId[] ownerIdsToConsider, string fieldFilterKey, string expectedValue)
    {
        var request = metadataRepository
            .Query()
            .Select(new MetadataPathCollection(topLevelFieldsToQuery))
            .WhereInstanceEquals(ownerIdsToConsider)
            .WhereFieldEquals(fieldFilterKey, expectedValue);
    
        await foreach (var metadata in request)
        {
    

    The query described in the section above is the most standard query that you could expect, as it doesn't use any filter. Following a LINQ-like approach, the query system provides a way to build more advanced queries.

    SELECT filter, to query a subset of the metadata

    
    public async Task QueryingMetadataWithSelectFilter(IMetadataRepository metadataRepository, IEnumerable<string> rootPathsToQuery)
    {
        // Query will return matches whose metadata content is restricted to the specified paths
        var request = metadataRepository
            .Query()
            .Select(new MetadataPathCollection(rootPathsToQuery));
    
        await foreach (var metadata in request)
        {
    

    Using the Select method, you can specialize the query to ensure that only a specific set of root keys will be included in the response.

    Note

    This will also forcefully add the key to every fetched metadata. If it didn't originally contain it, it will appear as empty.

    SELECT-ALL filter, to query everything for each instance

    
    public async Task QueryingMetadataWithSelectAllFilter(IMetadataRepository metadataRepository)
    {
        // Query will return matches whose metadata content is everything
        var request = metadataRepository
            .Query()
            .Select(MetadataPathCollection.All);
    
        await foreach (var metadata in request)
        {
    

    Using the Select method with MetadataPathCollection.All, all the available data for every instance will be included in the response.

    SELECT-ONLY-ID filter, to query no metadata content

    
    public async Task QueryingMetadataWithSelectOnlyIdFilter(IMetadataRepository metadataRepository)
    {
        // Query will return matches without metadata content, simply the id
        var request = metadataRepository
            .Query()
            .Select(MetadataPathCollection.None, new OptionalData(OptionalData.Fields.Id));
    
        var ids = new List<InstanceId>();
        await foreach (var metadata in request)
            ids.Add(metadata.Id);
    }
    
    

    Using the Select method with MetadataPathCollection.None, no metadata will be returned. The result will only contain the ids. This is especially useful when used with a Where filter.

    WHERE-INSTANCE-EQUALS filter, to query a subset of dataset instances

    
    public async Task QueryingMetadataWithWhereInstanceEqualsFilter(IMetadataRepository metadataRepository, IEnumerable<InstanceId> ownerIdsToConsider)
    {
        // Query will only return matches that are attached to the specified owner Ids
        var request = metadataRepository
            .Query()
            .WhereInstanceEquals(ownerIdsToConsider);
    
        await foreach (var metadata in request)
        {
    

    Using the WhereInstanceEquals method, you can specialize the query to ensure that only a specific subset of instances in the dataset will be included in the response.

    WHERE filter, to apply a metadata-based condition

    
    public async Task QueryingMetadataWithWhereFilter(IMetadataRepository metadataRepository, string fieldKey, string expectedValue)
    {
        // Query will only contain matches that follow the specified criterium
        var request = metadataRepository
            .Query()
            .WhereFieldEquals(fieldKey, expectedValue);
    
        await foreach (var metadata in request)
        {
    

    Using the WhereKeyEquals method, you can specialize the query to ensure that the matches to be included in the response follow a specific metadata-based condition:

    • Metadata should contain a specific key.
    • This key should have a value exactly equal to a constant value.
    • Requesting the null value will search for metadata values that equals to null or an empty string.
    
    public async Task QueryingMetadataWithWhereNotFilter(IMetadataRepository metadataRepository, string fieldKey)
    {
        // Query will only contain matches that follow the specified criterium
        var request = metadataRepository
            .Query()
            .WhereFieldNotEquals(fieldKey, null);
    
        await foreach (var metadata in request)
        {
    

    Using the WhereKeyNotEquals method, you can specialize the query to ensure that the matches to be included in the response follow a specific negative metadata-based condition:

    • Metadata should contain a specific key.
    • This key should have a value not equal to a constant value.

    Combine filters

    
    public async Task QueryingMetadataAdvanced(IMetadataRepository metadataRepository, IEnumerable<string> topLevelFieldsToQuery, InstanceId[] ownerIdsToConsider, string fieldFilterKey, string expectedValue)
    {
        var request = metadataRepository
            .Query()
            .Select(new MetadataPathCollection(topLevelFieldsToQuery))
            .WhereInstanceEquals(ownerIdsToConsider)
            .WhereFieldEquals(fieldFilterKey, expectedValue);
    
        await foreach (var metadata in request)
        {
    

    You can use any combination of the filters described above to build advanced queries and apply multiple filters at once.

    Use the result

    IAsyncEnumerable

    
    public async Task UsingTheEnumerableReturnedFromAQuery(IMetadataRepository metadataRepository, string fieldKey)
    {
        var request = metadataRepository.Query();
    
        await foreach (var metadata in request)
        {
            var id = metadata.Id;
            var name = metadata.Name;
            var ancestors = metadata.AncestorIds;
            var hasChildren = metadata.HasChildren;
            var properties = metadata.Properties[fieldKey];
        }
    }
    
    

    Since the result is an IAsyncEnumerable, you can use the await foreach statement to iterate through the matches.

    MetadataObjects and MetadataValues

    
    public async Task UsingMetadataObjects(IMetadataRepository metadataRepository, InstanceId id, string fieldKey, string subKey)
    {
        var request = metadataRepository.Query();
    
        await foreach (var metadata in request)
        {
            //MetadataContainer can either be MetadataObject, MetadataArray or MetadataValues
            var container = metadata.Properties[fieldKey];
        }
    }
    
    
    
    public async Task GettingAMetadataValue(IMetadataRepository metadataRepository, InstanceId id, string rootKey)
    {
        var request = metadataRepository.Query();
    
        await foreach (var metadata in request)
        {
            var value = metadata.Properties[rootKey].ToString();
        }
    }
    
    

    The Metadata is always returned as a MetadataObject as it always is a valid JSON object at the root. MetadataObject extends MetadataContainer that is also extended by MetadataValue and MetadataArray. You can use a MetadataObject as a IReadOnlyDictionary and access any valid JSON root objects, values or arrays by using the square bracket operator. When trying to read a value you can either use ToString() or ToNumber() depending on the underlying type.

    Hierarchy of instances

    Get the parents hierarchy of an instance

    
    public async Task QueryingMetadataWithParentHierarchy(IMetadataRepository metadataRepository, InstanceId id)
    {
        var instance = await metadataRepository.Query().WhereInstanceEquals(id).GetFirstOrDefaultAsync(default);
    
        var ancestorIds = instance.AncestorIds;
        var rootParentId = ancestorIds[0];
        var directParentId = ancestorIds[^1];
    
        var ancestors = await metadataRepository.Query().WhereInstanceEquals(ancestorIds).ToDictionaryAsync(x => x.Id, x => x, default);
    
        var rootParent = ancestors[rootParentId];
        var directParent = ancestors[directParentId];
    }
    
    

    Each instance has a parent hierarchy that can be accessed through the AncestorIds property. This property is a list of the ancestor ids of the instance where the first element is the top level parent (root) of the instance and the last element is the direct parent.

    Get the children of an instance

    
    public async Task QueryingDirectChildren(IMetadataRepository metadataRepository, InstanceId id)
    {
        var instance = await metadataRepository.Query().WhereInstanceEquals(id).GetFirstOrDefaultAsync(default);
    
        if (instance.HasChildren)
        {
            var directChildren = metadataRepository.Query().WhereHasAncestor(id, 0);
        }
    }
    
    

    By filtering the query with the WhereAncestorEquals method, you can specialize the query to ensure that only the direct children of a specific instance will be included in the response by setting the max depth value to 0.

    
    public async Task QueryingAllChildren(IMetadataRepository metadataRepository, InstanceId id)
    {
        var instance = await metadataRepository.Query().WhereInstanceEquals(id).GetFirstOrDefaultAsync(default);
    
        if (instance.HasChildren)
        {
            var allChildrenAndSubChildren = metadataRepository.Query().WhereHasAncestor(id, int.MaxValue);
        }
    }
    
    

    If you want to get all the children of a specific instance, you can use the WhereAncestorEquals method with a higher max depth value.

    • If you set the depth to 0, you will get the direct children of the instance.
    • If you set the depth to 1, you will get the direct children and the children of the children of the instance.
    • If you set the depth to int.MaxValue, you will get all the instances which has the given instance id as part of its AncestorIds list.

    Use helper methods

    Getting all keys in the dataset

    
    public async Task QueryingAllRootPaths(IMetadataRepository metadataRepository)
    {
        // The result will contain all the root paths in the dataset
        var result = await metadataRepository.GetAllPathsAsync();
    }
    
    

    By using the GetAllPathsAsync method, you can retrieve all the root keys that are present in the dataset.

    Note

    This method can have lower performance if the dataset contains too much content.

    Getting all owner ids in the dataset

    
    public async Task QueryingAllIds(IMetadataRepository metadataRepository, CancellationToken cancellationToken)
    {
        // The result will contain all the owner ids in the dataset
        var request = metadataRepository.Query().Select(MetadataPathCollection.None).WithCancellation(cancellationToken);
    
        await foreach (var instance in request)
        {
            var id = instance.Id;
        }
    }
    
    

    By using the GetAllIdsAsync method, you can retrieve all the owner ids present in the dataset.

    In This Article
    Back to top
    Copyright © 2025 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)