Aql Expressions are the objects that store information about the expressions that make up an AQL query.
The expression classes
All AQL expression inherits from the abstract base class AqlExpressionBase. From this class there are six abstract descending base classes, one for each universal WAF datatype:
- AqlExpressionBoolean
- AqlExpressionInteger
- AqlExpressionFloat
- AqlExpressionShortString
- AqlExpressionLongString
- AqlExpressionDateTime
These abstract classes define operator overloads for various operators. These operator overloads makes it possible to express combined expressions in a simple syntax. Compared to standard SQL, the WAF Expressions represent the parts between the SQL statements keywords. For example, consider the following conventional SQL:
SELECT cost * currency FROM products WHERE category == “cpu”;
In this SQL string the WAF AQL Expression would be used for all the lower case text between the upper case statement keywords. So AQL expressions are used for both properties and new combined or calculated fields.
There are specific rules to what operator match which datatype and what the resulting datetype is. Most of this is intuitive. For example, consider the following expression:
var expression = AqlContent.Name == "Example";
In this expression the “==” operator is a taking one class inheriting from “AqlExpressionShortString” and a normal C# string object. The result of this expresion is an expression inheriting from “AqlExpressionBoolean” as the result is either true or false.
Internally the operator overloading is converting this statement to this equivalent statement:
var expression = new AqlExpressionEqual(AqlContent.Name, new AqlExpressionValueShortString("Example"));
This expression would not compile as the operator overloading does not support the types given:
var expression = AqlContent.Name == DateTime.Now;
A C# DateTime type cannot be compared directly to an AqlExpressionShortString type. The above statement would cause a compile time error and be easy to identify in the code editor.
Below is a list of the operators relevant to WAF Expressions:
- “|”: OR operator (equivalent to “||”)
- “&”: AND operator (equivalent to “&&”)
- “==”: equal operator
- “!=” : not-equal operator
- “<=”: smaller than or equal operator
- “>=”: greater than or equal operator
- “+”: adding number fields
- “-“: subtracting number fields
- “/”: dividing number fields
- “*”: multiplying number fields
- “<”: greater than
- “>”: smaller than
- “(“ and “)”: brackets.
With the built in C# data types the operators “||” and “&&” means OR and AND. With WAF Expressions you must use the operators “|” and “&” as the “||” and “&&” operators cannot be overloaded in C#. Apart from this the WAF operators behave similarly to the normal C# operators.
When building complicated expressions there is a helper class called AqlExpressionBuilder:
var exp = new AqlExpressionBuilder();
exp.Or(AqlContent.Name == "Example1");
exp.Or(AqlContent.Name == "Example2");
exp.Or(AqlContent.Name == "Example3");
There is also a static class called “Aql” that contains quick access to useful methods, for example:
WAFContext.Session.GetContents<ArticleBase>(Aql.Like(AqlArticleBase.Name, "*test*"));
WAFContext.Session.GetContents<ArticleBase>(Aql.In(AqlArticle.NodeId, new int[] { 10, 102, 234 }));
See the section about Aql functions for more information about the methods.
Content Aliases
Content aliases represent a reference to a specific content class. The FROM statement takes this object as its parameter. The class can also represent a join of two or more content aliases.
The content alias class comes in to flavours, a static class, and a non-static class that must be instantiated. The static class is named Aql[ClassName] and the non-static class is AqlAlias[ClassName]. The classes contains properties that return an AqlExpression that represents the properties. Here is one example using the static class of the built in ArticleBase type:
WAFContext.Session.GetContents<ArticleBase>(AqlContent.Name == "Test");
// Here is another one:
var q = WAFContext.Session.CreateQuery();
q.From<ArticleBase>();
q.Select(AqlArticleBase.Name);
var rs = q.Execute();
Below is the same query using the non-static content alias class:
var q = WAFContext.Session.CreateQuery();
var alias = new AqlAliasArticleBase();
q.From(alias);
q.Select(alias.Name);
var rs = q.Execute();
In the last two examples there is no practical need for the non-static classes, but in a join like the one below we need to instantiate two aliases in order to separate the left and right side of the join:
var q = WAFContext.Session.CreateQuery();
var parent = new AqlAliasArticleBase();
var child = new AqlAliasArticleBase();
var join = new AqlAliasRelHierarchical(parent, child);
q.From(join);
q.Select(parent.Name);
q.Select(child.Name);
var rs = q.Execute();
Aql Expressions for enumeration property types
There are built-in functions to query on enumeration properties. There are two types of enumeration properties. “EnumerationProperty” that support one value and “EnumerationsProperty” that support multiple values.
Making queries on the “EnumerationProperty” property is quite straight forward as the system is simply using an integer datavalue to store the data. Here is an example:
WAFContext.Session.GetContents<SystemUser>(AqlSystemUser.Gender == (int)Gender.Male);
The actual enumeration value must be casted to an integer.
Making queries on the “EnumerationsProperty” is less straight forward. Internally the system is using a set of integer datavalues to build up a bit array where each bit represents one enumeration value. To query this field, a set of enumeration values must first be converted to a bit array and then the bit array is split into integers. This system has built-in functions that hides this complexity and makes it easy to query this property. Here is an example:
var contents = WAFContext.Session.GetContents<SystemUser>(
AqlMyClass.MyProperty.In(
NewEnumeration.NewEnumeration1,
NewEnumeration.NewEnumeration2
)
);
This examples uses the “.In()” method of the property expression. This will return true if one of the values given match. The example below uses the “.All()” method. This will return true only if all values match.
var contents = WAFContext.Session.GetContents<SystemUser>(
AqlMyClass.MyProperty.All(
NewEnumeration.NewEnumeration1,
NewEnumeration.NewEnumeration2
)
);
Query Result
The query result class is a object returned by the “.Execute()” method of a query. Internally it basically contains a two dimensional array with the result data. It is comparable to the ADO.Net record set, but it does not contain an open connection to the database while you are looping through the results. In a query the system retrieves all the data from the database and closes the connection before the result set object is returned. The advantage with this is that you do not need worry about closing database connections etc. The disadvantage is that it needs to keep the entire result set in memory, so use the paging functionality if you operate on large record sets (more than 10 000 records).
Here is an example using QueryResult object:
var q = WAFContext.Session.CreateQuery();
var parent = new AqlAliasArticleBase();
var child = new AqlAliasArticleBase();
var join = new AqlAliasRelHierarchical(parent, child);
q.From(join);
var rParent = q.Select(parent.Name);
var rChild = q.Select(child.Name);
q.RetrieveTotalCount = true;
var rs = q.Execute();
StringBuilder html = new StringBuilder();
html.Append("Total count:" + rs.RowCount + "<br />");
while (rs.Read())
{
html.Append(rParent.Value);
html.Append(" - ");
html.Append(rChild.Value);
html.Append("<br />");
}