Qerent.QMH.Data.Client 0.4.0-beta

This is a prerelease version of Qerent.QMH.Data.Client.
dotnet add package Qerent.QMH.Data.Client --version 0.4.0-beta
                    
NuGet\Install-Package Qerent.QMH.Data.Client -Version 0.4.0-beta
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Qerent.QMH.Data.Client" Version="0.4.0-beta" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Qerent.QMH.Data.Client" Version="0.4.0-beta" />
                    
Directory.Packages.props
<PackageReference Include="Qerent.QMH.Data.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Qerent.QMH.Data.Client --version 0.4.0-beta
                    
#r "nuget: Qerent.QMH.Data.Client, 0.4.0-beta"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Qerent.QMH.Data.Client@0.4.0-beta
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Qerent.QMH.Data.Client&version=0.4.0-beta&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Qerent.QMH.Data.Client&version=0.4.0-beta&prerelease
                    
Install as a Cake Tool

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 Elements lists or From/To ranges
  • 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: ModelAttribute object

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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