Appendix: Embedding Queried Data in a Dynamic Document

This section assumes you know what a KnowledgeKube Data Source is, even if you don't know how to create one. You should also know how data source aliases work. If you are unsure about either of these subjects, you should familiarise yourself with them.

KnowledgeKube lets you generate dynamic information inside documents using special constructs known as Fields. The most common type of field is a Keyword Field, which simply displays the value associated with a specific KnowledgeKube keyword when the document is generated.

You can learn more about placing fields inside dynamic documents by referring to Appendix: Writing Dynamic Documents for KnowledgeKube.

Ordinarily, a keyword represents a single value at the time a document is generated. The same is true for a keyword that refers to a data source Alias. If an alias is referenced by a keyword field, the document will display the current value of that alias, if it has one.

You can also use alias fields to display a set of values retrieved from a data source. By combining one or more of these fields with another type of field that calls a function named ForEachDataSourceRow, you can display all query results that match specific filter criteria.

The ForEachDataSourceRow document function should not be confused with the similarly-named expression parser function. Although they behave similarly, the two functions are written differently and each serves a distinct purpose.

The function works by embedding the contents of one document inside another. The embedded document fetches the results of a single data source query, so it must contain at least one reference to an alias in the chosen data source. The contents and structure of the resulting data are affected by two primary factors:

  • How the embedded document is formatted.
  • How the ForEachDataSourceRow expression is written in the host document.

When the document containing the ForEachDataSourceRow field (referred to as the Parent Document for the remainder of this topic) is rendered, KnowledgeKube queries the specified data source and arranges the filtered results according to how the embedded document (henceforth referred to as the Child Document) is written. By default, this means the full contents of the child document will be repeated once for each row returned by the query.

Take a look at the child document shown below:

A basic child document to be used by ForEachDataSourceRow.

This basic document, named DocChild, extracts a single field from a data source attached to a social media platform. This field references the Message alias, so the child document will extract values from the data source's Message column. When the parent document calls DocChild using ForEachDataSourceRow, it will display a version of the child document's contents for each row returned by the query. An example of such a parent document is shown below:

A basic parent document that calls the ForEachDataSourceRow function.

As you can see, this document contains some static content in addition to the ForEachDataSourceRow field. This means that when the document is generated, it displays the static content as-is, then invokes the function in the subsequent field using the arguments provided.

Although you can put other content in the same paragraph as a ForEachDataSourceRow field, that extra content will be excluded from the output document when the field is rendered.

Before you examine the syntax of the function, take a look at the result of rendering the parent document:

The result of embedding the simple child document inside the parent document.

As you can see, the contents of DocChild have been rendered five times, each one representing the Message value of a single row of the data source. The static text from the child document remains constant, but in each case the Message field has been replaced by a corresponding value from the data source.

Let's take a look at the syntax of the function in the parent document:

~~ForEachDataSourceRow(DocFB, GetAll, DocChild)~~

These three parameters are mandatory. If any of them are not given arguments, the field will not render any data.

The first argument corresponds with the DataSourceName parameter, which specifies the name of the data source used to populate your document. In this example, it refers to a data source named DocFB.

The second argument corresponds with the FilterName parameter, which specifies a Filter associated with the chosen data source. When the document is rendered, only results that satisfy the filter will be included in the resulting PDF. In this example, it refers to a filter named GetAll, which is designed to return all rows in the DocFB data source.

As with any operation that uses a Data Source Filter, you can apply ordering to the filter if you want to display the query results in a particular order. If you do not specify an order, the values will be displayed in the natural order they appear in the data source table.

The third argument corresponds with the DocumentKeyword parameter, which specifies the name of the child document used to collate the results of your data query before embedding them in the finished PDF. In this example, it refers to a child document named DocChild.

In addition to the three mandatory parameters, the ForEachDataSourceRow function has several Optional Parameters. To help you understand these parameters, you'll be looking at a slightly more complex example that shows another way to represent the results of a data source query.

Excluding any of the function's optional parameters will give them a value of False by default.

A common way to represent values from a data source is as a table inside a document. Tables like this display the values returned by your data source query, and may also have a set of headers to describe the contents of each column. An example of this is shown below:

A table, populated with values from a data source.

So far, you've seen how ForEachDataSourceRow replicates the contents of a child document for each row returned by a data query. In the example above, rather than replicating the child document in its entirety, a single row is added to the table for each row returned by the data query, while the header row only appears once.

The results are not as basic as the previous example, but the implementation is almost as straightforward. First of all, create a child document containing a single-row table, like the one shown in the image below. The child document in this example will use the same social media data source as before, albeit with two keyword fields instead of one. Each field should appear in its own column:

A child document to be used for creating a table with ForEachDataSourceRow.

When rendering a table like this, you should not include anything in the child document besides the table itself. KnowledgeKube will ignore everything else in the child document when embedding it with ForEachDataSourceRow.

Once again, to render the results of a query with this new child document, a parent document such as the one below is required:

A parent document containing ForEachDataSourceRow, with the addition of a fourth argument.

There's one important addition to this new parent document: a fourth argument for the ForEachDataSourceRow function. This argument corresponds with the IsGrid parameter, which tells KnowledgeKube whether the contents of the child document consist of a tabular grid of aliases. When used with a child document, the new parent document produces a result like this:

The result of embedding a child document that consists of a grid.

This alone may suffice, but you might also want to add a header row to the table. This is where the function's fifth parameter, HasHeader, comes in. The current example has so far excluded this parameter, which is why each row in the resulting grid is data-driven and formatted identically. To include a single, unique row containing static headers, you will need to add it to your child document as shown below:

grid-based child document, after adding a header row.

After updating the child document, modify the corresponding ForEachDataSourceRow function as follows:

A parent document containing ForEachDataSourceRow, with the addition of a fifth argument.

The parent document's ForEachDataSourceRow function now has an argument for its HasHeader parameter: True. This tells KnowledgeKube to treat the first row in the child document's table as a header. When a PDF is generated using the parent document, the first row will appear once, at the top of the table, while the data-driven rows will be generated in sequence as before. An example of this is shown below:

The result of embedding a child document that consists of a grid with a header.

The ForEachDataSourceRow function's sixth parameter is named VariableParameters. If this parameter is set to True, the function will treat arguments assigned to DataSourceName, FilterName and DocumentKeyword as variable names instead of literal values. Consider the example below:

~~ForEachDataSourceRow(MyDataSource, MyFilter, MyDocument)~~

Because the default value of VariableParameters is False, excluding it will cause the function above to attempt to use a data source called MyDataSource, a related filter called MyFilter, and a child document called MyDocument. The following is a modified version of the function, where VariableParameters has been assigned a value of True:

~~ForEachDataSourceRow(MyDataSource, MyFilter, MyDocument, , , True)~~

As the values for the fourth and fifth parameters have been left blank, a default value of False is applied to both.

Instead of looking for a data source, filter and child document named after the respective arguments, the function will look for ones named after the values stored in MyDataSource, MyFilter and MyDocument, respectively. If it cannot find a variable with the specified name, the function will treat that argument as a literal value, as normal.

The seventh and final parameter is called Refresh, which determines if the function should refresh the data source before executing. If this parameter is set to True, the data source will be updated with current data when the function is called.

~~ForEachDataSourceRow(DocFB, GetAll, DocChild, , , , True)~~

If omitted or set to False, the function will refresh the data source to use the most recently cached data from the specified data source and filter.

Since the fourth, fifth and sixth parameters have been left blank here, a default value of False is applied to each of them.