Join Now

Click here to join our
growing community:

JOIN LOGIN



Docs

Welcome Tour
Handbook
      Markups
      Variables
      Conditions
      Functions
      Forms
      Commands
      Concepts
Extensions
Pro Modules
Developers



Copyright © 2020
Terms of Use
Privacy Policy
BoltWire

Handbook

Query

Query is a powerful command/function that can be used to generate all kinds of dynamic reports. It can by modified in many ways and accepts numberous parameters. It is more often used as a function.

The search function and query function are similar. Search extracts information from sets of pages in your site. Query extracts information from an info page. Because queries only have to read one page, they are often much faster. However, the search function can be used to generate reports when data is spread across pages without it having to be indexed first.

Here's a simple function to illustrate how it works:

[(query page=info.members fmt='[[{+p}]]')]

This reads the info page and gathers all the fields on that info page, and runs that list through the report engine to generate this output. I've escaped it so you can see the connection with the fmt.

[[bob]]
[[joe]]
[[mary]]
...

Note, if page is not set, the value of the #1 parameter will be assumed to be the name of the info file. If a query page is not defined, no results will be returned.

The query command works exactly as the query function, except it is processed after a form is submitted and can be modified based on user input. Consider the following form:

[form]
Search:   [radio page index.names] Names   [radio page index.emails] Emails
[submit]
[command query page={=page} fmt='[[{+p}]]']
[command passdata query mode=post]
[form]

{?query}

The user selects either the name or email index. Then the query command generates a report of the info fields on that page. Passdata then sends that information back to the page when it reloads on form submission, and the post variable picks it up and displays it.

For the sake of illustration, the same thing could be done with this approach:

[form]
Search:   [radio page index.names] Names   [radio page index.emails] Emails
[submit]
[command passdata page]
[form]

[if set {?page}]<(query page={?page} fmt=''[[{+p}]]')>[if]

Here, the index is selected and only it is passed back to the page. When the page reloads, the conditional sees that the page has been set and the query is generated. In many cases it is easier to use a function than a command.

Queries & Pagenames

If the info page uses page names for fields, the two approaches can be combined to create even more flexibility. This is helpful if you have an index of certain pages--perhaps the number of comments on each of your blog posts. Once the index is created you can scan it very quickly without having to look up each page to read the number of comments. The key is making sure the index and the blog pages are in sync of course.

Assuming you are using page names for fields in your info fields, you can refine your search output using various search paramters like: pattern, or groups. This will limit output to the info fields that match those parameters. Here are some examples:

Group refer to groups of pages. If I set group=test, search will look for info fields i the specified group, like test.abort, test.header and test.search. See the search function for more details about using this parameter.

Pattern allows you to use regular expressions and only return page names that match it. For example, you could do: pat='/^[a-z]+\.posts/' to return page names like forum.posts or blog.posts. Pattern matching is an advanced programming topic I won't get into here, but it offers you virtually unlimited possibilities.

Note: You can combine both group and pattern parameters to further refine your search results.

Queries & Indexing
As noted, queries scan a specific query page and extract the field names of all the info vars on that page. It can then check the values of those fields in various ways to accept/reject items from the list. This is especially useful considering BoltWire has powerful built-in indexing capabilities to generate various kinds of info pages automatically and keep them updated for you. You can also use the info command/function to generate your own info pages and then have the query function scan those pages and extract data out of them in various ways. There are basically three types of queries (though developers can easily extend the indexing system to include additional modes). For more information see the tutorial on indexing. Here are the three types:

Text queries allow you to check if a certain word is in the value of an info field. BoltWire has an indexing mode which allows you to specify a group of pages and strip out all the words on each page for your index. That is, each line would look like "some.page: some words all on page pink elephants..." plus any other words on the page (duplicates omitted). Assuming you have had BoltWire generate such an index, you can set text='pink' to return all pages with the word pink on it. In this case, some.page would be returned as a positive match. You can also use boolean operators like the following: text="(blue && elephants) || ! turtle)". Again some.page would be returned as a match because it doesn't contain the word turtle. A text query cannot search for a phrase. So if you enter a phrase like text="pink elephants" it is treated as text="pink && elephants", rather than a phrase. You could however use the inpage conditional to further filter the results in the template.

Link queries allow you to check if a page name is in the value of an info value. BoltWire has an indexing mode which allows you to specify a group of pages and extract all the links on each page for your index. That is, each line would look like "some.page: test.one main forum.1010" assuming those three links were all on some.page. Once you have had BoltWire generate such an index, you can set link=main, and the query will return all pages containing that link. In this case, some.page would be returned as a positive match. Knowing which pages are linked to which can be very useful.

Data queries allow you to check the values in an info field against many kinds of operators. Let's say your index page had lines like "some.page: apple | blue | Spain" representing fruit, color and country. BoltWire's indexing capabilities allow you to specify a set of pages and extract their data values to create in index like this, and keep it updated automatically. To do a data query on this info page, you could insert the following as a parameter in the search function: #1(=)apple or #3(=)'United Kingdom'. Notice the parentheses around the = operator. The query would return some.page for the first but not the second. If you insert both parameters in a search function only those pages that pass all the data queries are returned.

Note: If I set page=names, BoltWire will look for index.names first, then info.names, and finally names. If nothing can be found, the search aborts. It's just a shortcut--but you can query any page in your site.

Operators
Data queries are especially powerful because you can use other operators besides just equality. The following are all available to you and you can mix and match different types of data queries in the same search:

#1(=)apple -- The value in part 1 is equal to apple
#1(!)apple -- The value in part 1 is not equal to apple
#1(<)5 -- The value in part 1 is less than 5
#1(>)5 -- The value in part 1 is greater than 5
#1(&)10|20 -- The value in part 1 is between 10 & 20
#1(,)bob -- The csv list found in part contains the item bob
#1(+)go -- The value in part 1 contains the string go (can be part of word, like got)
#1(-)go -- The string go is not found in the value of part 1
#1(?) -- Part 1 is assigned some value
#1(^) -- Part 1 does not have a value
#1(0)/pat/ -- Part 1 fails to match specified pattern
#1(1)/pat/ -- Part 1 matches the specified pattern

Legends
As a shortcut, BoltWire allows you to define legends in either the search function or in the info page. So in the search function, you could put legend='fruit | color | country'. Or on the info page itself, info.preferences, you could insert the following line

legend: fruit | color | country

Defining a legend (either way) allows you to search an index without remembering the order of the info fields. That is fruit(=)apple is translated automatically into #1(=)apple.

Another option is to define a legend on code.settings perhaps with a line like this:

legend_preferences: fruit | color | country

And then in the search function you could put legend= as one of the search parameters.

It will be worth taking a little time to think about how you want to structure the data on your site so you can generate the fastest and most effective searches on your site.

Combining Pagelists and Queries

Because you can combine queries and pagelist, you can do very specialized search. For example consider the following example:

[(query page=index.posts group=forum text='pink elephants')]

BoltWire would pull up all the field names in index.posts, eliminate any that do not look like forum.1000 or forum.1001, and then return only the ones that contain the word pink and the word elephants'.

Refining Your List

Once you have used one or the other or both of these methods to generate a list of matches, the search function sends your raw list of matches through an additional filtering system allowing you to refine your list even further. All of the following parameters are available to you:

include=some.page,another.page
add these items to those already in the list

exclude=some.page,another.page
remove these items from the list

type=info,index
return only pages whose last page part is info or index

type=-header,footer,side
remove all pages of these page types

type=number
only return pages whose last page part is a number

sort=false
this doesn't work in pagelists, but will in queries

sort=random
shuffle the order of the list

sort=lastmodified
list pages modified most recently first

sort={+p} or {+field} or {+2}, etc
sorts the output based on various vars

order=reverse
reverse the order of the list

when='number {+p2} && more {+p2} 1200'
only returns pages with p2 > 1200 (can use any conditional).

count=10
only return first 10 items

count=10-20
return items 10-20

count=false
return all items (ignore searchlimit in site.config, or default of 50).

Note: The sort parameter can recognize a wide variety of values including most data, page, and template variables. If a query, it also knows {+field}, {+value}, {+value::2} or its equivalent{+2}. You can also try {+group}, {+lastmodified} and possibly others.

Generating the Report

Once the items in the list are established, and the results refined, the function sends them over to the report engine. This engine is very powerful and gives you a wide very variety of options. See the tutorial for more information. Here's a brief summary:

You can use either a template or a fmt to generate the report. Templates run each item through the entire BoltWire markup engine and allow maximum versatility. It can be inline (defined in the function/command) or you can refer to a template page. The fmt returns raw markup back to the page and the BoltWire markup engine simply completes its cycle for the entire page. This is far faster, but it only allows you to include markup in the fmt that has not already been processed. So in general, templates are safer, fmts are faster.

If neither the template or fmt parameter is defined, list tries to set fmt to the value of parameter 2.

Several templates/fmts are built into BoltWire. You can set either template or fmt to any of these:

count - returns total number of hits
csv - returns a csv list of hits
list - returns a line of items separated by linebreaks
title - returns a list of titles linked to the pages
toc - returns a list of links indented by {p0}
default - returns a list of breadcrumbs

Note: the default for the query command/function is the last option--breadcrumbs. For the list command/function, it uses list.


Join

Normally each item in the output is joined together by a simple line break. You can change what joins them by resetting the join parameter. For example, [(search pages=a,b,c join=' | ')] will generate: a | b | c. To eliminate any delimiter set join=false.

Tables & Columns

You can create a table easily by using the following format. It supplies the opening and closing table markups, and each item generates a row. If you want to customize the table, you can set its attributes in the table parameter, like table='border=1' instead of table=true.

[(search bob,joe,dave fmt='
{+p} {~{+p}:phone}' table=true)]

If you have a short output (like title or {+page}), you can set cols=3 (or some other number) and a table will be generated with an equal portion of elements in each column. If you want special formatting for the table, you can add a table='some parameters' to the function as well. You do NOT need to put table=true for columns.

Formats

Suppose you set fmt=mydisplay. BoltWire first looks for an anchored section on the current page (or the action page if you are calling an action). Then it tries to find template.mydisplay. Finally it looks for a page called mydisplay. If none of these can be found, it will use the letters "mydisplay" as your fmt. You can of course put your fmt value directly in the fmt parameter. That's called an inline fmt.

Once BoltWire has a fmt value, it applies this value to each item in the list and links them together using the report delimiter (usually a line break). BoltWire then does whatever processing remains for the page on the output. This requires you to be familiar with the markup table to see which rules follow the function calling the list, and which are before. For long reports, the speed advantages of fmts over templates are significant.

Though not usually desirable, if you want to freeze the list output and stop all further processing use output=escape or escape=true.

The following template replacements are available to your fmt through some special code in the BoltWire report engine:

{+p} = The current item
{+p0} = The number of page parts
{+p1}, {+p2} = Individual page parts
{+page} = Last page part
{+count} = Return 7 if the seventh item
{+prev} = The item immediately preceding
{+next} = The next item
{+group} = The current page minus the last page part
{+first} = The first item in the report
{+last} = The last item in the report
{+matches} = Total number of items

You can also insert any parameter in the command/function directly into the fmt/template. So if you added color=red to the search command/function, you could put {+color} in the fmt and it would be replaced with red.

Templates

Templates are similar to fmts. BoltWire looks for a value in the same locations as fmts, and if nothing is found, it too will use an inline template. Once it has its value it applies it to each item in the report. However, it runs the entire markup table on each item, meaning you can use any markup and create as elaborate a template as you wish. Another difference is that the output is escaped by default. Though not recommended you can turn this off by setting escape=false.

All the template replacements available for fmts (see above, including the parameter insertions) are also available for templates. If you set query=some.page (and that page has items in your list as info fields), the following template replacements become available:

{+field} = the page name
{+value} = the corresponding info value
{+value::1} = the correspondng info value, part 1
{+#1} = the corresponding info value, part 1

If you use a full template, rather than an inline template you can greatly customize your report. For example, you can modify the report for the first item, the last item, for each new group, and even for specific items. You can also control what to display if there are no matches. Here's an easy example of a full template:

[(list bob,mary,sue,dave template=profile)]

/*
[[#profile]]
[(template first)]Welcome to my profile report!
[(template each)]{+p}
Phone: {~:phone}
Email: {~:email}
Address: {~:address}
[(template last)]Printed <(time %x)>
[(template none)]No records found...
[[#end]]
*/
The template is defined by the anchor and continues to the next anchor. It is hidden in comment markups, so the template is not visible on the page.

This particular template creates a nice header, displays each members id, phone, email and address drawn from the member page, and then the date is printed. If no matches are found. If there were more than one group in my list, I could have added a [(template group)] option to insert something between each change of page groups. If I wanted to display something special for sue, I could have added [(template 3)]. Actually, that would apply to mary, because once the output is alphabetized, mary will be number three.