Unlock a world of possibilities! Login now and discover the exclusive benefits awaiting you.
My friend, Øystein Kolsrud - Software Architect at Qlik, is back with part 3 of the Qlik Engine API fundamentals: Generic Objects
Introduction
This post will focus on the concept of generic objects and how they are used to organize information in Qlik Sense.
"Evaluate" revisited
Part 2 of this series focused on the concept of handles and gave an example of how to evaluate expressions in Qlik Sense using the app method "Evaluate" along with selections in a field. That method is quite handy for doing simple calculations, but it is very common that you want to evaluate multiple expressions at the same time. Imagine that we want to build a visualization representing a KPI. We would like the visualization to display the sum of sales, be we would also like to display the average sum of sales per month. We could do this by performing two calls to the engine using the "Evaluate" method like this:
{
"jsonrpc": "2.0",
"id": 6,
"method": "Evaluate",
"handle": 1,
"params": [
"Sum(Sales)"
]
}
{
"jsonrpc": "2.0",
"id": 7,
"method": "Evaluate",
"handle": 1,
"params": [
"Sum(Sales)/Count(Month)"
]
}
For which the engine would reply:
{
"jsonrpc": "2.0",
"id": 6,
"result": {
"qReturn": "513503"
}
}
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"qReturn": "5135.03"
}
}
Conceptually this works fine, but it is not very efficient as it requires a lot of communication with the engine. Ideally we would like these two expression to be evaluated together. And this is exactly the purpose of the concept of "Generic object". Generic objects provide an interface for grouping information and expressions that constitute different parts of the same entity. This can for instance include two expressions that should always be evaluated together like in the case of our KPI.
Grouping evaluations with generic objects
We will now use a generic object to group the two evaluations used for our KPI. In this first example I will utilize what is called a "session object" which is an object that is not persisted as part of the app, but created on the fly and discarded when the app is closed. The endpoint to create such an object is called "CreateSessionObject" and is defined here:
The argument required for this call is a structure of the type "GenericObjectProperties" which contains the definition of the different evaluations we would like to be associated with our object. The only property that must be defined when creating a generic object is the property "qInfo.qType" which is just a simple string. Since this is our own custom object, I'll just choose the value 'myKpi' for the object:
{
"jsonrpc": "2.0",
"id": 9,
"method": "CreateSessionObject",
"handle": 1,
"params": [ {"qInfo": {"qType": "myKpi"}} ]
}
The engine replies with an object interface for the newly created session object:
{
"jsonrpc": "2.0",
"id": 9,
"result": {
"qReturn": {
"qType": "GenericObject",
"qHandle": 3,
"qGenericType": "myKpi",
"qGenericId": "81bac84a-cb6a-48ac-8139-298b6e4913c5"
}
},
"change": [ 3 ]
}
Now that we have our object, we want to add the KPI values to this object, and to do that, we'll dig further into the concept of "properties".
Generic object properties
The properties of an object is where we define what evaluations to associate with that object. The properties can be defined when you create the object, but you can also modify the properties of an existing object. To retrieve the properties of an object you use the method "GetProperties":
The method takes no arguments and the call simply looks like this:
{
"jsonrpc": "2.0",
"id": 10,
"method": "GetProperties",
"handle": 3,
"params": []
}
Notice that we use the handle corresponding to our newly created generic object to define the method context. This handle was returned to us when we called "CreateSessionObject", and must be used for all interactions with this particular object. The engine returns the following response:
{
"jsonrpc": "2.0",
"id": 10,
"result": {
"qProp": {
"qInfo": {
"qId": "81bac84a-cb6a-48ac-8139-298b6e4913c5",
"qType": "myKpi"
},
"qMetaDef": {}
}
}
}
The value if "qId" has been randomly assigned by the engine, and is a unique identifier within the app. Now that we have the property definition, we will add our custom expression to this structure and call "SetProperties" on the object to update it:
{
"jsonrpc": "2.0",
"id": 12,
"method": "SetProperties",
"handle": 3,
"params": [
{
"qInfo": {
"qId": "81bac84a-cb6a-48ac-8139-298b6e4913c5",
"qType": "myKpi"
},
"qMetaDef": {},
"myProp0": { "qStringExpression": "=Sum(Sales)" },
"myProp1": { "qStringExpression": "=Sum(Sales)/Count(Month)" }
}
]
}
The names of the two properties "myProp0" and "myProp1" were picked by me, and could be anything. But the name of the inner expression, "qStringExpression" is NOT random. This is a predefined property name that the engine will recognized and act upon, and the definition of its contents can be found in the engine API reference documentation:
The response to the "SetProperties" call is pretty sparse, but states that the object has now changed:
{
"jsonrpc": "2.0",
"id": 12,
"result": {},
"change": [ 3 ]
}
We are now at the point where we want to tell the engine to compute our expressions, and this is where the concept of "Layout" comes into play.
Generic object layout
The term "layout" in the engine API does not have much to do with the term "layout" as we would think of it in the context of a graphical layout. (I remember I was confused by this when I first learned about the concept.) To the engine, a layout is a structure that is created based on the properties of an object and the state of the app in which that object resides. So the properties is where we define what the engine should compute, and the layout contains the computed values. So to compute the two expressions we added to our generic object, we simply call "GetLayout" on that object:
{
"jsonrpc": "2.0",
"id": 13,
"method": "GetLayout",
"handle": 3,
"params": []
}
The result of which is (and I will from now on exclude some of the information to reduce verbosity):
{
"jsonrpc": "2.0",
"id": 13,
"result": {
"qLayout": {
...
"myProp0": "513503"
"myProp1": "5135.03",
}
}
}
In the layout, the properties "myProp0" and "myProp1" get the evaluated value of the expressions we defined in the corresponding properties structure. When the layout is produced, the engine will traverse the properties and look for certain predefined names of which "qStringExpression" is one. When the engine encounters this value in the properties it will interpret it as an expression that should be evaluated, and when the layout for the object is produced it will add to the layout the computed counterpart for the symbols found in the properties structure.
When reading about the engine API you will sometimes come across the concept of a "properties/layout duality". This duality refers to how a certain value is expressed in the properties and what its counterpart is in the layout. The layout duality of a "qStringExpression" is simply a string, but we will see later that other properties have more complex layout duals.
Summary
Generic objects provide a means of grouping evaluations, but also enable other important features such as change notifications which will be covered in later posts in this series. The concept of generic objects is fundamental to how Qlik Sense organizes data and is how the visualizations in a Qlik Sense app are defined and persisted from an engine perspective.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.