Object query

Overview

EZOQL stands for EZPDO Object Query Language. EZOQL is a mini query language that resembles the standard SQL SELECT statements. It provides great flexibility in object retrieval.

Despite the resemblance to standard SQL statements, please keep in mind EZOQL is not your ordinary SQL. EZOQL is fully object-orientated. It understands notions like inheritance, polymorphism and object relationships.

You can find the EZOQL syntax in rigorous Backus Normal Form (BNF) here, but you can safely skip it. If you are familiar with the standard SQL SELECT statement, it probably takes no time to for you to understand EZOQL. Even if you don’t, the following examples should easily get you started.

Basic queries

From and Where

EZOQL allows you to do queries using the keywords from and where. Please note that EZOQL keywords are case-insensitive. So ‘from’ is the same as ‘FROM’. The following are examples of simple queries. Notice the use of ‘?’ as the placeholder for parameters passing into queries.

// get EZPDO manager 
$m = epManager::instance();
 
// find books by exact title
$books = $m->find("from Book as b where b.title = ?", $title);
 
// find books by the wild card (use LIKE)
$books = $m->find("from Book as b where b.title like 'Intro%'");
 
// find books who title is set to null
$books = $m->find("from Book as b where b.title is null");
 
// find books that has less than 100 pages
$books = $m->find("from Book as b where b.pages < ?", $pages);
 
// combination of search expressions
$books = $m->find("from Book as b where b.title like ? and b.pages < ?", $title, $pages);

The in operator

Starting from EZPDO 1.1.61), the in operator is supported in EZOQL. Here is a simple use.

// find books of which the prices is either $2.50 or $100.01
$books = $m->find("from Book as b where b.price in (2.50, 100.01)");
// find books authored by either Joe or Jane
$books = $m->find("from Book as b where b.author.name in ('Joe Smith', 'Jane Smith')");

Alternatively, you could pass an array as the parameter for the in operator.

// find books of which the prices is either $2.50 or $100.01
$books = $m->find("from Book as b where b.price in (?)", array(2.50, 100.01));
// find books authored by either Joe or Jane
$books = $m->find("from Book as b where b.author.name in (?)", array('Joe Smith', 'Jane Smith'));

Sorting and limit

The power of EZOQL dose not stop at finding objects. It can also help you sort (order by … asc|desc) and limit your search results.

// find books and sort by book id (default ascending order)
$books = $m->find("from Book as b where b.title like ? order by b.id", $title);
 
// find books and sort by id in ascending order
$books = $m->find("from Book as b where b.title like ? order by b.id asc", $title);
 
// find books and sort by id in desscending order
$books = $m->find("from Book as b where b.title like ? order by b.id desc", $title);
 
// find books and sort by id in desscending order and limit to the first two only
$books = $m->find("from Book as b where b.title like ? order by b.id desc limit 0, 2", $title);

You can also sort by multiple keys and orders. For example,

// find books and sort by id (ascending) and title (descending)
$books = $m->find("from Book as b where b.title like ? order by b.uuid asc, b.title desc", $title);

Sometimes order a set of returned objects randomly is useful. With EZOQL, You can use order by random(). Here is an example,

// find books with its matching a pattern in a random order
$books = $m->find("from Book as b where b.title like ? order by random()", $title);

Simplified syntax

One may challenge the necessity of using the aliases (i.e. the as clauses). They are indeed not required. The following three statements work exactly the same way.

$books = $m->find("from Book as b where b.title like ? order by b.uuid desc", $title);
$books = $m->find("from Book where Book.title like ? order by Book.uuid desc", $title);
$books = $m->find("from Book where title like ? order by uuid desc", $title);

Aggregate functions

Starting from EZPDO 1.1.02), aggregate functions are supported in EZOQL. The supported aggregate functions in EZOQL are

  • AVG(),
  • COUNT(),
  • MAX(),
  • MIN(), and
  • SUM().

The importance of the functions is obvious. Without them, if you want to compute the average of an integer or float variable in a class, you would have to fetch all the objects in the class. Retrieving all objects in a class (imagine all rows in a large table) is an expensive operation that normally you don’t want to do. With these functions the computation is delegated to the databases.

Example: Say you want to know what is the average cost per page and average cost per book for all PHP books, do this.

$cost = $m->find("sum(price) from Book where title like '%PHP%'"); 
$num_pages = $m->find("sum(pages) from Book where title like '%PHP%'");
$num_books = $m->find("count(*) from Book where title like '%PHP%'");
$cost_per_page = $cost/$num_pages;
$cost_per_book = $cost/$num_books;

Advanced queries

With the advent of EZPDO 1.1.03) comes the new EZOQL engine that allows you to do more advanced queries.

One of the biggest advantages that EZPDO has over other ORM packages is its capability of handling object relationships without involving you manually creating and maintaining relationships among database tables. Instead you can simply use @orm has|composed_of one|many to define your relationship variables in the class and EZPDO takes care of the creation and maintenance of relationship tables automatically.

EZOQL also provides an easy way for you to navigate through the relationship variables defined in your classes. And this is where EZOQL differs greatly from regular SQL statements. The following examples will make this clear.

Single-valued vars

Using a single-valued relationship var in EZOQL is the same as using a primitive var like we have done in the above examples. For example, if you want to use zipcode in the Contact object associated to an Author object to find all authors within the same zipcode, you can fire a query like this.

// find all authors within the same zip code
$authors = $m->find("from Author as a where a.contact.zipcode = '12345'); 

The variable ‘a.contact.zipcode’ involves one object relationship, an Author has one Contact. You can access variable more than one levels from the original object/class in your data model. For example,

// find all authors living on Walnut street
$authors = $m->find(
  "from Authors as a where a.contact.address.street LIKE '%Walnut Street%'");

Two object relationships are involved in ‘a.contact.address.street: an Author has one Contact and a Contact has an Address. Sometimes it is handy to use an associative array (or an example object) to specify the search criteria. You can use the placeholder (?) and write the query like this,

// find all authors living on Walnut street
$authors = $m->find(
  "from Authors as a where a.contact = ?", 
  array('address' => 
    array('street' => '100 Walnut Street') 
  ));

To use an example object for search, the query can be written as

// create an example object
$contact = $m->create('Contact');
$contact->address = $m->create('Address');
$contact->address->street = '100 Walnut Street';
 
// find all authors living on Walnut street
$authors = $m->find("from Authors as a where a.contact = ?", $contact);

Many-valued vars

To use a many-valued relationship var in query, you can use contains(). We give some examples.

Example-1: Say you want to find a book that is written by an author whose name is “Smith”. Keep in mind that a book can have more than one authors so authors is a many-valued relationship var in class Book. You can do a query like this.

$books = $m->find("from Book as b where b.authors.contains(a) and a.name = 'Smith'");

The contains() function following the many-valued variable (authors) has an alias (a) of class Author as its argument. The following condition, “a.name = ‘Smith’”, specifies the selection condition for the alias (a).

Example-2: You can get the same result by passing an array argument to the query,

$books = $m->find(
  "from Book as b where b.authors.contains(?)", array('name' => 'Smith'));

or by passing an example object,

$author = $m->create('Author');
$author->name = 'Smith';
$books = $m->find("from Book as b where b.authors.contains(?)", $author);
1) Now available in the nightly builds since 2007.02.10
2) Now available in the nightly builds since 2005.12.08.
3) Now available in the nightly builds since 2005.12.08.

8 user comments

  1. EZPDO » Blog Archive » EZPDO in the last week on March 19th, 2005:

    […] find it easy to use the runtime API to manage objects, and the mini object query language, EZOQL, to search objects. Auto-compile and auto-flush are another two useful features noted by many. We have a […]

  2. Danilo Medeiros on October 20th, 2005:

    It´s very important to note that the result of a find is always an array of objects, even if there is only one object resulting. I always get myself confused with this…

  3. kL on January 19th, 2006:

    // find books that has less than 100 pages
    $books = $m->find(”from Book as b where b.pages
    You mean with given number of pages?

  4. WASABI on February 15th, 2006:

    $cost = $m->find(”sum(price) from Book where title like ‘%PHP%’”);
    $num_pages = $m->find(”sum(pages) from Book where title like ‘%PHP%’”);
    $num_books = $m->find(”count(*) from Book where title like ‘%PHP%’”);
    $cost_per_page = $price/$num_pages;
    $cost_per_book = $price/$num_books;

    The last two lines reference variable $price, should say $cost?

  5. ezpdo4php on February 15th, 2006:

    yes, it should be $cost. corrected. thanks.

  6. Daniel on August 20th, 2006:

    Is it possible to perform the SQL “GROUP BY” functionality?
    I need to select a set of objects and calculate the sums of their field for groups of objects.
    The result should contain a set of N sums. Each sum for the group of objects (grouped by another field).
    In SQL it would be 1 query with the “GROUP BY” clause.
    Here I have to perform N queries for each group…

  7. dobes on December 17th, 2006:

    How can I add indexes to accelerate my queries? If I have large tables, I would like to avoid a linear scan.

  8. ezpdo4php on December 17th, 2006:

    See this post.

Post your comments

XHTML: tags you can use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>

Couldn't find your convert utility. Check that you have ImageMagick installed.