Qerent.QMH.Data.Client
0.4.0-beta
dotnet add package Qerent.QMH.Data.Client --version 0.4.0-beta
NuGet\Install-Package Qerent.QMH.Data.Client -Version 0.4.0-beta
<PackageReference Include="Qerent.QMH.Data.Client" Version="0.4.0-beta" />
<PackageVersion Include="Qerent.QMH.Data.Client" Version="0.4.0-beta" />
<PackageReference Include="Qerent.QMH.Data.Client" />
paket add Qerent.QMH.Data.Client --version 0.4.0-beta
#r "nuget: Qerent.QMH.Data.Client, 0.4.0-beta"
#:package Qerent.QMH.Data.Client@0.4.0-beta
#addin nuget:?package=Qerent.QMH.Data.Client&version=0.4.0-beta&prerelease
#tool nuget:?package=Qerent.QMH.Data.Client&version=0.4.0-beta&prerelease
Qerent QMH Data Client SDK
This document covers basic usage guidelines for using the Qerent.QMH.Data.Client SDK for bulk data operations with QMH.
Feature Summary:
- Efficient Serialisation of QMH data
- Multiple concurrency model options - Threaded, Task Based, None
- Dimensional slicing and filtering
Prerequisites
This SDK requires a minimum of .net 8.0.
Also see NugetPackage Dependencies.
QMHFlightClient
Usage requires creating a new instance of a client object and calling Get with a FlightRequest object instance
Constructor
| Parameter | Required | Description |
|---|---|---|
address |
Required | Address of the QMH instance (eg: https://my.qerent.com) |
apiKey |
Required | API key for the target instance |
parallelMode |
Optional | (Threaded, Task, None) Sets the parallel mechanism for retrieving endpoints. Default = Task |
ignoreCertificateErrors |
Optional | Don't verify certificate details |
loggerFactory |
Optional | Logger factory for Microsoft.Extensions.Logging to emit logs to |
Methods
| Method | Returns | Description |
|---|---|---|
GetAsync(FlightRequest request) |
IDictionary<string, IDictionary<string, ModelAttribute>> |
Return data for the queries contained in request. |
GetAsync(FlightRequest request)
Top level dictionary is keyed on DatasetIdentifier. The second level dictionary is keyed on the full Attribute Path. e.g. Path.To.My[Attribute].
Request Structure:
FlightRequest
+-- Queries
| +-- 0
| | +-- QueryText = '//*'
| | +-- DatasetIdentifiers = ['DatasetIdentifier1', 'DatasetIdentifier2']
| | +-- Slicers = [Optional: Array of FlightRequestDimensionalSlice]
| | | +-- 0
| | | | +-- Ranges = [Array of FlightRequestDimensionalRange]
| | | | | +-- 0
| | | | | | +-- Dimension = 'Time'
| | | | | | +-- Elements = ['2024', '2025'] (Optional)
| | | | | | +-- From = '2024' (Optional)
| | | | | | +-- To = '2025' (Optional)
| | +-- DimensionFilters = [Optional: Array of dimension name arrays]
| | | +-- ['Time']
| | | +-- ['Time', 'Scenario']
| +-- 1
| | +-- QueryText = '//*'
| | +-- DatasetIdentifiers = ['DatasetIdentifier1', 'DatasetIdentifier2']
+-- MetadataOptions = [Optional]
| +-- IncludeInputMask = false
| +-- IncludeFormat = false
| +-- IncludeUnitOfMeasure = false
| +-- IncludeFormulae = false
| +-- IncludeAnnotations = false
| +-- IncludeDimensionMap = true
Example:
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "//*",
DatasetIdentifiers = [ "DatasetIdentifier1" ]
}
]
};
var client = new QMHFlightClient("https://myqmhhost", "myapikey" );
var result = await client.GetAsync(request);
Response Structure:
IDictionary<string, IDictionary<string, ModelAttribute>>
+-- "DatasetIdentifier1" (string key)
| +-- "Path.To.My[Attribute1]" (string key) = ModelAttribute
| | +-- AttributeInfo (ModelAttributeInfo)
| | | +-- Id (int) = 123
| | | +-- Name (string) = "Attribute1"
| | | +-- FullPathName() (method) = "Path.To.My[Attribute1]"
| | | +-- QueryPath() (method) = "/Path/To/My/{Attribute1}"
| | | +-- DimensionMap (ModelAttributeDimensionMap)
| | | | +-- DimensionMapId (int) = 1
| | | | +-- Dimensions (string[]) = ["Time", "Scenario"]
| | | | +-- CellCoordinates (string[][]) = [["2024", "Actual"], ["2024", "Budget"], ...]
| | +-- GetValues() (method) = IReadOnlyList<double> { 1.0, 2.0, 3.0, 4.0 }
| |
| +-- "Path.To.My[Attribute2]" (string key) = ModelAttribute
| | +-- AttributeInfo (ModelAttributeInfo)
| | | +-- Id (int) = 124
| | | +-- Name (string) = "Attribute2"
| | | +-- FullPathName() (method) = "Path.To.My[Attribute2]"
| | | +-- QueryPath() (method) = "/Path/To/My/{Attribute2}"
| | | +-- DimensionMap (ModelAttributeDimensionMap)
| | | | +-- DimensionMapId (int) = 1
| | | | +-- Dimensions (string[]) = ["Time", "Scenario"]
| | | | +-- CellCoordinates (string[][]) = [["2024", "Actual"], ["2024", "Budget"], ...]
| | +-- GetValues() (method) = IReadOnlyList<double> { 5.0, 6.0, 7.0, 8.0 }
|
+-- "DatasetIdentifier2" (string key)
+-- "Path.To.My[Attribute1]" (string key) = ModelAttribute
+-- AttributeInfo (ModelAttributeInfo)
| +-- Id (int) = 456
| +-- Name (string) = "Attribute1"
| +-- FullPathName() (method) = "Path.To.My[Attribute1]"
| +-- QueryPath() (method) = "/Path/To/My/{Attribute1}"
| +-- DimensionMap (ModelAttributeDimensionMap)
| | +-- DimensionMapId (int) = 2
| | +-- Dimensions (string[]) = ["Time"]
| | +-- CellCoordinates (string[][]) = [["2024"], ["2025"], ...]
+-- GetValues() (method) = IReadOnlyList<double> { 10.0, 20.0, 30.0 }
XPath Query Syntax
The QueryText property uses XPath-like syntax to filter and select attributes from the model.
Path Separators
- Use
/to separate levels in the hierarchy - Example:
/Level1/Level2/Level3
Wildcards
*- Matches all attributes at the current level//*- Matches all attributes at any level (recursive)/*/*/*- Matches all attributes exactly 3 levels deep
Combining Queries
/* | /*/* | /*/*/*- Match all attributes at 1, 2, and 3 levels deep.
Escaping Special Characters
- Use curly braces
{}to wrap phrase segments containing spaces or special characters (these are not allowed in regular XPath). - Example:
/{My Company}/{North Region}/Sales/{Q1 2024} - Without escaping:
/My Company/North Region/Sales/Q1 2024(invalid) - With escaping:
/{My Company}/{North Region}/Sales/{Q1 2024}(valid)
Query Pattern Reference
| Query Pattern | Description |
|---|---|
//* |
All attributes in the model |
/Sales/* |
All attributes directly under Sales |
/Sales/Revenue/* |
All attributes under Sales/Revenue |
/*/*/* |
All attributes at exactly 3 levels deep |
/{Global Assumptions}/Ingredients/{Flour}/{Price Per Unit} |
Specific attribute with escaped phrases |
Filtering and Slicing
The QMH Flight Data Client supports three types of filtering/slicing:
1. XPath Filtering (QueryText)
Filter attributes by their path in the model hierarchy:
- Filtering by Path: Use XPath patterns to select specific branches or attributes from the model hierarchy
- Filtering by Depth: Use wildcards to select attributes at specific depths
- Filtering by Name: Specify exact paths to retrieve specific attributes
Example: QueryText = "/Sales/Revenue/*" returns all attributes under the Sales/Revenue path.
2. Dimensional Slicing (Slicers)
Slicers filter the coordinates within attributes based on dimension elements. Each slicer:
- Applies to cublets with matching dimensions
- Specifies which dimension elements to include in the response
- Can use explicit
Elementslists orFrom/Toranges - Reduces the shape of returned cublets by selecting specific slices
Key Points:
- Slicers match to the target cublet dimensions.
- Example: To slice a
[Time, Scenario]cublet to only "2024" and "Actual", provide a slicer with both Time and Scenario ranges - If slicing reduces a cublet's shape to match another existing shape (e.g.,
[Time, Scenario]→[Time]), results are merged into the same table for transmission efficiency
3. Dimension Filters (DimensionFilters)
Dimension filters limit results to attributes with specific dimension combinations:
- Specify allowed dimension shapes as string arrays of the dimension names
- Example:
[["Time"], ["Time", "Scenario"]]returns only cublets with those exact dimension combinations - Filters apply to the attributes returned, not the cell values
Filtering Comparison
| Type | What it filters | Example |
|---|---|---|
| XPath | Attribute paths in model | "/Sales/*" - Only Sales attributes |
| Slicers | Dimension element values | Time: [2024, 2025] - Only 2024 and 2025 data |
| DimensionFilters | Cublet dimension shapes | [["Time"]] - Only single-dimension Time cublets |
These can be combined for powerful data retrieval: use XPath to select attributes, DimensionFilters to specify shapes, and Slicers to extract specific dimensional slices.
Working with Results
The GetAsync method returns a nested dictionary structure:
IDictionary<string, IDictionary<string, ModelAttribute>>
- Outer Dictionary Key: Dataset Identifier (string)
- Inner Dictionary Key: Full attribute path (string) - e.g.,
"Path.To.My[AttributeName]" - Value:
ModelAttributeobject
ModelAttribute Structure
Each ModelAttribute contains:
| Member | Type | Description |
|---|---|---|
AttributeInfo |
ModelAttributeInfo | Metadata about the attribute |
GetValues() |
IReadOnlyList<double> | Method that returns the attribute's values |
ModelAttributeInfo Properties
The AttributeInfo object provides detailed metadata:
| Member | Type | Description |
|---|---|---|
Id |
int | Unique identifier for the attribute |
Name |
string | Attribute name (e.g., "Revenue") |
FullPathName() |
method → string | Returns full dot-notation path (e.g., "Path.To.My[AttributeName]") |
QueryPath() |
method → string | Returns XPath query format (e.g., "/Path/To/My/{AttributeName}") |
DimensionMap |
ModelAttributeDimensionMap | Dimensional metadata for the attribute |
ModelAttributeDimensionMap Structure
The DimensionMap provides information about the attribute's dimensions:
| Property | Type | Description |
|---|---|---|
DimensionMapId |
int | Unique identifier for this dimensional shape |
Dimensions |
string[] | Array of dimension names (e.g., ["Time", "Scenario"]) |
CellCoordinates |
string[][] | Array of coordinate combinations (e.g., [["2024", "Actual"], ["2024", "Budget"]]) |
Accessing Data - Basic Example
var result = await client.GetAsync(request);
// Get data for a specific dataset
var myDataset = result["DatasetIdentifier"];
// Get a specific attribute
var attribute = myDataset["Path.To.My[AttributeName]"];
// Get the values using the GetValues() method
var values = attribute.GetValues(); // IReadOnlyList<double>
// Access basic metadata
var attributeId = attribute.AttributeInfo.Id;
var attributeName = attribute.AttributeInfo.Name;
var fullPath = attribute.AttributeInfo.FullPathName();
var queryPath = attribute.AttributeInfo.QueryPath();
Accessing Data - With Dimensional Metadata
var result = await client.GetAsync(request);
foreach (var dataset in result)
{
var datasetId = dataset.Key;
Console.WriteLine($"Dataset: {datasetId}");
foreach (var attr in dataset.Value)
{
var attributePath = attr.Key;
var modelAttribute = attr.Value;
// Get attribute metadata
var info = modelAttribute.AttributeInfo;
Console.WriteLine($" Attribute: {info.Name}");
Console.WriteLine($" ID: {info.Id}");
Console.WriteLine($" Full Path: {info.FullPathName()}");
// Get dimensional information
var dimMap = info.DimensionMap;
Console.WriteLine($" Dimensions: [{string.Join(", ", dimMap.Dimensions)}]");
Console.WriteLine($" Shape ID: {dimMap.DimensionMapId}");
// Get values (call the method, not property access)
var values = modelAttribute.GetValues();
Console.WriteLine($" Values: [{string.Join(", ", values)}]");
// Map coordinates to values
for (int i = 0; i < values.Count; i++)
{
var coordinates = dimMap.CellCoordinates[i];
Console.WriteLine($" {string.Join(" x ", coordinates)}: {values[i]}");
}
}
}
Working with Multidimensional Data
var result = await client.GetAsync(request);
var attribute = result["MyDataset"]["Sales.Revenue[Total]"];
// Check dimensions
var dimensions = attribute.AttributeInfo.DimensionMap.Dimensions;
// e.g., ["Time", "Scenario"]
// Get coordinate mapping
var coordinates = attribute.AttributeInfo.DimensionMap.CellCoordinates;
// e.g., [["2024", "Actual"], ["2024", "Budget"], ["2025", "Actual"], ["2025", "Budget"]]
// Get values (note: this is a method call)
var values = attribute.GetValues();
// e.g., [100.0, 110.0, 120.0, 130.0]
// Create a lookup dictionary
var dataLookup = new Dictionary<string, double>();
for (int i = 0; i < coordinates.Length; i++)
{
var key = string.Join("|", coordinates[i]);
dataLookup[key] = values[i];
}
// Access specific coordinate value
var value2024Actual = dataLookup["2024|Actual"]; // 100.0
var value2025Budget = dataLookup["2025|Budget"]; // 130.0
Metadata Options
The MetadataOptions property on FlightRequest allows you to request additional metadata about attributes:
| Property | Type | Description |
|---|---|---|
IncludeInputMask |
bool | Include input masks for attributes. Default = false |
IncludeFormat |
bool | Include formatting information. Default = false |
IncludeUnitOfMeasure |
bool | Include units of measure. Default = false |
IncludeFormulae |
bool | Include attribute formulae. Default = false |
IncludeAnnotations |
bool | Include attribute annotations. Default = false |
IncludeDimensionMap |
bool | Include dimensional map data. Default = true |
Dimension Map
When IncludeDimensionMap = true, the response includes dimensional metadata describing:
- DimensionMapId: Unique identifier for each dimensional shape
- Dimensions: Array of dimension names and IDs for the cublet
- Elements: The specific dimension elements and their indices
- Coordinates: Cell coordinates mapping dimension combinations to data positions
This metadata is particularly useful for:
- Understanding the structure of multidimensional attributes
- Mapping dimension elements to array positions
- Processing data with dynamic dimension handling
Note: Dimensional metadata is returned in the Arrow Flight stream metadata and is accessible through the GetRawAsync method, which provides access to the raw Arrow record batches and associated metadata.
Understanding Slicers in Detail
Slicers are powerful tools for working with multidimensional data. Here's how they work:
Slicer Matching Rules
A slicer only applies to attributes whose dimensions exactly match the dimensions specified in the slicer's ranges:
- Matching Example: A slicer with
[Time, Scenario]ranges applies to attributes with[Time, Scenario]dimensions - Non-Matching Example: The same slicer does NOT apply to attributes with only
[Time]dimensions or[Time, Region, Scenario]dimensions
Normalization and Shape Reduction
When a slicer selects a single element from a dimension, it effectively removes that dimension from the result shape:
Example:
// Original attribute has dimensions: [Time, Scenario, Region]
// Values might be a 3D array: Time x Scenario x Region
var slicer = new FlightRequestDimensionalSlice
{
Ranges =
[
new FlightRequestDimensionalRange { Dimension = "Time", Elements = ["2024", "2025"] },
new FlightRequestDimensionalRange { Dimension = "Scenario", Elements = ["Actual"] }, // Single element
new FlightRequestDimensionalRange { Dimension = "Region", Elements = ["North", "South"] }
]
};
// Result shape becomes: [Time, Region] (2D array)
// Because Scenario was reduced to a single element, it's effectively removed
// Result: 2 time periods x 2 regions = 4 values
Shape Merging
If the resulting shape after slicing matches an existing shape in the response, the data is merged into the same table:
- Attributes with original shape
[Time]and sliced[Time, Scenario]→[Time]will appear in the same response table - This enables efficient data consolidation across different source shapes
Constant (Dimensionless) Attributes
To work with constant attributes (those without dimensions), use:
DimensionFilters = new string[][] { FlightRequestQuery.ConstantDimensionFilter }
// ConstantDimensionFilter is an empty array: new string[0]
This filters the result to only include scalar/constant values without any dimensional structure.
FlightRequestQuery Properties Reference
Required Properties
| Property | Type | Description |
|---|---|---|
QueryText |
string | XPath query string for selecting attributes (e.g., "//" or "/Sales/Revenue/") |
DatasetIdentifiers |
string[] | Array of dataset identifiers to query |
Optional Properties
| Property | Type | Description |
|---|---|---|
Slicers |
FlightRequestDimensionalSlice[] | Dimensional slicers to filter cublet values by specific dimension elements |
DimensionFilters |
string[][] | Filter to limit results to specific cublet dimension combinations |
FlightRequestDimensionalSlice Structure
| Property | Type | Description |
|---|---|---|
Ranges |
FlightRequestDimensionalRange[] | Array of dimension ranges that define the slice |
FlightRequestDimensionalRange Structure
| Property | Type | Description |
|---|---|---|
Dimension |
string | Name of the dimension (e.g., "Time", "Scenario", "Region") |
Elements |
List<string> | Explicit list of dimension elements to include (optional) |
From |
string | Starting element for a range (optional, alternative to Elements) |
To |
string | Ending element for a range (optional, alternative to Elements) |
Note: The use of Elements or From and To are mutually exclusive. That is, either specify the elements or a range.
Examples
Example 1: Query All Attributes from a Single Dataset
var client = new QMHFlightClient("https://myqmhhost.com", "myapikey");
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "//*",
DatasetIdentifiers = [ "MyDataset" ]
}
]
};
var result = await client.GetAsync(request);
// Access data for a specific dataset
var datasetData = result["MyDataset"];
// Iterate through all attributes
foreach (var attribute in datasetData)
{
var path = attribute.Key; // e.g., "Path.To.My[Attribute1]"
var modelAttribute = attribute.Value;
var values = modelAttribute.GetValues(); // double[]
Console.WriteLine($"{path}: [{string.Join(", ", values)}]");
}
Example 2: Filter Attributes Using XPath Queries
The QueryText property supports XPath-like syntax for filtering attributes. Use curly braces {} to escape phrases containing special characters.
var client = new QMHFlightClient("https://myqmhhost.com", "myapikey");
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
// Query specific path - all attributes under Sales/Revenue
QueryText = "/Sales/Revenue/*",
DatasetIdentifiers = [ "Q1Results" ]
}
]
};
var result = await client.GetAsync(request);
Example 3: Query Specific Attributes with Path Segments
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
// Query a specific attribute deep in the hierarchy
// Use curly braces for phrases with spaces or special characters
QueryText = "/{Global Assumptions}/Ingredients/{Flour}/{Price Per Unit}",
DatasetIdentifiers = [ "BudgetModel" ]
}
]
};
var result = await client.GetAsync(request);
var priceData = result["BudgetModel"]["Global Assumptions.Ingredients.Flour[Price Per Unit]"];
Example 4: Query Multiple Datasets with the Same Query
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "/Financials/Revenue/*",
DatasetIdentifiers = [ "Dataset2023", "Dataset2024", "Dataset2025" ]
}
]
};
var result = await client.GetAsync(request);
// Result contains data from all three datasets
var revenue2023 = result["Dataset2023"];
var revenue2024 = result["Dataset2024"];
var revenue2025 = result["Dataset2025"];
Example 5: Multiple Queries in a Single Request
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "/Sales/*",
DatasetIdentifiers = [ "Q1Data", "Q2Data" ]
},
new FlightRequestQuery
{
QueryText = "/Expenses/*",
DatasetIdentifiers = [ "Q1Data", "Q2Data" ]
},
new FlightRequestQuery
{
QueryText = "/Profit/*",
DatasetIdentifiers = [ "AnnualSummary" ]
}
]
};
var result = await client.GetAsync(request);
// Access different datasets and their attributes
var q1Sales = result["Q1Data"].Where(kvp => kvp.Key.StartsWith("Sales")).ToList();
var q1Expenses = result["Q1Data"].Where(kvp => kvp.Key.StartsWith("Expenses")).ToList();
Example 6: Working with Hierarchical Paths
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
// Navigate through nested objects and get all leaf attributes
QueryText = "/{Company A}/{North Region}/Stores/{Store 123}/Inventory/*",
DatasetIdentifiers = [ "RetailData" ]
}
]
};
var result = await client.GetAsync(request);
foreach (var attr in result["RetailData"])
{
Console.WriteLine($"Attribute: {attr.Value.AttributeInfo.AttributeName}");
Console.WriteLine($"Full Path: {attr.Value.AttributeInfo.FullPathName()}");
Console.WriteLine($"Values: [{string.Join(", ", attr.Value.GetValues())}]");
}
Example 7: Using Different Parallel Modes
// Task-based parallelism (default, recommended)
var clientTask = new QMHFlightClient(
"https://myqmhhost.com",
"myapikey",
ParallelMode.Task
);
// Thread-based parallelism
var clientThreaded = new QMHFlightClient(
"https://myqmhhost.com",
"myapikey",
ParallelMode.Threaded
);
// No parallelism (sequential processing)
var clientSequential = new QMHFlightClient(
"https://myqmhhost.com",
"myapikey",
ParallelMode.None
);
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "//*",
DatasetIdentifiers = [ "LargeDataset" ]
}
]
};
var result = await clientTask.GetAsync(request);
Example 8: With Logging Support
using Microsoft.Extensions.Logging;
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Trace);
});
var client = new QMHFlightClient(
"https://myqmhhost.com",
"myapikey",
ParallelMode.Task,
ignoreCertificateErrors: false,
loggerFactory: loggerFactory
);
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "//*",
DatasetIdentifiers = [ "MyDataset" ]
}
]
};
var result = await client.GetAsync(request);
// Logs will show endpoint fetching details
Example 9: Filtering and Slicing with XPath
The QueryText supports XPath patterns for filtering attributes at different levels:
var request = new FlightRequest
{
Queries =
[
// Get all attributes at any level
new FlightRequestQuery
{
QueryText = "//*",
DatasetIdentifiers = [ "MyDataset" ]
},
// Get attributes at specific depth (3 levels deep)
new FlightRequestQuery
{
QueryText = "/*/*/*",
DatasetIdentifiers = [ "MyDataset" ]
},
// Get specific branch of the hierarchy
new FlightRequestQuery
{
QueryText = "/Revenue/Products/*",
DatasetIdentifiers = [ "MyDataset" ]
},
// Get deeply nested specific attribute
new FlightRequestQuery
{
QueryText = "/{Yeast To West}/{Western Cape}/Region/{Cape Town}/Bakeries/{Low Stock Bakery}/Sales/Bread/{Forecasted Daily Sales}",
DatasetIdentifiers = [ "CMFileData" ]
}
]
};
var result = await client.GetAsync(request);
Example 10: Using Dimensional Slicers
Slicers allow you to filter attribute values based on specific dimension elements. Each slicer is applied to cublets with matching dimensions.
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "/Sales/Revenue/*",
DatasetIdentifiers = [ "FinancialModel" ],
Slicers =
[
// Slice to get only 2024 and 2025 data for the "Actual" scenario
new FlightRequestDimensionalSlice
{
Ranges =
[
new FlightRequestDimensionalRange
{
Dimension = "Time",
Elements = new List<string> { "2024", "2025" }
},
new FlightRequestDimensionalRange
{
Dimension = "Scenario",
Elements = new List<string> { "Actual" }
}
]
}
]
}
]
};
var result = await client.GetAsync(request);
Example 11: Using Range-Based Slicers
You can specify a range of dimension elements using From and To instead of explicit Elements:
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "//*",
DatasetIdentifiers = [ "BudgetData" ],
Slicers =
[
new FlightRequestDimensionalSlice
{
Ranges =
[
// Get all months from Jan to Jun
new FlightRequestDimensionalRange
{
Dimension = "Month",
From = "Jan",
To = "Jun"
}
]
}
]
}
]
};
var result = await client.GetAsync(request);
Example 12: Using Dimension Filters
Dimension filters limit the result set to only include cublets with specific dimension combinations:
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "//*",
DatasetIdentifiers = [ "MyDataset" ],
// Only return attributes with exactly these dimension combinations
DimensionFilters = new string[][]
{
new string[] { "Time" }, // Cublets with only Time dimension
new string[] { "Time", "Scenario" } // Cublets with Time and Scenario dimensions
}
}
]
};
var result = await client.GetAsync(request);
// Result will only contain attributes that match the specified dimension shapes
Example 13: Combining Slicers and Dimension Filters
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "/Revenue/*",
DatasetIdentifiers = [ "ComprehensiveModel" ],
// First, filter to only get cublets with Time and Scenario dimensions
DimensionFilters = new string[][]
{
new string[] { "Time", "Scenario" }
},
// Then, slice to get specific time periods and scenarios
Slicers =
[
new FlightRequestDimensionalSlice
{
Ranges =
[
new FlightRequestDimensionalRange
{
Dimension = "Time",
From = "2024-Q1",
To = "2024-Q4"
},
new FlightRequestDimensionalRange
{
Dimension = "Scenario",
Elements = new List<string> { "Budget", "Forecast" }
}
]
}
]
}
]
};
var result = await client.GetAsync(request);
Example 14: Working with Dimensional Metadata
The dimension map is automatically included in the response for every attribute and provides coordinate information:
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "/Sales/Revenue/*",
DatasetIdentifiers = [ "FinancialModel" ]
}
]
};
var result = await client.GetAsync(request);
var dataset = result["FinancialModel"];
foreach (var kvp in dataset)
{
var attributePath = kvp.Key;
var attribute = kvp.Value;
// Access dimensional metadata
var dimMap = attribute.AttributeInfo.DimensionMap;
Console.WriteLine($"Attribute: {attribute.AttributeInfo.Name}");
Console.WriteLine($"Dimension Map ID: {dimMap.DimensionMapId}");
Console.WriteLine($"Dimensions: [{string.Join(", ", dimMap.Dimensions)}]");
// Get values and coordinates
var values = attribute.GetValues();
var coords = dimMap.CellCoordinates;
// Display each value with its coordinates
for (int i = 0; i < values.Count; i++)
{
Console.WriteLine($" {string.Join(" x ", coords[i])}: {values[i]}");
}
}
// Example output:
// Attribute: Total Revenue
// Dimension Map ID: 5
// Dimensions: [Time, Scenario]
// 2024 x Actual: 1000000.0
// 2024 x Budget: 1100000.0
// 2025 x Actual: 1200000.0
// 2025 x Budget: 1300000.0
Example 15: Requesting Additional Metadata
Use MetadataOptions to request additional metadata beyond the default dimension map:
var request = new FlightRequest
{
Queries =
[
new FlightRequestQuery
{
QueryText = "//*",
DatasetIdentifiers = [ "MyDataset" ]
}
],
MetadataOptions = new MetadataOptions
{
IncludeDimensionMap = true, // Always included by default
IncludeFormat = true, // Include number formatting info
IncludeFormulae = true, // Include calculation formulas
IncludeAnnotations = true, // Include attribute annotations
IncludeInputMask = true, // Include input masks
IncludeUnitOfMeasure = true // Include units
}
};
// Note: Additional metadata (beyond DimensionMap) is returned in the
// Arrow Flight stream metadata and is accessible via GetRawAsync
var rawResult = await client.GetRawAsync(request);
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Apache.Arrow.Flight (>= 22.0.1)
- Grpc.Net.Client.Web (>= 2.71.0)
- Microsoft.Extensions.Logging (>= 9.0.9)
- System.Linq.Async (>= 6.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.4.0-beta | 62 | 4/18/2026 |
| 0.3.0-beta | 88 | 2/6/2026 |
| 0.1.0-beta | 149 | 10/16/2025 |