UniverseQuery 2.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package UniverseQuery --version 2.1.0
                    
NuGet\Install-Package UniverseQuery -Version 2.1.0
                    
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="UniverseQuery" Version="2.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="UniverseQuery" Version="2.1.0" />
                    
Directory.Packages.props
<PackageReference Include="UniverseQuery" />
                    
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 UniverseQuery --version 2.1.0
                    
#r "nuget: UniverseQuery, 2.1.0"
                    
#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.
#addin nuget:?package=UniverseQuery&version=2.1.0
                    
Install UniverseQuery as a Cake Addin
#tool nuget:?package=UniverseQuery&version=2.1.0
                    
Install UniverseQuery as a Cake Tool

Universe

A simpler way of querying a CosmosDb Namespace

Installation

dotnet add package Universe

How-to:

  1. Your models / cosmos entities should inherit from the interface
public class MyCosmosEntity : ICosmicEntity
{
  public string FirstName { get; set; }
  
  public string LastName { get; set; }
  
  // The properties below are implementations from ICosmicEntity
  public string id { get; set; }
  public DateTime AddedOn { get; set; }
  public DateTime ModifiedOn { get; set; }

  [JsonIgnore]
  public string PartitionKey => FirstName;
}
  1. Create a repository like so:
public class MyRepository : Galaxy<MyModel>
{
    public MyRepository(CosmosClient client, string database, string container, string partitionKey) : base(client, database, container, partitionKey)
    {
    }
}

// If you want to see debug information such as the full Query text executed, use the format below:
public class MyRepository : Galaxy<MyModel>
{
    public MyRepository(CosmosClient client, string database, string container, string partitionKey) : base(client, database, container, partitionKey, true)
    {
    }
}
  1. In your Startup.cs / Main method / Program.cs, configure the CosmosClient like so:
_ = services.AddScoped(_ => new CosmosClient(
    System.Environment.GetEnvironmentVariable("CosmosDbUri"),
    System.Environment.GetEnvironmentVariable("CosmosDbPrimaryKey"),
    clientOptions: new CosmosClientOptions()
    {
        Serializer = new UniverseSerializer(), // This is from Universe.Options
        AllowBulkExecution = true // This will tell the underlying code to allow async bulk operations
    }
));
  1. In your Startup.cs / Main method / Program.cs, configure your CosmosDb repository like so:
_ = services.AddScoped<IGalaxy<MyModel>, MyRepository>(service => new MyRepository(
    client: service.GetRequiredService<CosmosClient>(),
    database: "database-name",
    container: "container-name",
    partitionKey: "/partitionKey"
));
  1. Inject your IGalaxy<MyModel> dependency into your classes and enjoy a simpler way to query CosmosDb

Basic Operations

Simple Query Operations

// Get a single document by id and partition key
(Gravity gravity, MyModel model) = await galaxy.Get("document-id", "partition-key-value");

// Basic query with a single filter condition
(Gravity gravity, MyModel model) = await galaxy.Get(
    clusters: new List<Cluster>() 
    {
        new Cluster(Catalysts: new List<Catalyst>
        {
            new Catalyst(nameof(MyModel.PropertyName), "value")
        })
    }
);

Creating Documents

// Create a single document
MyModel model = new MyModel 
{ 
    PropertyName = "value",
    // Set other properties
};
(Gravity gravity, string id) = await galaxy.Create(model);

// Bulk create multiple documents
List<MyModel> models = new List<MyModel>
{
    new MyModel { /* properties */ },
    new MyModel { /* properties */ }
};
Gravity gravity = await galaxy.Create(models);

Updating Documents

// Update a single document
model.PropertyName = "new value";
(Gravity gravity, MyModel updatedModel) = await galaxy.Modify(model);

// Bulk update multiple documents
foreach (MyModel item in models)
{
    item.PropertyName = "new value";
}
Gravity gravity = await galaxy.Modify(models);

Deleting Documents

// Delete a document
Gravity gravity = await galaxy.Remove("document-id", "partition-key-value");

Advanced Query Examples

Complex Queries with Multiple Conditions

// Query with multiple conditions in a single cluster
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>()
    {
        new Cluster(Catalysts: new List<Catalyst>
        {
            new Catalyst(nameof(MyModel.PropertyName), "value"),
            new Catalyst(nameof(MyModel.NumberProperty), 123, Where: Q.Where.And)
        })
    }
);

// Query with multiple clusters (combining conditions with AND/OR)
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>()
    {
        new Cluster(Catalysts: new List<Catalyst>
        {
            new Catalyst(nameof(MyModel.PropertyName), "value"),
            new Catalyst(nameof(MyModel.AnotherProperty), 123, Where: Q.Where.Or)
        }, Where: Q.Where.And),
        new Cluster(Catalysts: new List<Catalyst>
        {
            new Catalyst(nameof(MyModel.Status), "Active")
        })
    }
);

Special Operators

// Using In operator for array properties
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>()
    {
        new Cluster(Catalysts: new List<Catalyst>
        {
            new Catalyst(nameof(MyModel.Tags), "tag1", Operator: Q.Operator.In)
        })
    }
);

// Check if a property is defined
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>()
    {
        new Cluster(Catalysts: new List<Catalyst>
        {
            new Catalyst(nameof(MyModel.OptionalProperty), Operator: Q.Operator.Defined)
        })
    }
);

// Comparison operators
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>()
    {
        new Cluster(Catalysts: new List<Catalyst>
        {
            new Catalyst(nameof(MyModel.NumberProperty), 100, Operator: Q.Operator.Gt)
        })
    }
);

Sorting and Column Selection

// Query with sorting
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>() { /* query conditions */ },
    sorting: new List<Sorting.Option>
    {
        new Sorting.Option(nameof(MyModel.PropertyName), Sorting.Direction.DESC)
    }
);

// Query with column selection
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>() { /* query conditions */ },
    columnOptions: new ColumnOptions(
        Names: new List<string>
        {
            nameof(MyModel.id),
            nameof(MyModel.PropertyName),
            nameof(MyModel.AnotherProperty)
        }
    )
);

// Using TOP to limit results
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>() { /* query conditions */ },
    columnOptions: new ColumnOptions(
        Names: new List<string>
        {
            nameof(MyModel.id),
            nameof(MyModel.PropertyName)
        },
        Top: 10
    )
);

// Using DISTINCT
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>() { /* query conditions */ },
    columnOptions: new ColumnOptions(
        Names: new List<string>
        {
            nameof(MyModel.PropertyName)
        },
        IsDistinct: true
    )
);

Pagination

// First page
(Gravity gravity, IList<MyModel> items) = await galaxy.Paged(
    page: new Q.Page(25), // 25 items per page
    clusters: new List<Cluster>() { /* query conditions */ }
);

// Access continuation token from the gravity object
string continuationToken = gravity.ContinuationToken;

// Next page using continuation token
(Gravity nextGravity, IList<MyModel> nextItems) = await galaxy.Paged(
    page: new Q.Page(25, continuationToken),
    clusters: new List<Cluster>() { /* same query conditions */ }
);

Group By Queries

// Group by a property
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>() { /* query conditions */ },
    group: new List<string> { nameof(MyModel.Category) }
);

// Group by with COUNT
(Gravity gravity, IList<MyModel> results) = await galaxy.List(
    clusters: new List<Cluster>() { /* query conditions */ },
    columnOptions: new ColumnOptions(
        Names: new List<string> { nameof(MyModel.Category) },
        Count: true
    )
);

Understanding the Gravity Object

The Gravity object is returned by all operations and contains valuable information:

(Gravity gravity, MyModel model) = await galaxy.Get("document-id", "partition-key-value");

// Request Units consumed by the operation
double requestUnits = gravity.RU;

// Continuation token for pagination (only populated in Paged queries)
string continuationToken = gravity.ContinuationToken;

// Query information (only available when debug mode is enabled)
if (gravity.Query.HasValue)
{
    string queryText = gravity.Query.Value.Text;
    IEnumerable<(string, object)> parameters = gravity.Query.Value.Parameters;
    
    Console.WriteLine($"Query: {queryText}");
    foreach ((string name, object value) in parameters)
    {
        Console.WriteLine($"Parameter: {name} = {value}");
    }
}

Error Handling

try
{
    (Gravity gravity, MyModel model) = await galaxy.Get("non-existent-id", "partition-key");
}
catch (UniverseException ex)
{
    // Universe-specific exceptions
    Console.WriteLine($"Universe error: {ex.Message}");
}
catch (CosmosException ex)
{
    // Cosmos DB specific exceptions
    Console.WriteLine($"Cosmos error: {ex.Message}, Status: {ex.StatusCode}");
}
catch (Exception ex)
{
    // Other errors
    Console.WriteLine($"Error: {ex.Message}");
}

Performance Considerations

  • Bulk Operations: Enable AllowBulkExecution in the CosmosClientOptions for efficient batch processing.
  • RU Tracking: The Gravity object provides RU consumption information for cost optimization.
  • Column Selection: Select only the columns you need to reduce data transfer.
  • Debug Mode: The debug mode (enabled by passing true to the Galaxy constructor) provides query details but adds overhead.
  • Partition Key: Always consider your partition strategy for best performance.

Additional Information

For more detailed information about Universe, please check the API Documentation or the GitHub repository.

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
3.0.2 53 5/31/2025
2.1.0 477 3/25/2025
2.0.1 201 9/24/2023
2.0.0 297 3/31/2023
1.4.1 622 1/20/2023
1.4.0 906 10/20/2022
1.3.2 634 10/11/2022
1.2.1 851 8/22/2022
1.2.0 476 8/12/2022
1.1.4 526 8/5/2022