Table of Contents
This card is a work in progress. It's largely building on previous work that now lives at wql+samples. There is also design for new WQL in process at WQL+Design.
WQL Basics
WQL, the Wagn Query Language, is a syntax for finding Wagn cards. If you haven't come across it before, there's a brief introduction on the WQL card. This card is for those who've figured out why and where to use it but want to learn how to write the stuff.
A WQL statement defines a list of cards by their properties, their relationships to other cards, or both.
Building Blocks
WQL is built out of card definitions, properties, and relationships.
Card definitions, of course identify a card or list of cards. Every WQL statement is, as a whole, a card definition, because it defines a list of cards. But it can also nest other card definitions. For example, if I wrote WQL to find all the cards that link to the "Praise" card, a definition of the "praise card" would need be nested in the query.
Cards are defined by their properties (name, type, content) and their relationships (linked to from X, included on Y, connected to Z...).
WQL also serves to help arrange the cards by ordering and limiting results.
Language
Though WQL can be expressed both in JSON (for power users) and Ruby (for developers), the structure is the same in both. For example, card definitions may always be expressed as a "hash", or a set of key / value pairs. In Ruby, we might express a simply WQL statement as follows:
{ :name => 'Help' }
while the same in JSON would be
{ "name" : "Help" }
The meaning is the same. Unless you're a techie and care about such things, you don't need to know that the JSON gets converted to Ruby on the server side, and that this translation provides a layer of security. But it may comfort you all the same.
Luckily, the above example is about as different as the two representations get. Both surround hashes with curly brackets, but Ruby uses symbols which start with colons, like :name, which JSON doesn't support. And the keys and values are separated by => in Ruby and : in JSON.
In all other cases, whether you're writing in Ruby or JSON your WQL will use the same basic representation for the same basic concepts:
- Strings: text within quotes:
eg. "boisenberry"
- Numbers -- so obvious I won't provide an example, and
- Arrays: Lists between square brackets:
eg. [ "A", 2, "C", 4 ]
Note: All examples below are given in JSON WQL.
Properties
Each card definition can describe the following card properties:
- name
- type
- content
- id (each card has a unique id that remains the same even when the name is changed)
Future properties that WQL will address include date created, date last updated, editors, permissions, etc.
Exact Matches
Each of these properties can be used as the key in a card definition. For example, here is a search for the card named Lewis Hoffman.
{"name": "Lewis Hoffman"}
This card happens to have the id 3, so it call also be found this way:
{"id": 3}
Both the name and id uniquely identify cards in Wagn, so they each return one card. This type query will return a list.
{"type": "Fruit"}
Exact matches on content can return lists, too.
{"content": "Kiwi"}
To narrow searches, you can combine properties:
{"type":"Fruit", "content": "Kiwi"}
Operators
All the above examples represent exact matches. The name is, precisely, "Lewis Hoffman". Or the content is exactly "Kiwi". WQL allows several more flexible ways to constrain properties, via operators.
WQL supports the following operators:
- match, ~
- gt, >
- lt, <
- eq, =
- ne, !=
- in
In the WQL examples above, all the values in our key/value pairs are just strings, which in WQL means the operator is implicitly "=". To use operators, the syntax is as follows:
PROPERTY:[OPERATOR, VALUE]
So, the above example of cards with content equalling Kiwi:
{"content": "Kiwi"}
is actually the short form of the following:
{"content": ["eq", "Kiwi"]}
or
{"content": ["=", "Kiwi"]}
The other operators are more interesting. This one, for example, gets all the cards which mention Hooze:
{"content": ["match", "Hooze"] }
There is shorthand for this, too. By far the most common use of operators is to search on card content. So, if you use the operator as the key, it will assume you mean content, as with the following syntax:
OPERATOR:VALUE
and the following example (equivalent to the one above)
{"match": "Hooze" }
The following major bugs persist at time of writing:
- the match operator is not working properly on the name property
- numeric operators are treating card values as strings
Relationships
Here's where it gets interesting. Cards in Wagn are related to other cards in two main ways: references (links and includes), and connections (plus cards). Since these relationships are the source of most structure in Wagn, this is the the element of WQL that adds the most depth.
All of these relationships are specified in WQL by nesting card definitions. With the exception of certain "plus" relationships, they all take this form:
RELATIONSHIP: CARD_DEFINITION
References
Cards linking to or including other cards comprise references. (note -- insert links to feature cards inline here)
WQL supports the following reference keys:
- link_to - web links
- linked_to_by
- include - embedded cards
- included_by
- refer_to - web links or embedded cards
- referred_to_by
The simplest reference relationship is the link_to. Here's how you can use WQL to find all the cards linked to the "John Abbe" card:
{"link_to": "John Abbe"}
Notice that in this case our card definition is just the card name, "John Abbe". This is a short form of the more explicit expression:
{"link_to": {"name":"John Abbe"} }
Written that way, it's easier to see why we say there are nested card definitions: curly brackets inside curly brackets.
Connections
Frankly, if WQL ever gets a little mind-bending, it's in the connections. But that's partly because connections are a pretty powerful new way of doing things. If you haven't gotten the basic ideas behind plus cards, continuing to read here will likely be very confusing. If you get the gist of plus cards, this will all make sense. But don't feel bad if you have to peek again later.
WQL supports two main connection keys:
- part - value defines cards that are part of a given plus card
- plus - value defines plus cards of which a given card is part
For each of those, it also supports two side-specific connection keys:
- left, right
- left_plus, right_plus
Part queries are generally pretty simple. Remember, if it has a part, it is always a plus card!
{"part": "website"}
This will get all the plus cards of which "email" is the rightmost part:
{"right": "email"}
Plus queries can get a little more involved. This is because, while a card can only ever have two part cards, it can have countless plus cards. And you often want to refer to them in pairs - the other part and the plus card. For example, you might want to find all of the cards connected to "status" where the content of X+status is "closed". So "status" is the other part, while "X+status" is the plus card. Here's the syntax for that:
"plus":[OTHER_PART, PLUS_CARD]
...and that example we mentioned:
{"plus":["status",{"content":"closed"}]}
In that one,
"status" defines the part, and {"content":"closed"} defines the plus card.If you give the "plus" key just one card definition (like all the other relationship keys), then it will treat it as the other part.
For example, this finds all the cards connected to status (regardless of the value of the plus cards):
{"plus":"status"}
Finding cards that don't fit certain criteria
You can use "not" as an operator to exclude cards specified by the card definition. Right now this only works with part, left, right, plus, left_plus and right_plus. For example, here are all the User cards with no nerdy nickname card attached yet:
{"type": "User", "not": {"plus": "nerdy nickname"}}
Note: Avoid using not on sites that have many (millions+) cards - it would be very slow.
Arranging Results
limit: number or numbers (goes directly into sql)
sort: update, create, alpha, plusses, or relevance
dir: asc OR desc
Relative Searches
Very useful in Auto cards
_self, _left, _right
Also eventually need to fit in:
:or, :return, _none
WQL syntax
search for exact name