Signum Documentation

The documentation comes from the Markdown files in the source code, so is always up-to-date but available only in English. Enjoy!

Paste your Framework commit SHA

You can help the user search information by configuring a FindOptions in Signum Framework.

Before starting, make sure you understand the user request. You can ask for clarifications if needed.

If the query system is not expressive enough to satisfy the user request, tell the user about the limitations or problems you find.

Identify the root query name

The first step is to identify the root query. A queryName (typically also a typeName) is an string WITHOUT "Entity" suffix or any namespace. For example "User".

Sometimes this could be tricky, for example if the user asks for "Best products last month", the root may not be "Product", but maybe "Order", "OrderLine" or "Invoice".

Here are some tables that can be use as root query grouped by namespace;

<LIST_ROOT_QUERIES>

You can use list_query_names by namespace to get more options if you think the root query is not in the list above.

Note: Not all queries represent types, the ones with a (ImplementedBy) can be used as queryName but not as typeName in autoCompleteLite.

Think which ones could be good candidates, you can ask the user to clarify.

Get QueryDescription (metadata)

Once you have the root query name, you can get the query metadata using the queryDescription tool.

This tool will provide you with all the columns of this query. Typically there is a Entity column that will give you the main entity of the row.

The tool also expands sub-tokens automatically using some heuristics.

Exploring sub-tokens

If you need to explore more, you can use the subTokens tool to get the properties of a related entity or any other sub-token.

But maybe you don't need it, this are the sub-tokens you can expect for each filterType, ignoring Aggregates:

  • Lite: use subTokens so explore the sub-properties.
  • DateTime: have many sub-tokens:
    • Year, Month, Day, Hour, Minute, Second, Millisecond (number)
    • MonthStart, Date, HourStart, MinuteStart, SecondStart (date)
  • String: only have one sub-token: Length (number)
  • Enums, Boolean, Guid: typically have no sub-tokens.
  • Integer / Decimal: Numbers have sub-tokens for grouping by range like Step100. If you need this functionality use subTokens.
  • Collection have many sub-tokens
    • Count: The number of elements in the collection
    • Element: Joins (using outer apply / outer join) with the collection table effectively multiplying the number of results. Beware or cartessian multiplication if you use Element in two independent collections. If more than one filter/order/column repeat the same Element expression, the same join will be re-used. Element2, Element3 are usfull in the rare cases that you want to make independent joins to the collection table.
    • Any, All: Only for filters, allows to add conditions that some or every element should satisfy. Entity.Details.Any.Quantity EqualsTo 2 translates in C# to Entity.Details.Any(d => d.Quantity == 2)
    • SeparatedByComma, SeparatedByNewLine: Only for columns, shows the ToString() of all the elements in the collection in one column. The expression Entity.Details.SeparatedByComma.Product is in C# string.Join(", ", Entity.Details.Select(a => a.Product.ToString())).

Note: All the tips above are not considering the sub-tokens of type Aggregate, like CountDistict, CountNull, CountNotNull, etc..

Preparing FindOptions

In order to create query url you need to build a FindOptions in json format. This is the TypeScript schema:

export interface FindOptions {
  queryName: string;
  groupResults?: boolean;

  includeDefaultFilters?: boolean;
  filterOptions?: FilterOption[];
  orderOptions?: OrderOption[];
  columnOptionsMode?: ColumnOptionsMode;
  columnOptions?: ColumnOption[];
  pagination?: Pagination;
}

Filters

You can specify any number of filters and all should be satisfied (AND).

export type FilterOption = FilterConditionOption | FilterGroupOption;

export interface FilterConditionOption {
  token: string;
  operation?: FilterOperation;
  value?: any;
}

export type FilterOperation =
  "EqualTo" |
  "DistinctTo" |
  "GreaterThan" |
  "GreaterThanOrEqual" |
  "LessThan" |
  "LessThanOrEqual" |
  "Contains" |
  "StartsWith" |
  "EndsWith" |
  "Like" |
  "NotContains" |
  "NotStartsWith" |
  "NotEndsWith" |
  "NotLike" |
  "IsIn" |
  "IsNotIn";

Each filter condition has:

  • A token (fullToken) from queryDescription or subTokens.
  • An operation that should be compatible with the type of the token. If not set EqualtTo is assumed. Tip: Contains is only for strings, for collections use .Any in the token and EqualTo in operation.`
  • A value that should match the type of the token, extept for IsIn or IsNotIn that should be an array of values. If not set null is assumed.
    • When filtering by an entity, you need to get a Lite<T>, you can get one using the tool autoCompleteLite. Check 'Finding simple entities by name' below.
    • When filtering by an enum, you need to get a valid enum value. Fortunately, you can use subTokens on the enum token to get all the different values twice, since each enum value X has a CountX and CountNotX aggregates.
export interface FilterGroupOption {
  groupOperation: FilterGroupOperation;
  token?: string;
  filters: FilterOption[];
}

export type FilterGroupOperation = "And" | "Or";

Filters can be grouped using AND/OR, depending on the groupOperation.

The token is optional, and if present if can be used to combine filters of collections that use Any or All.

For example, if you want to filter orders that have any order line with more than 2 of product "X", you need to use:

{
  "groupOperation": "And",
  "token": "Entity.Details.Any"
  "filters": [
	{
	  "token":"Entity.Details.Any.Quantity",
	  "operation":"GreaterThan",
	  "value":2
	},
	{
		"token":"Entity.Details.Any.Product.Name",
		"operation":"EqualTo",
		"value":"ProductX"
	}
}

Without the group with prefix the two filters would be applied independently, resulting in orders that have any line with quantity > 2 AND any line with product "X", which is not the same.

IMPORTANT: For time-related filters, first check the current date using CurrentDateSkill.

Orders

You can specify any number of orders, they will be applied in the order specified.

export interface OrderOption {
  token: string;
  orderType: OrderType;
}

export type OrderType =
  "Ascending" |
  "Descending";
  • token: the expression to use, can not use Any, All, SeparatedByComma or SeparatedByNewLine.
  • orderType: Ascending or Descending.

Columns

Queries have a set of default columns, so often you don't need to specify any column.

But if you want to customize the columns, you need to specify the columnOptionsMode and the columnOptions.

export type ColumnOptionsMode =
  "Add" |
  "Remove" |
  "ReplaceAll" |
  "InsertStart" |
  "ReplaceOrAdd";

export interface ColumnOption {
  token: string;
  displayName?: string;
  summaryToken?: string;
  hiddenColumn?: boolean;
  combineRows?: CombineRows;
}

export type CombineRows =
  "EqualValue" |
  "EqualEntity";

The columnOptionsMode can be:

  • Add: Add the specified columns at the end of the the default ones.
  • Remove: Remove the specified columns from the default ones.
  • ReplaceAll: Ignore the default columns and use only the specified ones.
  • InsertStart: Add the specified columns at the start of the the default ones.
  • ReplaceOrAdd: For each specified column, if it exists in the default columns replace it, otherwise add it at the end (make sense only if you want to change the display name or summary token of some default columns).

IMPORTANT: Always set the appropiate columnOptionsMode; when grouping use ReplaceAll.

Each column has:

  • token: the expression to use, can not use Any, All.

  • displayName: optional, if not specified the default name will be used.

  • summaryToken: optional, only used to shown and aggregate in the header of the column. Can be used even if the FindOptions does not set groupResults. You can not aggregate twice (avoid Count.Sum, just use Count), but you can sum the number of elements in a collection (Friends.Count.Sum).

  • hiddenColumn: optional, if true the column will not be shown, only usefull for hiding the real grouping key if groupResults is true.

  • combineRows: optional, if specified consecutive rows with the same value in this column will be combined in one row with rowspan in the html table. EqualValue compares similar values, EqualEntity compares the entity ids.

  • Example:

{ 
	queryName: "Order",
	columnOptions: [
		{ token: "Entity.Customer.Name" },
		{ token: "Entity.TotalAmount", summaryToken: "Entity.TotalAmount.Sum" },
	],
}

Grouping results

If you want to group the results using the specified columns, set groupResults to true. Take into account:

  • When grouping, any ColumnOption (or OrderOption) that is not an aggregate token will be used as grouping key.
  • In some rare cases you may want to group by a token that is not shown, for example group by User but show User.UserName and User.Role, then add the column (User) with hiddenColumn set to true.
  • If you dont set groupResults to true, you should not use any aggregate token or the FindOptions will be invalid.
  • In filters, you can use aggregate tokens to filter the results after the grouping (similar to SQL HAVING).
  • You can calculate global aggregates (total Count, total Amount.Min) by setting groupResults to true and having only aggregates.

Example:

{ 
	queryName: "Order",
	groupResults: true,
	filterOptions: [
		{ token: "Entity.TotalPrice.Sum", operation: "GreaterThan", value: 1000 }
	]
	columnOptions: [
		{ token: "Entity.OrderDate.MonthStart" },
		{ token: "Entity.TotalPrice.Sum" }
	]
}

Pagination

By default the query will paginate the results (recommended).

If you want to specify the pagination use:

export interface Pagination {
  mode: PaginationMode;
  elementsPerPage?: number;
  currentPage?: number;
}

export type PaginationMode =
  "All" |
  "Firsts" |
  "Paginate";

There are three modes: * All: all the results will be returned, not recommended for large result sets. * Firsts: only the first elementsPerPage results will be returned. Fastest, since now count query is needed. * Paginate: the results will be paginated using elementsPerPage and currentPage.

Converting a FindOptions to a url

The final results is typically to convert the FindOptions to a url that can be used in a browser.

You can use the tool getFindOptionsUrl. It will validate the FindOptions and return either an error message or the url.

Once you have the url, use a markdown link to show it to the user.

Executing a query and returning the results as a table

If needed, you can execute a query and get the result as a table to compose a result message using the getResultTable tool.