Querying metadata
Setting up a IMetadataProvider
public IMetadataProvider CreateMetadataProvider(IServiceHttpClient serviceHttpClient, IServiceHostResolver serviceHostResolver, string sceneId, string sceneVersion)
{
return new MetadataProvider(serviceHttpClient, serviceHostResolver, sceneId, sceneVersion);
}
IMetadataProvider is the first entry point for any metadata query.
The constructor of the default implementation requires:
- An instance of
IServiceHttpClientandIServiceHostREsolver. See Best practices: dependency injection page of the Identity package documentation for more information. - A
sceneIdandsceneVersion. See Unity Cloud Storage documentation for more information.
Querying metadata
public async Task QueryingMetadata(IMetadataProvider metadataProvider)
{
// Perform the query
MatchCollection result = await metadataProvider.Query().ExecuteAsync();
// Iterate through the match collection
foreach (InstanceId ownerId in result.Keys)
{
// Each entries in the MatchCollection is attached to a single ownerId, and contains a MetadataObject
var metadata = result[ownerId];
}
}
With a IMetadataProvider, you can perform a query by combining the Query and the ExecuteAsync methods.
Once the query has been executed, you can easily iterate through the matches which each hold:
OwnerIdto point to the single Owner that contains metadataRequestedMetadatato expose the queried metadata attached to this owner.
Filtering
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(IMetadataProvider metadataProvider, string[] rootKeysToQuery)
{
// Query will return matches whose metadata content is restricted to the specified keys
MatchCollection result = await metadataProvider.Query()
.Select(rootKeysToQuery)
.ExecuteAsync();
}
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 from the metadata
public async Task QueryingMetadataWithSelectAllFilter(IMetadataProvider metadataProvider)
{
// Query will return matches whose metadata content is everything
MatchCollection result = await metadataProvider.Query()
.SelectAll()
.ExecuteAsync();
}
Using the SelectAll method, the whole metadata content for every instances will be included in the response.
SELECT-ONLY-ID filter, to query no metadata content
public async Task QueryingMetadataWithSelectOnlyIdFilter(IMetadataProvider metadataProvider)
{
// Query will return matches without metadata content, simply the id
MatchCollection result = await metadataProvider.Query()
.SelectOnlyId()
.ExecuteAsync();
}
Using the SelectOnlyId method, no metadata will be returned. The result will only contains the ids. This is especially useful when used with a Where filter.
INCLUDED-IN filter, to query a subset of scene instances
public async Task QueryingMetadataWithIncludedInFilter(IMetadataProvider metadataProvider, InstanceId[] ownerIdsToConsider)
{
// Query will only return matches that are attached to the specified owner Ids
MatchCollection result = await metadataProvider.Query()
.IncludedIn(ownerIdsToConsider)
.ExecuteAsync();
}
Using the IncludedIn method, you can specialize the query to ensure that only a specific subset of instances in the scene will be included in the response.
WHERE filter, to apply a metadata-based condition
public async Task QueryingMetadataWithWhereFilter(IMetadataProvider metadataProvider, IEnumerable<string> pathToKey, string expectedValue)
{
// Query will only contain matches that follow the specified criterium
MatchCollection result = await metadataProvider.Query()
.WhereKeyEquals(pathToKey, expectedValue)
.ExecuteAsync();
}
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.
LIMIT-TO filter, to specify the number of metadata records to return
public async Task QueryingMetadataWithWhereFilter(IMetadataProvider metadataProvider, int amount)
{
// Query will only contain matches that follow the specified criterium
MatchCollection result = await metadataProvider.Query()
.LimitTo(amount)
.ExecuteAsync();
}
Using the LimitTo method, you can specify the max number of metadata record to return. This is especially useful when querying a scene containing thousands of metadata records.
Fetching to much metadata at once can impact performance.
- When not set, the default value is set to the maximum value of an integer
Combining filters
public async Task QueryingMetadataAdvanced(IMetadataProvider metadataProvider, string[] rootKeysToQuery, InstanceId[] ownerIdsToConsider, IEnumerable<string> pathToKey, string expectedValue)
{
MatchCollection result = await metadataProvider.Query()
.Select(rootKeysToQuery)
.IncludedIn(ownerIdsToConsider)
.WhereKeyEquals(pathToKey, expectedValue)
.ExecuteAsync();
}
You can use any combination of the filters described above to build advanced queries and apply multiple filters at once.
Using the result
MatchCollection
public async Task UsingTheMatchCollectionReturnedFromAQuery(IMetadataProvider metadataProvider, InstanceId id)
{
MatchCollection result = await metadataProvider.Query().ExecuteAsync();
IEnumerable<InstanceId> ids = result.Keys;
IEnumerable<MetadataObject> metadataObjects = result.Values;
MetadataObject metadataObject = result[id];
}
Since MatchCollection is a ReadOnlyDictionnary, you can use your usual dictionaries method on it to get the data you want. For example, you can:
- Use the property Keys to fetch all the contained OwnerId
- Use the property Values to fetch all the contained MetadataObject
- Use the square bracket operator to fetch a specific MetadataObject associated to an OwnerId
MetadataObjects and MetadataValues
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Cloud.Common;
using Unity.Cloud.Metadata;
using UnityEngine;
#pragma warning disable S1481 // Unused local variables should be removed
#pragma warning disable S3903 // Types should be defined in named namespaces
#pragma warning disable S1144 // Unused private types or members should be removed
public class QueryingMetadataSnippet
{
// Trash code to allow snippets below to compile
public static class PlatformServices
{
public static IServiceHttpClient ServiceHttpClient { get; private set; }
public static IServiceHostResolver ServiceHostResolver { get; private set; }
}
#region CreateMetadataProvider
public IMetadataProvider CreateMetadataProvider(IServiceHttpClient serviceHttpClient, IServiceHostResolver serviceHostResolver, string sceneId, string sceneVersion)
{
return new MetadataProvider(serviceHttpClient, serviceHostResolver, sceneId, sceneVersion);
}
#endregion
#region QueryingMetadata
public async Task QueryingMetadata(IMetadataProvider metadataProvider)
{
// Perform the query
MatchCollection result = await metadataProvider.Query().ExecuteAsync();
// Iterate through the match collection
foreach (InstanceId ownerId in result.Keys)
{
// Each entries in the MatchCollection is attached to a single ownerId, and contains a MetadataObject
var metadata = result[ownerId];
}
}
#endregion
#region SelectAll
public async Task QueryingMetadataWithSelectAllFilter(IMetadataProvider metadataProvider)
{
// Query will return matches whose metadata content is everything
MatchCollection result = await metadataProvider.Query()
.SelectAll()
.ExecuteAsync();
}
#endregion
#region SelectOnlyId
public async Task QueryingMetadataWithSelectOnlyIdFilter(IMetadataProvider metadataProvider)
{
// Query will return matches without metadata content, simply the id
MatchCollection result = await metadataProvider.Query()
.SelectOnlyId()
.ExecuteAsync();
}
#endregion
#region Select
public async Task QueryingMetadataWithSelectFilter(IMetadataProvider metadataProvider, string[] rootKeysToQuery)
{
// Query will return matches whose metadata content is restricted to the specified keys
MatchCollection result = await metadataProvider.Query()
.Select(rootKeysToQuery)
.ExecuteAsync();
}
#endregion
#region IncludedIn
public async Task QueryingMetadataWithIncludedInFilter(IMetadataProvider metadataProvider, InstanceId[] ownerIdsToConsider)
{
// Query will only return matches that are attached to the specified owner Ids
MatchCollection result = await metadataProvider.Query()
.IncludedIn(ownerIdsToConsider)
.ExecuteAsync();
}
#endregion
#region Where
public async Task QueryingMetadataWithWhereFilter(IMetadataProvider metadataProvider, IEnumerable<string> pathToKey, string expectedValue)
{
// Query will only contain matches that follow the specified criterium
MatchCollection result = await metadataProvider.Query()
.WhereKeyEquals(pathToKey, expectedValue)
.ExecuteAsync();
}
#endregion
#region LimitTo
public async Task QueryingMetadataWithWhereFilter(IMetadataProvider metadataProvider, int amount)
{
// Query will only contain matches that follow the specified criterium
MatchCollection result = await metadataProvider.Query()
.LimitTo(amount)
.ExecuteAsync();
}
#endregion
#region Advanced
public async Task QueryingMetadataAdvanced(IMetadataProvider metadataProvider, string[] rootKeysToQuery, InstanceId[] ownerIdsToConsider, IEnumerable<string> pathToKey, string expectedValue)
{
MatchCollection result = await metadataProvider.Query()
.Select(rootKeysToQuery)
.IncludedIn(ownerIdsToConsider)
.WhereKeyEquals(pathToKey, expectedValue)
.ExecuteAsync();
}
#endregion
#region QueryingAllRootKeys
public async Task QueryingAllRootKeys(IMetadataProvider metadataProvider)
{
// The result will contain all the root keys in the scene
IEnumerable<string> result = await metadataProvider.GetAllKeysAsync();
}
#endregion
#region QueryingAllIds
public async Task QueryingAllIds(IMetadataProvider metadataProvider)
{
// The result will contain all the owner ids in the scene
IEnumerable<InstanceId> result = await metadataProvider.GetAllIdsAsync();
}
#endregion
#region MatchCollection
public async Task UsingTheMatchCollectionReturnedFromAQuery(IMetadataProvider metadataProvider, InstanceId id)
{
MatchCollection result = await metadataProvider.Query().ExecuteAsync();
IEnumerable<InstanceId> ids = result.Keys;
IEnumerable<MetadataObject> metadataObjects = result.Values;
MetadataObject metadataObject = result[id];
}
#endregion
#region MetadataObject
public async Task UsingMetadataObjects(IMetadataProvider metadataProvider, InstanceId id, string rootKey, string subKey)
{
MatchCollection result = await metadataProvider.Query().ExecuteAsync();
//MetdataContainer can either be MetadataObject, MetadataArray or MetadataValues
MetadataContainer metadata = result[id][rootKey];
}
#endregion
#region MetadataValue
public async Task GettingAMetadataValue(IMetadataProvider metadataProvider, InstanceId id, string rootKey, string subKey)
{
MatchCollection result = await metadataProvider.Query().ExecuteAsync();
string myValue = result[id][rootKey][subKey].ToString();
}
#endregion
#pragma warning restore S1144 // Unused private types or members should be removed
#pragma warning restore S3903 // Types should be defined in named namespaces
#pragma warning restore S1481 // Unused local variables should be removed
}
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 ReadOnlyDictionnary 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.
Helper methods
Getting all keys in the scene
public async Task QueryingAllRootKeys(IMetadataProvider metadataProvider)
{
// The result will contain all the root keys in the scene
IEnumerable<string> result = await metadataProvider.GetAllKeysAsync();
}
By using the GetAllKeysAsync method, you can retrieve all the root keys that are present in the scene.
Note: This method can have lower performance if the scene contains too much content.
Getting all owner ids in the scene
public async Task QueryingAllIds(IMetadataProvider metadataProvider)
{
// The result will contain all the owner ids in the scene
IEnumerable<InstanceId> result = await metadataProvider.GetAllIdsAsync();
}
By using the GetAllIdsAsync method, you can retrieve all the owner ids present in the scene.