The TypedElementQueryBuilder allows for the construction of queries for use by the Typed Element Data Storage. There are two types of queries, simple and normal. Simple queries are guaranteed to be supported by the data storage backend and guaranteed to have no performance side effects. <Normal queries pending development.>
Queries are constructed with the following section:
- Select A list of the data objects that are returned as the result of the query.
- Count Counts the total number or rows that pass the filter.
- Where A list of conditions that restrict what's accepted by the query.
- DependsOn A list of systems outside the data storage that will be accessed by the query('s user).
- Compile Compiles the query into its final form and can be used afterwards.
Calls to the sections become increasingly restrictive, e.g. after calling Where only DependsOn can be called again.
Arguments to the various functions take a pointer to a description of a UStruct. These can be provided in the follow ways:
- By using the templated version, e.g. Any<FStructExample>()
- By calling the static StaticStruct() function on the UStruct, e.g. FStructExample::StaticStruct();
- By name using the Type or TypeOptional string operator, e.g. "/Script/ExamplePackage.FStructExample"_Type or "/Script/OptionalPackage.FStructOptional"_TypeOptional All functions allow for a single type to be added or a list of types, e.g. ReadOnly(Type<FStructExample>() or ReadOnly({ Type<FStructExample1>(), FStructExample2::StaticStruct(), "/Script/ExamplePackage.FStructExample3"_Type });
Some functions allow binding to a callback. In these cases the arguments to the provided callback are analyzed and added to the query automatically. Const arguments are added as ReadOnly, while non-const arguments are added as ReadWrite. Callbacks can be periodically called if constructed as a processor, in which case the callback is triggered repeatedly, usually once per frame and called for all row (ranges) that match the query. If constructed as an observer the provided target type is monitored for actions like addition or deletion into/from any table and will trigger the callback once if the query matches. The following function signatures are accepted by "Select":
- void([const]Column&...)
- void([const]Column*...)
- void(RowHandle, [const]Column&...)
- void(<Context>&, [const]Column&...)
- void(<Context>&, RowHandle, [const]Column&...)
- void(<Context>&, [const]Column*...)
- void(<Context>&, const RowHandle*, [const]Column*...) Where <Context> is IQueryContext or FCachedQueryContext<...> e.g.: void(
FCachedQueryContext<Subsystem1, const Subsystem2>& Context,
RowHandle Row,
ColumnType0& ColumnA,
const ColumnType1& ColumnB) {...}
FCachedQueryContext can be used to store cached pointers to dependencies to reduce the overhead of retrieving these. The same const principle as for other arguments applies so dependencies marked as const can only be accessed as read-only or otherwise can be accessed as readwrite.
The following is a simplified example of these options combined together: FProcessor Info( EQueryTickPhase::FrameEnd, DataStorage->GetQueryTickGroupName(EQueryTickGroups::SyncExternalToDataStorage); Query = Select(FName(TEXT("Example Callback")), Info, [](FCachedQueryContext<Subsystem1, const Subsystem2>&, const FDataExample1&, FDataExample2&) {});
"Select" is constructed with:
- ReadOnly: Indicates that the data object will only be read from
- ReadWrite: Indicated that the data object will be read and written to.
"Count" does not have any construction options.
"Where" is constructed with:
- All: The query will be accepted only if all the types listed here are present in a table.
- Any: The query will be accepted if at least one of the listed types is present in a table.
- Not: The query will be accepted if none of the listed types are present in a table. The above construction calls can be mixed and be called multiple times. All functions accept a nullptr for the type in which case the call will have no effect. This can be used to reference types in plugins that may not be loaded when using the TypeOptional string operator.
"DependsOn" is constructed with:
- ReadOnly: Indicates that the external system will only be used to read data from.
- ReadWrite: Indicates that the external system will be used to write data to.
Usage example: FQueryDescription Query = Select() .ReadWrite({ FDataExample1::StaticStruct() }) .ReadWrite<FDataExample2, FDataExample3>() .ReadOnly<FDataExample4>() .Where() .All<FTagExample1, FDataExample5>() .Any("/Script/ExamplePackage.FStructExample"_TypeOptional) .None(FTagExample2::StaticStruct()) .DependsOn() .ReadOnly<USystemExample1, USystemExample2>() .ReadWrite(USystemExample2::StaticClass()) .Compile();
Creating a query is expensive on the builder and the back-end side. It's therefore recommended to create a query and store its compiled form for repeated use instead of rebuilding the query on every update.