The Query Object

The query object is the main object you use to both define the query and execute it.

It comes in two versions. The main and general purpose version is called “AqlQuery”. The simpler and more specialized version is called “AqlContentsQuery”.

AqlQuery

An instance of AqlQuery is always associated with a WAFSession object. To create an instance you can call:

var q = WAFContext.Session.CreateQuery();

// or

var q = new AqlQuery(WAFContext.Session);

 

The AQL Query is associated with a session so that the action or result can automatically be filtered according to the user’s read access rights and the current culture.

To define a query is much like defining an SQL query with a FROM part where you define the class type or joins of class types you want to select from, and a SELECT part where you define the properties from these classes you want to select:

var q = WAFContext.Session.CreateQuery();
var articleAlias = new AqlAliasArticleBase();
q.From(articleAlias);
var p = q.Select(articleAlias.Name);

List<string> names = new List<string>();

var rs = q.Execute();

while (rs.Read()) names.Add(p.Value);

 

This query retrieves the names of all articles in the system and collets them in a list. The first statement creates a query object associated with the WAF session associated with the current pageview. The second statement creates a class alias to the article base class. The third statement calls the “.From()” method on the query object to define that the query is on the object type “ArticleBase” and all descending classes. The next statement calls the “.Select” method to specify that the search should select the articles name property. It returns an object “p” that act as a reference to the selected property in the result later on. Next a string list object is created and then the “.Execute()” method on the query object is called. The execute statement causes the query to be sent to the WAFEngine, here the query checks the query cache for matches and if none are found the query is sent further to the WAFDataAccessLayer where the query is converted to SQL and executed in the database. On the way back the result is stored in a ResultSet object called “rs” in the code above. (At this point any open database connection is closed by the system and the conventional “using” statement used with ADO.Net that ensures that the connection is closed is not necessary with WAF.) The while loop simply loops through the results and adds one string for each record found.

(Worth mentioning here is that the entire query is type safe and type casting is not necessary.)

This is the basic model for which any query can be performed. Here is the same query with more parameters:

var q = WAFContext.Session.CreateQuery();
var articleAlias = new AqlAliasArticleBase();
q.From(articleAlias);
var pName = q.Select(articleAlias.Name);
var pIngress = q.Select(articleAlias.Ingress);
q.Where(articleAlias.AuthorId == WAFContext.Session.UserId);
q.OrderBy(articleAlias.ReleaseDate);

StringBuilder html = new StringBuilder();

var rs = q.Execute(10);

while (rs.Read())
{
   html.Append("<h1>");
   html.Append(HttpUtility.HtmlEncode(pName.Value));
   html.AppendLine("</h1>");
   html.Append("<p>");
   html.Append(HttpUtility.HtmlEncode(pIngress.Value));
   html.AppendLine("</p>");
}

 

Here the query includes a call to the “.Where()” method. The Where statement accepts a boolean AqlExpression object and in this case the expression is created with the “==” operator and will make the query contain only articles where the current user is the author. The call to the “.OrderBy()” method also accepts an expression and in this example the result will be sorted by the release date. The “.Execute()” statement is passed the parameter “10” so the result only include the first 10 matching rows. The result is used to build an html string.

It is important to point out here that the two previous examples queries returns property data directly and does not instantiate any actual ArticleBase objects. This makes the query faster but a little more tedious to define. The last query could also be performed by selecting the content object instead:

var q = WAFContext.Session.CreateQuery();
var articleAlias = new AqlAliasArticleBase();
q.From(articleAlias);
var pArticle = q.Select<ArticleBase>(articleAlias);
q.Where(articleAlias.AuthorId == WAFContext.Session.UserId);
q.OrderBy(articleAlias.ReleaseDate);

StringBuilder html = new StringBuilder();

var rs = q.Execute();

while (rs.Read())
{
   ArticleBase article = pArticle.Value;
   html.Append("<h1>");
   html.Append(HttpUtility.HtmlEncode(article.Name));
   html.AppendLine("</h1>");
   html.Append("<p>");
   html.Append(HttpUtility.HtmlEncode(article.Name));
   html.AppendLine("</p>");
}

 

The query object is “smart” in that you do not necessarily need to define all the details about a query. The system will make guesses on the details that you do not provide. Here is an example:

var q = WAFContext.Session.CreateQuery();
var pArticle = q.Select<ArticleBase>();
var rs = q.Execute();

 

In this query the call to the “.From()” method is not necessary as the system will understand that it must look up at the ArticleBase table/content type to find data about articles.

There are also static versions of the content alias classes so you do not need to instantiate them all the time. Here is an example:

var q = WAFContext.Session.CreateQuery();
var pArticle = q.Select<ArticleBase>();
q.Where(AqlArticleBase.AuthorId == WAFContext.Session.UserId);
q.OrderBy(AqlArticleBase.ReleaseDate);
var rs = q.Execute();

 

The class “AqlArticleBase” has static properties you can use directly in the AQL expressions. In this example there is no instantiation of the AqlAliasArticleBase class. (The disadvantage with this using the static Aql classes is that it will not work when you join two content class of the same type in one query. Then there is no way to say which property belongs to the left or right part of the join. See the part aboit joins later on for more details.)

The order in which you call the methods on the query object like “.From()” or “.Where()” etc. does not matter.

The “.Execute()” method on the query object has overloads where the result is output directly into a list:

var q = WAFContext.Session.CreateQuery();
var pArticle = q.Select<ArticleBase>();
List<ArticleBase> articles = q.Execute<ArticleBase>();

AqlContentsQuery

The object AqlContentsQuery is a simplified version of AqlQuery. Internally AqlContentsQuery uses the AqlQuery object. The key difference is that AqlContentsQuery only can output ContentBase objects and that it supports a shorter syntax.

Like “AqlQuery” it is always associated with a WAF session and it can be instantiated like this:

var q = WAFContext.Session.Query();

 

The methods on the AqlContentsQuery object return itself so that the calls can be chained:

List<ArticleBase> articles = WAFContext.Session.Query<ArticleBase>().Where(AqlArticleBase.Hidden == true).Execute();

// or with more parameters:

List<ArticleBase> articles = WAFContext.Session.Query<ArticleBase>()
   .Where(AqlArticleBase.Hidden == true)
   .Where(AqlArticleBase.AuthorId == WAFContext.Session.UserId)
   .OrderBy(AqlArticleBase.ReleaseDate)
   .Execute(10);

 

The query object is also useful for doing sub queries on relation properties:

 ArticleBase currentPage = WAFContext.Request.GetContent<ArticleBase>();
var subPages = currentPage.Children.Query<ArticleBase>().OrderBy(AqlArticleBase.Name).Execute();

foreach (var page in subPages) {
   // ...
}