Query language

From WandoraWiki
(Difference between revisions)
Jump to: navigation, search
(Directives)
(Aggregate)
 
(88 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Wandora uses a custom query language to select topics in a topic map. Currently the query language is used only in [[Custom topic panel]].
+
Wandora uses a custom query language to select topics in a topic map. Currently the query language is used in the search tool, in [[Custom topic panel]] and in [[Query topic map]]. Image below illustrates Wandora's query interface in search tool. Search tool is opened with menu option '''Edit > Find...'''.
 +
 
 +
 
 +
[[Image:Find_query_tab.gif|center]]
 +
 
  
 
== Introduction ==
 
== Introduction ==
Line 5: Line 9:
 
Wandora does not use any standard query language. Instead queries are done by invoking a method of a Java class implementing a certain interface. The class may then perform anything whatsoever as long as in the end it returns query results in the format specified by the Java interface. Wandora does however include a number of classes designed in a way that makes it possible to build complex queries by combining these simple predefined query directive classes. This somewhat resembles a traditional query language.
 
Wandora does not use any standard query language. Instead queries are done by invoking a method of a Java class implementing a certain interface. The class may then perform anything whatsoever as long as in the end it returns query results in the format specified by the Java interface. Wandora does however include a number of classes designed in a way that makes it possible to build complex queries by combining these simple predefined query directive classes. This somewhat resembles a traditional query language.
  
The queries are defined using a generic scripting language. Wandora uses Java scripting API so it should be possible to use a number of different languages. Examples in this article use Mozilla Rhino 1.6 language that should be found in most installations. This scripting language uses a syntax that is identical to regular Java syntax in nearly every way. Because of this you should be at least somewhat familiar with Java syntax to understand the examples on this page.
+
The queries are defined using a generic scripting language. Wandora uses Java scripting API so it should be possible to use a number of different languages. Examples in this article should work with Mozilla Rhino 1.6 scripting engine using ECMAScript. This scripting engine should be present in most installations. ECMAScript syntax is very similar to regular Java syntax.  
  
As an example, the default configuration contains the following query:
+
Following example demonstrates a query that selects the number of instances in a topic.
  
  1 importPackage(org.wandora.query);
+
1 importPackage(org.wandora.query2);
  2 importPackage(org.wandora.topicmap);
+
2 new Count(
  3 new RecursiveDirective(
+
3  new Instances()
   4    new RolesDirective(
+
  4 );
  5        new IsContextTopicDirective(
+
  6            new SelectDirective(XTMPSI.SUPERCLASS_SUBCLASS,
+
  7                                XTMPSI.SUPERCLASS,XTMPSI.SUBCLASS),
+
  8            XTMPSI.SUBCLASS),
+
  9        XTMPSI.SUPERCLASS),
+
  10    XTMPSI.SUPERCLASS
+
11 );
+
  
The first two lines import needed packages. This is one of the few exceptions where syntax is different to normal Java syntax. The query package contains the predefined directives, the topicmap packages contains the XTMPSI class which contains some subject identifiers as static variables. The rest is the actual query definition technically all in one line but broken on several lines for readability. The query is defined by constructing several directives inside one another.  
+
First line imports the query package. This is one of few common cases where ECMAScript syntax is different than normal Java syntax. Lines 2 to 4 contain the actual query. The ''Count'' directive counts the number of rows in the result of the directive inside it. The ''Instances'' directive inside ''Count'' on line 3 selects all instances of the input.
  
The query is executed selecting different sets of topics in the topic map using a specified context topic as the basis of the selection. Starting with the innermost directive on lines 6 and 7 in the above example, this directive selects topics that are found with association type SUPERCLASS_SUBCLASS and are players with either role SUPERCLASS or SUBCLASS. These should usually be the only players of the association, and the context topic will be one of these. This directive alone is a valid query and the result is a table that is identical to the superclass-subclass table in traditional topic panel, i.e. it contains all associations of type superclass-subclass that are related to the context topic and for all association both players are included.
+
All directives may return any number of rows and get as input a single row. Each row may contain any number of values. Values are indexed with column role names which can be any text strings but are generally formed like URIs. Thus each row resembles a topic map association, only without an association type. One of the columns in a row is marked as active. This is usually the column that was last added or modified by a directive and its value is the primary input for other directives. Most directives use the default column name "#DEFAULT".  
  
The next directive on lines 5 to 8 is a filtering directive that removes certain rows from the results the directive inside it, in this case the select directive. This directive only includes rows where a certain topic is the same topic as the context topic. What topic is compared to context topic is defined as the second parameter of the constructor on line 8, in this case the topic with role SUBCLASS. Again, this directive is a valid query on its own (with the select defined inside it of course). It will select only those superclass-subclass associations where context topic is the subclass.
+
It was said above that each directive receives as input only a single row. However in the above example the ''Count'' directive counts the number of rows of the ''Instances'' directive which of course may be any number of rows. The ''Instances'' directive or its result are not actually considered input to ''Count'' directive. ''Count'' is executed first so it must have received input before we even get to ''Instances''. The input to top-most directive is usually the currently open topic in Wandora. To be more specific, it contains a single column with the default name and the value is the currently open topic in Wandora and this single column is the active column of the row.
  
Next directive on lines 4 to 9 filters columns, or roles, in the query defined inside it. It only includes columns with label SUPERCLASS. We previously only included rows where subclass is the context topic itself so that column is a bit redundant to show. Again this is a valid query on its own.
+
The ''Count'' directive passes its input to the inner ''Instances'' directive as is. The ''Instances'' directive uses the active column of its input, in this case the currently open topic in Wandora, and gets all instances of that. Generally directives add their results to the input row as new columns with the default name and set the new column as active. In this case the only column in input uses the default name and gets overwritten. Thus the result of the ''Instances'' directive is some number of rows, each containing a single column with the default name and the value is some topic which is an instance of the input.
  
The outermost query is the most complex in this example. It recursively applies the query inside it to different contexts and collects all the results. It starting with the context of the query itself, that is the topic where this query is executed. After that it applies the same query but uses results of previous execution as the context, each individually. The second parameter specifies which topic of the query result is used as context for further queries. The directive collects the results of each query execution in a single collection, removing duplicate rows however. The directive is smart enough not to get caught in infinite loops. When there are no new rows found, the query terminates and returns all rows it collected. The inner query finds all super classes of the context so this query then recursively finds all super classes, all super classes of super classes and so on.
+
The result of the ''Instances'' directive goes back to the ''Count'' directive which counts the rows in it. It adds this number in the input row, not the results of ''Instances'' directive. Again the single column gets overwritten because it had the default name. The final result is a single row which contains a single column with the default name and a number indicating the number of instances in the currently open topic.
  
The example above is a typical example of how to build a query. The innermost query is often a SelectDirective or InstancesDirective. SelectDirective selects topics using associations of a specified types and including topics of specified roles. Only associations related to the context topic are considered. InstancesDirective selects all instances of the context topic. Results of the inner query can then be filtered with other queries. Another typical example is joining several queries together by using results of one select as context for another.
+
{| border="1"
 +
! #DEFAULT
 +
|-
 +
| align="center" | 9
 +
|}
  
  1 importPackage(org.wandora.query);
+
Now lets modify the query slightly.
  2 importPackage(org.wandora.topicmap);
+
  3 new UnionDirective(
+
  4    new RolesDirective(
+
  5        new JoinDirective(
+
  6            new RecursiveDirective(
+
  7                new SelectDirective(XTMPSI.SUPERCLASS_SUBCLASS,XTMPSI.SUBCLASS),
+
  8                XTMPSI.SUBCLASS),
+
  9            XTMPSI.SUBCLASS,
+
10            new InstancesDirective()),
+
11        XTMPSI.INSTANCE)
+
12 );
+
  
Here lines 7 to 8 specify a similar recursive query as in previous example. In this case subclasses are selected recursively instead of super classes. Lines 5 to 10 contain a JoinDirective. This directive executes one directive using results of another as the context. The first constructor parameter is the source directive, next parameter defines what topic of the source results are used as context for next query and third parameter is the next query. In this case all instances of all subclasses (subclasses selected recursively) are returned. The other queries are only used to tidy the results. RolesDirective removes all but the instance column and UnionDirective when used with only one parameter removes duplicate rows. UnionDirective could also be used to combine results of several queries.
+
1 importPackage(org.wandora.query2);
 +
2 new Count(
 +
3  new Instances()
 +
4 ).from(
 +
5  new Instances()
 +
6 )
  
== Directives ==
+
The ''from'' method on line 4 causes the input for the ''Count'' directive to come from some other directive. In this case we use again an ''Instances'' directive. The execution of this query goes as follows. The initial input, the currently open topic, first goes to the directive inside the ''from'' part, that is ''Instances'' directive on line 5. This returns the instances of the currently open topic. Then each of these result rows are fed one at a time to the ''Count'' directive and all the results of ''Count'' are combined. The ''Count'' directive itself works exactly as in previous example, it only gets a different input this time. Now it counts the instances of all instances of the currently open topic. We still only get one column in the final result because at each step the default column gets overwritten. The final result might look something like this.
  
Following table contains a list of all directives currently implemented in Wandora. Parameters in square brackets, [ and ], are optional. Parameters with three dots after them, ..., can be repeated as many times as necessary.
+
{| border="1"
 +
! #DEFAULT
 +
|-
 +
| align="center" | 7
 +
|-
 +
| align="center" | 2
 +
|-
 +
| align="center" | 5
 +
|-
 +
| align="center" | 0
 +
|-
 +
| align="center" | 4
 +
|-
 +
| align="center" | 1
 +
|-
 +
| align="center" | 5
 +
|-
 +
| align="center" | 1
 +
|-
 +
| align="center" | 9
 +
|}
  
All parameters that represent topics are given as Strings containing the topic subject identifier. Using the Locator class is also possible. Column labels, also called roles, can be any String in locator format. It does not need to be a subject identifier of any topic in the topic map, although it usually is.
+
In the above table each number tells the number of instances of some topic which is an instance of the currently open topic. But this isn't very useful since we can't know which number corresponds to which topic. So let's modify the query a bit again.
  
You can add tilde character, ~, in front of any locator. This will cause Wandora to automatically remove that column before showing the result table. If you use the column in multiple queries, the tilde character must be used in all of them. Also remember that the column will be present during the entire query execution which may affect behavior of some directives. Specifically, union directive doesn't consider two rows to be equal even if the only difference is in columns marked with tilde. This may cause the final query result to contain duplicate rows. To avoid this, remove unwanted columns with RolesDirective before using UnionDirective to remove duplicate rows.
+
1 importPackage(org.wandora.query2);
 +
2 new Count(
 +
3  new Instances()
 +
4 ).as("#count").from(
 +
5  new Instances().as("#instance")
 +
6 )
  
Filter directives are slightly different to other directives. They all extend a common abstract Java class to make it easier to create custom filters and also to make it possible to combine several filters with boolean operators. The AndDirective and OrDirective take other filter directives as their parameter. The directives given as parameters  must be filter directives and they are executed a bit differently. The source directive in them is not executed at all, instead they are only used to filter rows selected inside AndDirective or OrDirective. When you use FilterDirectives like this, you can pass them null as source directive. If you use filter directives alone, or not directly inside AndDirective or OrDirective, you still need to pass a source directive. The filtering will be then done on results returned by this source directive. See examples below.
+
The ''as'' methods on line 4 and 5 reset the column name to something other than the default. On line 5 we change the column containing the instance topics to "#instance" and on line 4 the column for the count number to "#count". Now our final result has these two columns and we can actually see which topic each of the instance counts belongs to.
  
{| cellspacing="0" cellpadding="5" border="1"  
+
{| border="1"
  ! colspan=3 | Select directives
+
  ! #instance
 +
! #count
 
  |-
 
  |-
  ! Directive
+
  | align="center" | Role
  ! Parameters
+
  | align="center" | 7
! Description
+
 
  |-
 
  |-
  | valign="top" | SelectDirective
+
  | align="center" | Wandora variant name version
  | valign="top" | association&nbsp;type,<br>roles ...
+
  | align="center" | 2
  | valign="top" | Selects topics using associations. Only associations related to the context topic are considered, that is associations that have the context topic as (at least) one of its players. Only associations of the specified type are used and only topics in the specified roles are selected. The result will have one column for each role specified, the label of each column is the locator used to specify the column.
+
|-
 +
| align="center" | Association type
 +
  | align="center" | 5
 +
|-
 +
| align="center" | Wandora class
 +
| align="center" | 0
 +
|-
 +
| align="center" | Wandora language
 +
| align="center" | 4
 +
|-
 +
| align="center" | Role class
 +
| align="center" | 1
 +
|-
 +
| align="center" | Content type
 +
| align="center" | 5
 +
|-
 +
| align="center" | Occurrence type
 +
| align="center" | 1
 +
|-
 +
| align="center" | Schema type
 +
| align="center" | 9
 +
|}
  
Following example will select or topic related to context topic with superclass_subclass association type and playing the role subclass. Note that this will include the context topic itself if it is the subclass of something.
+
As was mentioned earlier, directives usually use the active column value of the input row. This active column is the last column that was added or modified. In most cases this is the right choice for input but not always. Let's say we want to get the base name of the instance topics too and add a ''BaseName'' directive as is done on line 5 in following example.
  
  new SelectDirective(XTMPSI.SUPERCLASS_SUBCLASS,XTMPSI.SUBCLASS);
+
  1 importPackage(org.wandora.query2);
 +
2 new Count(
 +
3  new Instances()
 +
4 ).as("#count").from(
 +
5  new BaseName().as("#basename").from(
 +
6    new Instances().as("#instance")
 +
7  )
 +
8 )
  
|-
+
The base name column is the last column added and thus the active column. The instances directive tries to use this as input. This will fail because the base names aren't actually topics. To fix this we need to manually change the active column.
| valign="top" | InstancesDirective
+
| valign="top" | [new&nbsp;role]
+
| valign="top" | Selects all instances of the context topic. Result will have only one column. The column will have the provided label or XTMPSI.INSTANCE if none was given.
+
  
Following example will select all instances of the context topic
+
1 importPackage(org.wandora.query2);
 +
2 new Count(
 +
3  new Instances().of("#instance")
 +
4 ).as("#count").from(
 +
5  new BaseName().as("#basename").from(
 +
6    new Instances().as("#instance")
 +
7  )
 +
8 )
  
new InstancesDirective();
+
The ''of'' method on line 3 changes the active column before the input gets to the ''Instances'' directive and this query works as expected.
  
|-
+
Now that we have the base name, we can use it for something. We can for example only include rows where the base name contains the word "type".
| valign="top" | TypesOfDirective
+
| valign="top" | [new&nbsp;role]
+
| valign="top" | Selects all types of the context topic. Result will have only one column. The column will have the provided label or XTMPSI.CLASS if none was given.
+
  
Following example will select all instances of the context topic
+
1  importPackage(org.wandora.query2);
 +
2  new Count(
 +
3    new Instances().of("#instance")
 +
4  ).as("#count").from(
 +
5    new BaseName().as("#basename").from(
 +
6      new Instances().as("#instance")
 +
7    ).where(
 +
8      new Regex(".*type.*")
 +
9    )
 +
10 )
  
new TypesOfDirective();
+
The ''where'' method on line 7 is used to filter rows. It contains a filtering directive, in this case ''Regex'' which filters based on a regular expression. The regular expression ".*type.*" matches everything that contains the word "type" somewhere in the base name.
  
 +
We could call the ''where'' method after everything else at line 10 and get the same result (we would have to add ''of'' method call too so the comparison is done using the right column). However this would cause the ''Count'' directive to be processed for all rows, even those that are eventually going to be filtered out anyway. In this case it may not have a drastic effect but the directive inside ''Count'' on line 3 could be something much more complex and take significant amount of time to process. Then the place where the filtering is done would have a significant effect on the time it takes to execute the query. In general, you should try to reduce the number of rows processed as early as possible.
 +
 +
The final result of the above query is.
 +
 +
{| border="1"
 +
! #instance
 +
! #basename
 +
! #count
 
  |-
 
  |-
  | valign="top" | ContextTopicDirective
+
  | align="center" | Association type
  | valign="top" | result&nbsp;type,<br>result&nbsp;role
+
  | align="center" | Association type
  | valign="top" | Returns the context topic. Result type and the role of the returned context topic are given as parameters.
+
  | align="center" | 5
 
  |-
 
  |-
  | valign="top" | TopicsDirective
+
  | align="center" | Content type
  | valign="top" | result&nbsp;type,<br>result&nbsp;role,<br>topics&nbsp;...
+
  | align="center" | Content type
  | valign="top" | Returns a predefined array of topics. The result type and role for the returned topcis are given as parameters as are the returned topics themselves. The result will contain all the specified topics, and only them, with the specified role.
+
  | align="center" | 5
 
  |-
 
  |-
  ! colspan=3 | Filter directives
+
  | align="center" | Occurrence type
 +
| align="center" | Occurrence type
 +
| align="center" | 1
 
  |-
 
  |-
! Directive
+
  | align="center" | Schema type
! Parameters
+
  | align="center" | Schema type
! Description
+
  | align="center" | 9
|-
+
|}
  | valign="top" | AndDirective
+
  | valign="top" | source&nbsp;directive,<br>not,<br>filters ...
+
  | valign="top" | Filters rows by applying two or more other filters and including rows where all specified filters alone would include the row. If not parameter is true, returns the complement set, in other words all rows where at least one filter does not include the row.
+
  
Following example selects all instances of the context that are also of type "http://www.wandora.net/type1" and "http://www.wandora.net/type2".
+
Some directives take ''TopicOperand''s as parameters. These are essentially things that will resolve into a topic in some way. It can be an actual Topic object, a String containing the subject identifier of a topic or a directive which produces the topic as the default value of the first result row. If the directive operand produces more than one row, all the other rows are ignored. The operand gets as input the same input row as the directive using the operand.  
  
new AndDirective(new InstancesDirective(), false,
+
The most usual case is to use the subject identifier, you'll pass this to the directive simply as a String. The following example illustrates this. A topic is passed directly to ''IsOfType'' as a subject identifier String. So this gets all instances that are of the specified type.
        new IsOfTypeDirective(null,XTMPSI.INSTANCE,
+
                              "http://www.wandora.net/type1"),
+
        new IsOfTypeDirective(null,XTMPSI.INSTANCE,
+
                              "http://www.wandora.net/type2"));
+
  
  |-
+
  1 importPackage(org.wandora.query2);
  | valign="top" | OrDirective
+
  2 new Instances()
  | valign="top" | source&nbsp;directive,<br>not,<br>filters ...
+
  3 .where(
  | valign="top" | Filters rows by applying two or more other filters and including rows where at least one specified filter alone would include the row. If not parameter is true, returns the complement set, in other words all rows where no filter includes the row.
+
  4  new IsOfType("http://www.wandora.org/core/associationtype")
 +
5 )
  
Following example selects all instances of the context that are also of type "http://www.wandora.net/type1" or "http://www.wandora.net/type2".
+
Only when the operand somehow depends on the input row, do you need to use a directive as an operand. And in this case you typically just have an ''Of'' directive which picks one column from the input row, but in theory the directive could be as complex as you want. The following example gets instances of the current topic that are instances of themselves. Here the ''IsOfType'' filter depends on the input row being filtered instead of filtering with a static topic like in the previous example. The ''Of'' directive could in this case be replaced with a simple ''Identity'' to do the same thing because the active (and only) column in the input is the topic itself.
  
  new OrDirective(new InstancesDirective(), false,
+
  1 importPackage(org.wandora.query2);
        new IsOfTypeDirective(null,XTMPSI.INSTANCE,
+
2 new Instances()
                              "http://www.wandora.net/type1"),
+
3 .where(
        new IsOfTypeDirective(null,XTMPSI.INSTANCE,
+
new IsOfType(new Of("#DEFAULT"))
                              "http://www.wandora.net/type2"));
+
5 )
  
|-
 
| valign="top" | IsContextTopicDirective
 
| valign="top" | source&nbsp;directive,<br>context&nbsp;role,<br>[not]
 
| valign="top" | Filters based on (in)equality between context and one of the topics in the row. If not parameter is not specified or is false, returns rows where topic playing the specified context role is equal to context topic. Otherwise returns rows where the specified topic is not equal to context topic.
 
  
Following example selects only those players of superclass-subclass association where context topic plays the subclass role.
+
You can find examples of queries on all of the directive pages (see list below). There are also some more [[Complex example queries]] on a separate page.
  
new IsContextTopicDirective(
+
== Directives ==
      new SelectDirective(XTMPSI.SUPERCLASS_SUBCLASS,
+
                          XTMPSI.SUPERCLASS,XTMPSI.SUBCLASS),
+
      XTMPSI.SUBCLASS);
+
  
|-
+
=== Query Structure ===
| valign="top" | ContextIsOfTypeDirective
+
| valign="top" | source&nbsp;directive,<br>type,<br>[not]
+
| valign="top" | Either includes or rejects all rows depending on the type of the context topic. If not parameter is not specified or is false, returns all rows if context topic is of the specified type and returns empty set if not. If not parameter is true, does the opposite. Note that if the filter returns an empty set, it will not actually execute source query.
+
  
Following example executes the SelectDirective only if context is of type "http://www.wandora.net/type1". This can be useful if you're only interested in certain associations in the context of a certain type of topic. Or you can avoid executing a computationally complex query if you know that it will not return anything because the context is not of the correct type.
+
Note that ''As'', ''From'', ''Join'' and ''Of'' directives can be constructed by calling similarly named methods in directives instead of creating them using the constructors of the classes directly. Using the methods to define the query structure usually results in much more readable queries.
  
new ContextIsOfTypeDirective(
+
* [[As (query directive) |As]]([String original,] String newRole) - Changes the role name of the active column or the specified column.
      new SelectDirective( /* something */ ),
+
      "http://www.wandora.net/type1");
+
  
You can also make a query depend on the type of context topic using the not parameter, like this:
+
* [[From (query directive) |From]](Directive to,Directive from) - Takes the results from one directive and feeds them one row at a time to another combining all results.
  
new UnionDirective(
+
* [[First (query directive) |First]]([int count,] Directive directive) - Returns the first or the first N rows of specified directive.
      new ContextIsOfTypeDirective(
+
          new SelectDirective( /* something */ ),
+
          "http://www.wandora.net/type1"),
+
      new ContextIsOfTypeDirective(
+
          new SelectDirective( /* something different */ ),
+
          "http://www.wandora.net/type1",true)
+
      );
+
  
|-
+
* [[If (query directive) |If]](Directive cond,Directive then[,Directive else]) - Returns results of one of two directives depending on the input.
| valign="top" | IsOfTypeDirective
+
| valign="top" | source&nbsp;directive,<br>type&nbsp;role,<br>type,<br>[not]
+
| valign="top" | Includes rows where topic playing the specified role is of the specified type. If not parameter is defined and is true, includes rows where the specified topic is not of the specified type.
+
  
Following example selects those instances of the context topic that are also of the type "http://www.wandora.net/type1".
+
* [[Join (query directive) |Join]](Directive d1,Directive d2,...) - Joins the results of inner directives by performing a cartesian product on the results of them.
  
new IsOfTypeDirective(new InstancesDirective(),XTMPSI.INSTANCE,
+
* [[Last (query directive) |Last]]([int count,] Directive directive) - Returns the last or last N rows of specified directive.
                      "http://www.wandora.net/type1");
+
  
|-
+
* [[Of (query directive) |Of]](String role) - Changes the active column of input to the specified column.
| valign="top" | IsTopicDirective
+
| valign="top" | source&nbsp;directive,<br>topic&nbsp;role,<br>topic,<br>[not]
+
| valign="top" | Includes rows where topic playing the specified role is the specified topic. If not parameter is defined and is true, includes rows where topic with the specified role is not the specified topic.
+
  
Following example selects associations when topic playing role "http://www.wandora.net/role2" is topic "http://www.wandora.net/topic".
+
* [[Recursive (query directive) |Recursive]](Directive recursion[,int maxDepth[,boolean onlyLeaves]]) - Applies a directive recursively.
  
new IsTopicDirective(
+
* [[Roles (query directive) |Roles]](String s1[,...]) - Returns the input row but with only the specified roles. If a role is not found in input, it is added with null value.
        new SelectDirective( "http://www.wandora.net/associationtype1",  
+
                            "http://www.wandora.net/role1",
+
                            "http://www.wandora.net/role2"),
+
        "http://www.wandora.net/role2",
+
        "http://www.wandora.net/topic");
+
  
|-
+
* [[Union (query directive) |Union]](Directive d1,Directive d2[,...]) - Joins the results of inner directives by concatenating them.
! colspan=3 | Joining directives
+
|-
+
! Directive
+
! Parameters
+
! Description
+
|-
+
| valign="top" | JoinDirective
+
| valign="top" | source&nbsp;directive,<br>[join&nbsp;role,]<br>join&nbsp;directive
+
| valign="top" | Joins two directives by using results of one as the context of another. Source directive is executed first. Each topic in the results with the specified join role will be used as context for the join directive. Results are joined by taking the context topic from first result set and all other topics of that row and adding them to each row of the results of the join directive. If source and join directives return columns with same label, results from join directive are used.
+
  
For example if source directive returns result set:
+
* [[Unique (query directive) |Unique]](Directive directive) - Removes duplicate rows.
  
{| cellspacing="0" cellpadding="5" border="1"
+
=== Aggregate ===
! role1
+
! role2
+
|-
+
| topic11
+
| topic12
+
|-
+
| topic21
+
| topic22
+
|}
+
  
And using role1 as join role, join directive returns following results when using topics topic11 and topic21, respectively, as context:
+
* [[Average (query directive) |Average]](Directive directive) - Returns the average of values of active column.
  
{|border="0"
+
* [[Count (query directive) |Count]](Directive directive) - Counts the number of rows returned by the inner directive using same input the ''Count'' directive got.
|
+
{| cellspacing="0" cellpadding="5" border="1"
+
! role3
+
! role4
+
|-
+
| topic11_13
+
| topic11_14
+
|-
+
| topic11_23
+
| topic11_24
+
|}
+
| &nbsp;&nbsp;
+
|
+
{| cellspacing="0" cellpadding="5" border="1"
+
! role3
+
! role4
+
|-
+
| topic21_13
+
| topic21_14
+
|-
+
| topic21_23
+
| topic21_24
+
|}
+
|}
+
  
Then the final results will look like this:
+
* [[Concat (query directive) |Concat]](Directive directive) - Concatenates the String representation of active column values.
  
{| cellspacing="0" cellpadding="5" border="1"
+
* [[Sum (query directive) |Sum]](Directive directive) - Returns the sum of values of active column.
! role1
+
! role2
+
! role3
+
! role4
+
|-
+
| topic11
+
| topic12
+
| topic11_13
+
| topic11_14
+
|-
+
| topic11
+
| topic12
+
| topic11_23
+
| topic11_24
+
|-
+
| topic21
+
| topic22
+
| topic21_13
+
| topic21_14
+
|-
+
| topic21
+
| topic22
+
| topic21_23
+
| topic21_24
+
|}
+
  
For example the following query will select instances of all subclasses of context topic. The result will have two columns, XTMPSI.SUBCLASS and XTMPSI.INSTANCE. Former is the result of select directive and latter of instances directive.
+
=== Primitive ===
  
new JoinDirective(
+
* [[Empty (query directive) |Empty]]() - Returns an empty result.
        new SelectDirective(XTMPSI.SUPERCLASS_SUBCLASS,
+
                            XTMPSI.SUBCLASS),
+
        XTMPSI.SUBCLASS,
+
        new InstancesDirective());
+
  
The join role can be omitted in which case the context of the JoinDirective itself is used as the context of the other directive. That is, the context for the other directive is not taken from results of the first directive. This can be used to join two unrelated queries.
+
* [[Identity (query directive) |Identity]]() - Returns the input row as is.
  
|-
+
* [[Literals (query directive) |Literals]](String s1,[...]) - Returns the strings provided to constructor.
| valign="top" | RecursiveDirective
+
| valign="top" | recursive&nbsp;directive,<br>recursion&nbsp;role,<br>[max&nbsp;depth,<br>only&nbsp;last,<br>remove&nbsp;duplicates]
+
| valign="top" | Recursively applies a directive collecting all returned rows. Results of each application of the recursive directive are combined as if using the UnionDirective, that is they are combined by simply concatenating them together, removing duplicates. Recursive directive is first executed using the context of this RecursiveDirective. For subsequent applications, context topic is taken from previous query results using the specified recursion role. All used context topics are indexed during execution to avoid using same context twice and thus possibly getting stuck in an infinite loop.
+
  
You may optionally give additional parameters. Max depth can be specified to indicate maximum number of recursion steps. Use -1 for default behavior of possibly infinite recursions. If only last parameter is true, only leaf topics of recursion will be included. That is only topics where next recursion step returns an empty set. You can use the last parameter to avoid removing of duplicate rows. Note that the three last parameters must either all be specified or all be left out.
+
* [[Null (query directive) |Null]]() - Returns a null value.
  
See introduction section for examples.
+
* [[Static (query directive) |Static]](ArrayList<Result> rows) - Returns the provided result rows.
  
|-
+
=== Filtering ===
| valign="top" | UnionDirective
+
| valign="top" | directives ...
+
| valign="top" | Combines results of several directives by concatenating the tables. If directives return results with different column labels, assumes null values for unused columns in other result sets. Will remove duplicate rows from final results. It is possible to only specify one directive and effectively use UnionDirective to remove duplicate rows from results of another directive without performing an actual union operation.
+
  
For example, combining following result sets:
+
Note: All filtering directives can be used with the ''where'' method present in all directives. ''A.where(B)'' resolves to ''new From(B,A)''.
  
{|border="0"
+
* [[And (query directive) |And]](WhereDirective d1,WhereDirective d2,[...]) - Includes rows which satisfy all inner filtering directives.
|
+
{| cellspacing="0" cellpadding="5" border="1"
+
! role1
+
! role2
+
|-
+
| topic11
+
| topic12
+
|-
+
| topic21
+
| topic22
+
|}
+
| &nbsp;&nbsp;
+
|
+
{| cellspacing="0" cellpadding="5" border="1"
+
! role2
+
! role3
+
|-
+
| topic32
+
| topic33
+
|-
+
| topic42
+
| topic43
+
|}
+
|}
+
  
Will result in  
+
* [[Compare (query directive) |Compare]](String operand1,String operator,String operand2[,boolean numeric]) - Compares the values of two roles in the input column.
  
{| cellspacing="0" cellpadding="5" border="1"
+
* [[Exists (query directive) |Exists]](Directive directive) - Includes rows where the inner directive returns a non-empty result using the row itself as input.
! role1
+
! role2
+
! role3
+
|-
+
| topic11
+
| topic12
+
| null
+
|-
+
| topic21
+
| topic22
+
| null
+
|-
+
| null
+
| topic32
+
| topic33
+
|-
+
| null
+
| topic42
+
| topic43
+
|}
+
  
Following example will combine results of two select directives. Note that the results of the combined directives should use (at least partly) same column labels.
+
* [[IsOfType (query directive) |IsOfType]](TopicOperand op) - Includes rows where the active column value is an instance of the specified type.
  
new UnionDirective(
+
* [[Not (query directive) |Not]](WhereDirective directive) - Returns rows which do not satisfy the inner filtering directive.
      new SelectDirective( /* something */ ),
+
      new SelectDirective( /* something different */ )
+
      );
+
  
|-
+
* [[Or (query directive) |Or]](WhereDirective d1,WhereDirective d2[,...]) - Includes rows which satisfy at least one of inner filtering directive.
! colspan=3 | Complex directives
+
|-
+
! Directive
+
! Parameters
+
! Description
+
|-
+
| valign="top" | TransitiveDirective
+
| valign="top" | association&nbsp;type,<br>role&nbsp;1,<br>role&nbsp;2
+
| valign="top" | Returns the transitive closure of a binary association. For example, to get all super classes, their super classes and so on for the context topic and similarly for sub classes, you can use this simple query script:
+
  
new TransitiveDirective(XTMPSI.SUPERCLASS_SUBCLASS,
+
* [[Regex (query directive) |Regex]](String regex[,int mode]) - Includes rows where the active column matches the specified regular expression.
                        XTMPSI.SUPERCLASS,
+
                        XTMPSI.SUBCLASS);
+
  
You can achieve the same results using RecursiveDirective with some other directives. TransitiveDirective makes this common task much easier. Execution of the query will also be more efficient.
+
=== Topic maps directives ===
|-
+
! colspan=3 | Other directives
+
|-
+
! Directive
+
! Parameters
+
! Description
+
|-
+
| valign="top" | CountDirective
+
| valign="top" | source&nbsp;directive,<br>type,<br>role
+
| valign="top" | Directive that counts the rows of the source directive. Type parameter is the return type of the query and role is the column label.
+
|-
+
| valign="top" | RolesDirective
+
| valign="top" | source&nbsp;directive,<br>roles ...
+
| valign="top" | Directive that filters columns, including only the specified columns.
+
  
Following example includes only the superclass column of the inner query. RolesDirective is usually used to remove a column that is not wanted in the final results but is needed in the query itself, in this example to only include rows where context topic plays the role superclass.
+
Result and input rows may contain values of any type. Most other than topic maps related directives only use string values. Most topic maps related queries assume that input values are either topics or subject identifiers of topics. Thus it is usually not necessary to specifically convert string values to topics first. The most common case where you would want to specifically do that is to ensure that the final result of the query contains topics instead of string. This allows you to use all the Wandora topic related tools on the result table. To convert subject identifier string to topics use the [[Topics (query directive)|Topics]] directive.
  
new RolesDirective(
+
* [[AllTopics (query directive) |AllTopics]]() - Returns all topics of the topic map.  
        new IsContextTopicDirective(
+
              new SelectDirective(XTMPSI.SUPERCLASS_SUBCLASS,
+
                                  XTMPSI.SUPERCLASS,XTMPSI.SUBCLASS),
+
              XTMPSI.SUBCLASS),
+
        XTMPSI.SUPERCLASS),
+
  
|-
+
* [[BaseName (query directive) |BaseName]]() - Gets the base name of the active column value of input.
| valign="top" | SortDirective
+
 
| valign="top" | source&nbsp;directive,<br>sort&nbsp;column,<br>[descending]
+
* [[Instances (query directive) |Instances]]() - Gets all instances of the active column value of input.
| valign="top" | Directive that sorts the results using the specified column. If descending is specified and is true sorts in descending order.
+
  
Following example selects all instances of the context topic and sorts.
+
* [[IsOfType (query directive) |IsOfType]](TopicOperand op) - Includes rows where the active column value is an instance of the specified type.
  
new SortDirective(
+
* [[Occurrence (query directive |Occurrence]]([TopicOperand type][,TopicOperand version]) - Gets the occurrence with specified type and version from the active column value of input. You can leave out the version to get all occurrences of a type or both parameters to get all occurrences in the topic.
        new InstancesDirective(),
+
        XTMPSI.INSTANCE);
+
  
|}
+
* [[Players (query directive) |Players]](TopicOperand associationType,TopicOperand r1[,...]) - Gets players of specified roles from associations of specified type.
 +
 
 +
* [[SubjectIdentifiers (query directive) |SubjectIdentifiers]]() - Gets all subject identifiers of the active column value of input.
 +
 
 +
* [[Topics (query directive) |Topics]]() - Gets the topic with subject identifier equal to the string value of the active column value of input.
 +
 
 +
* [[Types (query directive) |Types]]() - Gets all types of the active column value of input.
 +
 
 +
* [[Variant (query directive) |Variant]]([TopicOperand type][,TopicOperand version]) - Gets the variant name with the specified type and version from the active column value of input. You can leave out the version to get all variants of a type or both parameters to get all variants in the topic.
 +
 
 +
* [[Variant2 (query directive) |Variant2]](TopicOperand scope, [...]) - Gets the variant name with the specified scope. You can use this to get variants with any kind of scope while the other directive is specifically for variants which have exactly two topics in the scope, a type and a version.
 +
 
 +
=== Others ===
 +
 
 +
* [[Eval (query directive) |Eval]](String expression) - Evaluates a script.
 +
 
 +
* [[Regex (query directive) |Regex]](String regex[,String replace][,int mode]) - Filters and/or performs search and replace operations using regular expressions.
 +
 
 +
== Notes, known issues, and limitations ==
 +
 
 +
Most of these things you can work around by using the [[Eval (query directive) |Eval]] directive. It takes a custom script which can then do pretty much anything. See examples in the directive page.
 +
 
 +
* Explicit '''new''' operator in front of directives can be omitted.
 +
* You can use the query language with [http://www.jython.org Jython] scripts too. You need version 2.5.1 of Jython or later. Jython scripts don't return the value of the last line, instead you have to assign the query directive in a variable ''query''. The syntax is naturally slightly different, specifically you don't use the ''new'' operator when constructing new classes and there are the usual python restrictions to whitespace use. Also note that the ''Eval'' directive still uses the default scripting engine i.e. ECMAScript, even if invoked through Jython. For example one of the introduction examples in Jython:
 +
 
 +
from org.wandora.query2 import *
 +
query=Count(
 +
  Instances()
 +
).as("#count").from(
 +
  Instances().as("#instance")
 +
)
 +
 
 +
 
 +
== See also ==
 +
 
 +
Wandora also supports [[TMQL]] queries.

Latest revision as of 11:12, 23 September 2014

Wandora uses a custom query language to select topics in a topic map. Currently the query language is used in the search tool, in Custom topic panel and in Query topic map. Image below illustrates Wandora's query interface in search tool. Search tool is opened with menu option Edit > Find....


Find query tab.gif


Contents

[edit] Introduction

Wandora does not use any standard query language. Instead queries are done by invoking a method of a Java class implementing a certain interface. The class may then perform anything whatsoever as long as in the end it returns query results in the format specified by the Java interface. Wandora does however include a number of classes designed in a way that makes it possible to build complex queries by combining these simple predefined query directive classes. This somewhat resembles a traditional query language.

The queries are defined using a generic scripting language. Wandora uses Java scripting API so it should be possible to use a number of different languages. Examples in this article should work with Mozilla Rhino 1.6 scripting engine using ECMAScript. This scripting engine should be present in most installations. ECMAScript syntax is very similar to regular Java syntax.

Following example demonstrates a query that selects the number of instances in a topic.

1 importPackage(org.wandora.query2);
2 new Count(
3   new Instances()
4 );

First line imports the query package. This is one of few common cases where ECMAScript syntax is different than normal Java syntax. Lines 2 to 4 contain the actual query. The Count directive counts the number of rows in the result of the directive inside it. The Instances directive inside Count on line 3 selects all instances of the input.

All directives may return any number of rows and get as input a single row. Each row may contain any number of values. Values are indexed with column role names which can be any text strings but are generally formed like URIs. Thus each row resembles a topic map association, only without an association type. One of the columns in a row is marked as active. This is usually the column that was last added or modified by a directive and its value is the primary input for other directives. Most directives use the default column name "#DEFAULT".

It was said above that each directive receives as input only a single row. However in the above example the Count directive counts the number of rows of the Instances directive which of course may be any number of rows. The Instances directive or its result are not actually considered input to Count directive. Count is executed first so it must have received input before we even get to Instances. The input to top-most directive is usually the currently open topic in Wandora. To be more specific, it contains a single column with the default name and the value is the currently open topic in Wandora and this single column is the active column of the row.

The Count directive passes its input to the inner Instances directive as is. The Instances directive uses the active column of its input, in this case the currently open topic in Wandora, and gets all instances of that. Generally directives add their results to the input row as new columns with the default name and set the new column as active. In this case the only column in input uses the default name and gets overwritten. Thus the result of the Instances directive is some number of rows, each containing a single column with the default name and the value is some topic which is an instance of the input.

The result of the Instances directive goes back to the Count directive which counts the rows in it. It adds this number in the input row, not the results of Instances directive. Again the single column gets overwritten because it had the default name. The final result is a single row which contains a single column with the default name and a number indicating the number of instances in the currently open topic.

#DEFAULT
9

Now lets modify the query slightly.

1 importPackage(org.wandora.query2);
2 new Count(
3   new Instances()
4 ).from(
5   new Instances()
6 )

The from method on line 4 causes the input for the Count directive to come from some other directive. In this case we use again an Instances directive. The execution of this query goes as follows. The initial input, the currently open topic, first goes to the directive inside the from part, that is Instances directive on line 5. This returns the instances of the currently open topic. Then each of these result rows are fed one at a time to the Count directive and all the results of Count are combined. The Count directive itself works exactly as in previous example, it only gets a different input this time. Now it counts the instances of all instances of the currently open topic. We still only get one column in the final result because at each step the default column gets overwritten. The final result might look something like this.

#DEFAULT
7
2
5
0
4
1
5
1
9

In the above table each number tells the number of instances of some topic which is an instance of the currently open topic. But this isn't very useful since we can't know which number corresponds to which topic. So let's modify the query a bit again.

1 importPackage(org.wandora.query2);
2 new Count(
3   new Instances()
4 ).as("#count").from(
5   new Instances().as("#instance")
6 )

The as methods on line 4 and 5 reset the column name to something other than the default. On line 5 we change the column containing the instance topics to "#instance" and on line 4 the column for the count number to "#count". Now our final result has these two columns and we can actually see which topic each of the instance counts belongs to.

#instance #count
Role 7
Wandora variant name version 2
Association type 5
Wandora class 0
Wandora language 4
Role class 1
Content type 5
Occurrence type 1
Schema type 9

As was mentioned earlier, directives usually use the active column value of the input row. This active column is the last column that was added or modified. In most cases this is the right choice for input but not always. Let's say we want to get the base name of the instance topics too and add a BaseName directive as is done on line 5 in following example.

1 importPackage(org.wandora.query2);
2 new Count(
3   new Instances()
4 ).as("#count").from(
5   new BaseName().as("#basename").from(
6     new Instances().as("#instance")
7   )
8 )

The base name column is the last column added and thus the active column. The instances directive tries to use this as input. This will fail because the base names aren't actually topics. To fix this we need to manually change the active column.

1 importPackage(org.wandora.query2);
2 new Count(
3   new Instances().of("#instance")
4 ).as("#count").from(
5   new BaseName().as("#basename").from(
6     new Instances().as("#instance")
7   )
8 )

The of method on line 3 changes the active column before the input gets to the Instances directive and this query works as expected.

Now that we have the base name, we can use it for something. We can for example only include rows where the base name contains the word "type".

1  importPackage(org.wandora.query2);
2  new Count(
3    new Instances().of("#instance")
4  ).as("#count").from(
5    new BaseName().as("#basename").from(
6      new Instances().as("#instance")
7    ).where(
8      new Regex(".*type.*")
9    )
10 )

The where method on line 7 is used to filter rows. It contains a filtering directive, in this case Regex which filters based on a regular expression. The regular expression ".*type.*" matches everything that contains the word "type" somewhere in the base name.

We could call the where method after everything else at line 10 and get the same result (we would have to add of method call too so the comparison is done using the right column). However this would cause the Count directive to be processed for all rows, even those that are eventually going to be filtered out anyway. In this case it may not have a drastic effect but the directive inside Count on line 3 could be something much more complex and take significant amount of time to process. Then the place where the filtering is done would have a significant effect on the time it takes to execute the query. In general, you should try to reduce the number of rows processed as early as possible.

The final result of the above query is.

#instance #basename #count
Association type Association type 5
Content type Content type 5
Occurrence type Occurrence type 1
Schema type Schema type 9

Some directives take TopicOperands as parameters. These are essentially things that will resolve into a topic in some way. It can be an actual Topic object, a String containing the subject identifier of a topic or a directive which produces the topic as the default value of the first result row. If the directive operand produces more than one row, all the other rows are ignored. The operand gets as input the same input row as the directive using the operand.

The most usual case is to use the subject identifier, you'll pass this to the directive simply as a String. The following example illustrates this. A topic is passed directly to IsOfType as a subject identifier String. So this gets all instances that are of the specified type.

1 importPackage(org.wandora.query2);
2 new Instances()
3 .where(
4   new IsOfType("http://www.wandora.org/core/associationtype")
5 )

Only when the operand somehow depends on the input row, do you need to use a directive as an operand. And in this case you typically just have an Of directive which picks one column from the input row, but in theory the directive could be as complex as you want. The following example gets instances of the current topic that are instances of themselves. Here the IsOfType filter depends on the input row being filtered instead of filtering with a static topic like in the previous example. The Of directive could in this case be replaced with a simple Identity to do the same thing because the active (and only) column in the input is the topic itself.

1 importPackage(org.wandora.query2);
2 new Instances()
3 .where(
4   new IsOfType(new Of("#DEFAULT"))
5 )


You can find examples of queries on all of the directive pages (see list below). There are also some more Complex example queries on a separate page.

[edit] Directives

[edit] Query Structure

Note that As, From, Join and Of directives can be constructed by calling similarly named methods in directives instead of creating them using the constructors of the classes directly. Using the methods to define the query structure usually results in much more readable queries.

  • As([String original,] String newRole) - Changes the role name of the active column or the specified column.
  • From(Directive to,Directive from) - Takes the results from one directive and feeds them one row at a time to another combining all results.
  • First([int count,] Directive directive) - Returns the first or the first N rows of specified directive.
  • If(Directive cond,Directive then[,Directive else]) - Returns results of one of two directives depending on the input.
  • Join(Directive d1,Directive d2,...) - Joins the results of inner directives by performing a cartesian product on the results of them.
  • Last([int count,] Directive directive) - Returns the last or last N rows of specified directive.
  • Of(String role) - Changes the active column of input to the specified column.
  • Recursive(Directive recursion[,int maxDepth[,boolean onlyLeaves]]) - Applies a directive recursively.
  • Roles(String s1[,...]) - Returns the input row but with only the specified roles. If a role is not found in input, it is added with null value.
  • Union(Directive d1,Directive d2[,...]) - Joins the results of inner directives by concatenating them.
  • Unique(Directive directive) - Removes duplicate rows.

[edit] Aggregate

  • Average(Directive directive) - Returns the average of values of active column.
  • Count(Directive directive) - Counts the number of rows returned by the inner directive using same input the Count directive got.
  • Concat(Directive directive) - Concatenates the String representation of active column values.
  • Sum(Directive directive) - Returns the sum of values of active column.

[edit] Primitive

  • Empty() - Returns an empty result.
  • Identity() - Returns the input row as is.
  • Literals(String s1,[...]) - Returns the strings provided to constructor.
  • Null() - Returns a null value.
  • Static(ArrayList<Result> rows) - Returns the provided result rows.

[edit] Filtering

Note: All filtering directives can be used with the where method present in all directives. A.where(B) resolves to new From(B,A).

  • And(WhereDirective d1,WhereDirective d2,[...]) - Includes rows which satisfy all inner filtering directives.
  • Compare(String operand1,String operator,String operand2[,boolean numeric]) - Compares the values of two roles in the input column.
  • Exists(Directive directive) - Includes rows where the inner directive returns a non-empty result using the row itself as input.
  • IsOfType(TopicOperand op) - Includes rows where the active column value is an instance of the specified type.
  • Not(WhereDirective directive) - Returns rows which do not satisfy the inner filtering directive.
  • Or(WhereDirective d1,WhereDirective d2[,...]) - Includes rows which satisfy at least one of inner filtering directive.
  • Regex(String regex[,int mode]) - Includes rows where the active column matches the specified regular expression.

[edit] Topic maps directives

Result and input rows may contain values of any type. Most other than topic maps related directives only use string values. Most topic maps related queries assume that input values are either topics or subject identifiers of topics. Thus it is usually not necessary to specifically convert string values to topics first. The most common case where you would want to specifically do that is to ensure that the final result of the query contains topics instead of string. This allows you to use all the Wandora topic related tools on the result table. To convert subject identifier string to topics use the Topics directive.

  • AllTopics() - Returns all topics of the topic map.
  • BaseName() - Gets the base name of the active column value of input.
  • Instances() - Gets all instances of the active column value of input.
  • IsOfType(TopicOperand op) - Includes rows where the active column value is an instance of the specified type.
  • Occurrence([TopicOperand type][,TopicOperand version]) - Gets the occurrence with specified type and version from the active column value of input. You can leave out the version to get all occurrences of a type or both parameters to get all occurrences in the topic.
  • Players(TopicOperand associationType,TopicOperand r1[,...]) - Gets players of specified roles from associations of specified type.
  • SubjectIdentifiers() - Gets all subject identifiers of the active column value of input.
  • Topics() - Gets the topic with subject identifier equal to the string value of the active column value of input.
  • Types() - Gets all types of the active column value of input.
  • Variant([TopicOperand type][,TopicOperand version]) - Gets the variant name with the specified type and version from the active column value of input. You can leave out the version to get all variants of a type or both parameters to get all variants in the topic.
  • Variant2(TopicOperand scope, [...]) - Gets the variant name with the specified scope. You can use this to get variants with any kind of scope while the other directive is specifically for variants which have exactly two topics in the scope, a type and a version.

[edit] Others

  • Eval(String expression) - Evaluates a script.
  • Regex(String regex[,String replace][,int mode]) - Filters and/or performs search and replace operations using regular expressions.

[edit] Notes, known issues, and limitations

Most of these things you can work around by using the Eval directive. It takes a custom script which can then do pretty much anything. See examples in the directive page.

  • Explicit new operator in front of directives can be omitted.
  • You can use the query language with Jython scripts too. You need version 2.5.1 of Jython or later. Jython scripts don't return the value of the last line, instead you have to assign the query directive in a variable query. The syntax is naturally slightly different, specifically you don't use the new operator when constructing new classes and there are the usual python restrictions to whitespace use. Also note that the Eval directive still uses the default scripting engine i.e. ECMAScript, even if invoked through Jython. For example one of the introduction examples in Jython:
from org.wandora.query2 import *
query=Count(
  Instances()
).as("#count").from(
  Instances().as("#instance")
)


[edit] See also

Wandora also supports TMQL queries.

Personal tools