web/wdoc/tut_rql_en.rst
author Sylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 20 May 2010 20:47:55 +0200
changeset 5556 9ab2b4c74baf
parent 1398 5fe84a5f7035
child 8017 1df3b5e9d010
permissions -rw-r--r--
[entity] introduce a new 'adapters' registry This changeset introduces the notion in adapters (as in Zope Component Architecture) in a cubicweb way, eg using a specific registry of appobjects. This allows nicer code structure, by avoid clutering entity classes and moving code usually specific to a place of the ui (or something else) together with the code that use the interface. We don't use actual interface anymore, they are implied by adapters (which may be abstract), whose reg id is an interface name. Appobjects that used to 'implements(IFace)' should now be rewritten by: * coding an IFaceAdapter(EntityAdapter) defining (implementing if desired) the interface, usually with __regid__ = 'IFace' * use "adaptable('IFace')" as selector instead Also, the implements_adapter_compat decorator eases backward compatibility with adapter's methods that may still be found on entities implementing the interface. Notice that unlike ZCA, we don't support automatic adapters chain (yagni?). All interfaces defined in cubicweb have been turned into adapters, also some new ones have been introduced to cleanup Entity / AnyEntity classes namespace. At the end, the pluggable mixins mecanism should disappear in favor of adapters as well.

.. -*- coding: utf-8 -*-

Let's learn RQL by practice...

.. contents::

Introduction
------------

RQL has a syntax and principle inspirated from the SQL query language, though
it's at a higher level in order to be more intuitive and suitable to easily make
advanced queries on a schema based database.

* the main idea in RQL is that we'are following relations between entities
* attributes are a special case of relations
* RQL has been inspirated from SQL but is at a higher level
* a knowledge of the application'schema is necessary to build rql queries

To use RQL, you'll have to know the basis of the language as well as a good
knowledge of the application'schema. You can always view it using the "schema"
link in user's dropdown menu (on the top-right of the screen) or by clicking here_.

.. _here: schema


Some bits of théory
-------------------

Variables et types
~~~~~~~~~~~~~~~~~~
Entities and attributes'value to follow and / or select are represented by the
query by *variables* which must be written upper-case.

Possible types for each variable are deducted from the schema according to
constraints in the query.

You can explicitly constrain a variable's type using the special relation "is".

Base types
~~~~~~~~~~
* `String` (literal: between double or simple quotes)
* `Int`, `Float` (using '.' as separator)
* `Date`, `Datetime`, `Time` (literal: string YYYY/MM/DD[ hh:mm] or
  `TODAY` and `NOW` keywords)
* `Boolean` (keywords `TRUE` et `FALSE`)
* keyword `NULL`

Opérateurs
~~~~~~~~~~
* Logical operators : `AND`, `OR`, `,`
* Mathematical operators: `+`, `-`, `*`, `/`
* Comparisons operators: `=`, `<`, `<=`, `>=`, `>`, `~=`, `LIKE`, `IN`

  * `=` is the default comparison operator

  * `LIKE` / `~=` permits use of the special character `%` in a string to tell
    the string must begin or end with a prefix or suffix (as SQL LIKE operator) ::
    
      Any X WHERE X name ~= 'Th%'
      Any X WHERE X name LIKE '%lt'

  * `IN` permits to give a list of possible values ::

      Any X WHERE X name IN ('chauvat', 'fayolle', 'di mascio', 'thenault')

Grammaire des requêtes de recherche
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::

  [DISTINCT] <entity type> V1(, V2)*
  [GROUPBY V1(, V2)*]  [ORDERBY <orderterms>]
  [WHERE <restriction>] 
  [LIMIT <value>] [OFFSET <value>]

:entity type:
  Type of the selected variable(s). You'll usually use `Any` type to not specify
  any type.
:restriction:
  List of relations to follow, in the form
    `V1 relation V2|<constant value>`
:orderterms:
  Define a selection order : variable or column number, followed by the sort method
  (`ASC`, `DESC`), with ASC as default when not specified

notice about grouped query (e.g using a `GROUPBY` clause): every selected
variable should be either grouped or used in an aggregat function.


Example schema
--------------

In this document, we will suppose the application's schema is the one described
here. Available entity types are :

:Person:
  ::

	name      (String, required) 
	birthday (Date)


:Company:
  ::

	name   (String)


:Note:
  ::

	diem (Date)
	type (String)


And relations between those entities: ::

	Person  works_for    Company
	Person  evaluated_by Note
	Company evaluated_by Note


Meta-data
~~~~~~~~~
Every entities'type have the following meta-data:

* `eid (Int)`, a unique identifier
* `creation_date (Datetime)`, date on which the entity has been created
* `modification_date (Datetime)`, lastest date on which the entity has been modified

* `created_by (CWUser)`, relation to the user which has created this entity

* `owned_by (CWUser)`, relation to the user()s considered as owner of this
  entity, the entity's creator by default

* `is (Eetype)`, special relation to specify a variable type.

A user's entity has the following schema:

:CWUser:
  ::

	login  	  (String) not null
	password  (Password)
	firstname (String)
	surname   (String)


Basis queries
-------------
0. *Every persons* ::
   
      Person X

   or ::

      Any X WHERE X is Person


1. *The company named Logilab* ::

      Company S WHERE S name 'Logilab'


2. *Every entities with a "name" attribute whose value starts with 'Log'* ::

      Any S WHERE S name LIKE 'Log%'

   or ::

      Any S WHERE S name ~= 'Log%'

   This query may return Person or Company entities.


3. *Every persons working for the Logilab company* ::

      Person P WHERE P works_for S, S name "Logilab"

   or ::

      Person P WHERE P works_for S AND S name "Logilab"


4. *Company named Caesium ou Logilab* ::

      Company S WHERE S name IN ('Logilab','Caesium')

   or ::

      Company S WHERE S name 'Logilab' OR S name 'Caesium'


5. *Every company but ones named Caesium ou Logilab* ::

      Company S WHERE NOT S name IN ('Logilab','Caesium')

   or ::

      Company S WHERE NOT S name 'Logilab' AND NOT S name 'Caesium'


6. *Entities evaluated by the note with eid 43* ::

      Any X WHERE X evaluated_by N, N eid 43


7. *Every persons order by birthday from the youngest to the oldest* ::
   
      Person X ORDERBY D DESC WHERE X birthday D

   Notice you've to define a variable using the birthday relation to use it in the
   sort term. 


8. *Number of persons working for each known company* ::
   
      Any S, COUNT(X) GROUPBY S WHERE X works_for S

   Notice you've that since you're writing a grouped query on S, X have to be
   either grouped as well or used in an aggregat function (as in this example).


   
Advanced
--------
0. *Person with no name specified (i.e NULL)* ::

      Person P WHERE P name NULL


1. *Person which are not working for any company* ::

      Person P WHERE NOT p works_for S


2. *Every company where person named toto isn't working* ::

      Company S WHERE NOT P works_for S , P name 'toto'


3. *Every entity which have been modified between today and yesterday* ::

      Any X WHERE X modification_date <= TODAY, X modification_date >= TODAY - 1


4. *Every note without type, to be done in the next 7 days, ordered by date* ::

      Any N, D where N is Note, N type NULL, N diem D, N diem >= TODAY,
      N diem < today + 7 ORDERBY D


5. *Person with an homonym (without duplicate)* ::

      DISTINCT Person X,Y where X name NX, Y name NX

   or even better (e.g. without both (Xeid, Yeid) and (Yeid, Xeid) in the results) ::

      Person X,Y where X name NX, Y name NX, X eid XE, Y eid > XE