see on GitHub

The Search Pattern


A graph query's search pattern follows the match keyword with data node and edge clauses, aliases that clarify query logic and enable filtering and projection, dedicated graph syntax, and integrated RQL commands.

Sample queries included in this article use only data that is available in the Northwind sample database, so you may easily try them out.


Data Nodes

We use the term data nodes to distinguish these graph elements from RavenDB's distributed servers (cluster nodes).

In a search pattern, a data-node clause is surrounded by parentheses:
(Orders)

The simplest pattern would include just a data-node clause, retrieving a collection's contents unconditionally.
Here's a query that retrieves all documents of the Orders collection, just like the non-graph query from orders would:
match (Orders)

Resulting in this pile of orders:

Simple Query: Graphical View

Simple Query: Graphical View

  • A data node is a document.
    Graph queries can locate nodes in a documents collection, in the results of an index query, or in a selected subset of either.

  • A data node clause can include RQL statements.
    Here is the same query, match (Orders), with an added RQL filter that leaves us with just one document:

    match 
         (Orders 
           where id() = 'Orders/1-A')

    And its results:

    Simple Query: Graphical View

    Simple Query: Graphical View

    Simple Query: Textual View

    Simple Query: Textual View

Edges

A RavenDB edge is a simple string field (aka "edge specifier") that holds a document's ID.
In your search pattern, an edge clause is surrounded by square brackets:
[ShipVia]

The following query locates heavy orders by their freight, and uses an edge to find who ships them:

match 
   (Orders where Freight > 800) -  
   [ShipVia] ->  
   (Shippers)
  • (Orders where Freight > 800)
    This is the origin data-node clause.
    In this case, origin data nodes are orders from the Orders collection whose freight exceeds 800.
  • [ShipVia]
    This is the edge clause.
    In this case, the edge specifier is a string field named ShipVia.
    The query will open the ShipVia property of each origin node, pull the ID stored in it, and look for a document with this ID in the destination data-node clause.
  • (Shippers)
    This is the destination data-node clause.
    In this case, destination data nodes are looked for in the Shippers collection.

This query produces the following results.

ShipVia Edge

ShipVia Edge

1. This origin data node is orders/293-A of the Orders collection.
2. This edge is "ShipVia": "shippers/3-A", a string field of the origin node.
3. This destination data node is shippers/3-A of the Shippers collection.


Edges Are Directional.

An edge always has a direction.

In the following query for example, each origin node (an order in the Orders collection), points at a destination node (the ordering company's profile, limited in this query to one specific company).

match 
    (Orders)-
    [Company]->
    (Companies where Name = "LILA-Supermercado")
  • The edge specifier is Company, a string property of each origin node.
    We use the direction identifiers -, -> and <- to tell the query which way the origin is and which way is the destination.
  • The hyphen - that connects (Orders) with the edge clause, identified (Orders) as the origin data-node clause.
  • The arrow -> that connects (Companies) with the edge clause, identifies (Companies) as the destination data-node clause.

These are this query's results:

Edges Are Directional

Edges Are Directional

Queries can be directed both to the right and to the left.

You can use -> or <- to direct your query either to the right or to the left.

Take these two queries for example:
(Orders)-[Company]->(Companies)
-and-
(Companies)<-[Company]-(Orders)

In both cases, Orders is the origin, Company the edge and Companies the destination.
The syntax of both queries is valid, their functionality is equivalent, and their results similar.

Expanded Queries can be Bi-Directional.

An expanded query may include some sections directed to the right, and other sections directed to the left.
This syntax may create concise, effective queries.


Complex Edges

A complex edge is a nested JSON structure whose properties include edge specifiers.
A query can use a node's complex edge, to connect it to multiple other nodes.

  • To address a complex edge in a query, use this syntax:
    [Lines as line select Product]

    • Lines is a nested JSON structure.
    • Product is the actual edge specifier within the complex edge.
  • Here is an implementation of a complex edge in a document:

    Complex Edge in document orders/125-A

    Complex Edge in document orders/125-A

  • This query uses orders' Lines structure as a complex edge, to follow the multiple products handled by each order.

    match 
        (Orders as orders)- 
        [Lines as line select Product]-> 
        (Products as products)

    With these results:

    Retrieved Using Complex Edges

    Retrieved Using Complex Edges

  • Direct Approach to an Edge Specifier
    You can directly approach an edge specifier within a complex edge, using this syntax:

    match 
        (Orders as orders)- 
        [Lines[].Product] -> 
        (Products as products)

Aliases

To give an element an alias, use the as keyword.

Document Query
match 
    (Orders)- 
    [Product as product]-> 
    (Products)
Aliases
  • Use unique aliases.
    Aliases are labels that data elements can be referred by when filtered, projected and compared.
    An alias must therefore be declared only once, and bare a unique name.

  • Use _ to exclude an element from the textual results.
    If you want to prevent RavenDB from including an element in the textual graph results, give it _ for an alias.
    E.g. match (Orders as _)
    _, __, ___ and so on have the same functionality, so you can exclude multiple elements from the results and keep each alias unique.

  • Explicit syntax always includes an alias.
    When you declare a data element using the explicit syntax, providing an alias is always required.
    with {from Orders} as orders


What Are Aliases For?

Aliases have a few important roles in graph queries.

  • Readability
    Most commonly, aliases are used to improve query readability and clarify each element's role in it.

  • Abstraction
    It is sometimes useful to instate the same data element, e.g. the Employees collection, in multiple parts of a query.
    To do this, we need to give the reoccuring element a unique alias in each query part it participates.

    The following query for example finds who Sales Representatives report to.

    match 
          (Employees as employed 
             where Title = "Sales Representative") - 
          [ReportsTo as reportsTo]-> 
          (Employees as incharge)

    The Employees collection is instated twice: as employed in the origin node clause, and as incharge in the destination node clause.
    During execution, the abstracting aliases are used to retrieve and connect profiles of employed sales representatives and the employees incharge of them, and looking for both in the same collection poses no problem.

    Employees

    Employees

    These aliases are operational, not just informative: they relay to the graph interpreter how it should handle this query.
    Reinstating an element without providing a unique alias for each of its occurrences, may produce unreliable results.

  • Projection
    To project details of a query's results, we follow the query with a SELECTclause.
    Within this clause, we refer to the query's data elements by their aliases. No aliases, no projection.


Aliases Scope

When you set an alias, be aware of its scope.

  • In the following explicit declaration of a data-node, pd is an in-clause alias that cannot be used outside its scope.
    with {from Products as pd where pd.PricePerUnit > 18} as PricyProduct

  • PricyProduct on the other hand is set for the whole clause and can be approached and selected for projection.

    with {from Products as pd where pd.PricePerUnit > 18} as PricyProduct
    
    match 
       (Orders as LondonOrders 
          where ShipTo.City = 'London') -
       [Lines[].Product] -> 
       (PricyProduct)
    
    select
       PricyProduct as Pricy