Previous Index Next

5 Google App Engine Specific Features

This chapter describes DAO generator features specific for Google App Engine target.

5.1 GAE Types

5.1.1 GAE Core Types

As of the version 1.2 we support all GAE core types including Key, GeoPt or User.

AuDAO recognizes all GAE core classes by the column/type/@class attribute for types Serializable and List. If you specify a non-core GAE class, then it is stored as GAE types Blob or ShortBlob - according to the attribute column/type/@max-length (ShortBlob is used only when max-length <= 500).

For the GAE specific objects the column/type/@class attribute must start with prefix "gae:" following by the simple class name - for example class="gae:GeoPt". Example of the XML:

  <column name="location">
    <type class="gae:GeoPt">Serializable</type>
  </column> 
will allow you to pass GeoPt objects directly into your DTOs:
  dto.setLocation( new GeoPt( latitude, longitude )); 

Do not specify the attribute column/type/@max-length for GAE core types. If you do so, then the objects will be stored as Blob or ShortBlob - according to the max-length' value.

See also: XSD - column type

5.1.2 Lists

As of the version 1.2 the lists support almost the same types as simple fields including all GAE core types like Key, GeoPt or User. The only restriction is that lists cannot contain enumeration types now.

We recognize the following list types:

Name Example of Java Type Example of XML Definition Elements Stored as
list of core java.lang types List<Boolean> List<Boolean> Boolean
list of java.util.Date List<java.util.Date> <type class="java.util.Date">List</type> java.util.Date
list of other GAE core types List<GeoPt> <type class="gae:GeoPt">List</type> GeoPt
list of other DTO types - short List<MyDto> <type class="table:my_dto" max-length="500">List</type> ShortBlob
list of other DTO types List<MyDto> <type class="table:my_dto">List</type> Blob
list of other types - short List<com.foo.MyClass> <type class="com.foo.MyClass" max-length="500">List</type> ShortBlob
list of other types List<com.foo.MyClass> <type class="com.foo.MyClass">List</type> Blob
native list List <type class="java.util.List">Serializable</type> exactly as stored in list
anonymous list - short List <type max-length="500">List</type> ShortBlob
anonymous list List <type>List</type> Blob

Native lists allow you to store exactly what you want - no conversions are performed. It is your responsibility to store to list only types supported by GAE.

Anonymous lists allow you to create lists of several types objects which are converted to Blobs or ShortBlobs.

See also: XSD - column type

5.2 Storage Options

GAE's datastore is schema-less and defines one column indexes by default. We will show you how to optimize DAOs generated by AuDAO.

5.2.1 Do Not Store Null Values

By default AuDAO generates code which stores null values. This is similar to normal RDBMS. But null values occupies a place in the storage and also consumes CPU when storing/retrieving. AuDAO allows you to not store null values optionally.

You can specify exactly in which columns/properties your DAOs will store nulls and where don't. It is specified by the <gae empty="true"> flag added to the column definition.

Example:

  <column name="normal_type">
    <type max-length="500">String</type>
    <not-null/>
    <edit/>
  </column>

  <column name="null_normal_type">
    <ref column="normal_type"/>
    <edit/>
  </column>

  <column name="null_empty_type">
    <ref column="normal_type"/>
    <edit/>
    <gae empty="true"/>
  </column> 
The null_empty_type column does not store nulls, the null_normal_type column stores nulls and the normal_type column does not store nulls - it is a <not-null/> column.

You can only tag as empty columns which are not <not-null/>. the following example shows nonsense definitions:

  <!-- nonsense
  <column name="empty_type">
    <ref column="normal_type"/>
    <not-null/>
    <edit/>
    <gae empty="true"/>
  </column>
  --> 

You cannot search for null values in columns which do not store nulls. If you want to search for null values, then you have to let you DAOs to store nulls into datastore.

See also: XSD - gae element

5.2.2 Explicit Unindexed Columns

By default AuDAO generates code which stores column values as indexed ones. It means that you can search for values stored in such columns. But it also means that an index is created for such column and such index occupies space in the datastore. Also additional CPU is needed when storing/updating whole entity.

AuDAO allows you to disable such indexing for the given column explicitly. It is specified by the <gae unindexed="true"> flag added to the column definition.

If a column is tagged as unindexed, it behave as empty for null values - it means that no null values are stored in such column. The reason is obvious: you cannot search by the unindexed columns and null values would only occupy place in the datastore without any advantage.

Example:

  <column name="normal_type">
    <type max-length="500">String</type>
    <not-null/>
    <edit/>
  </column>

  <column name="unindexed_type">
    <ref column="normal_type"/>
    <not-null/>
    <edit/>
    <gae unindexed="true"/>
  </column>

  <column name="null_normal_type">
    <ref column="normal_type"/>
    <edit/>
  </column>

  <column name="null_unindexed_type">
    <ref column="normal_type"/>
    <edit/>
    <gae unindexed="true"/>
  </column>

  <column name="null_empty_unindexed_type">
    <ref column="normal_type"/>
    <edit/>
    <gae unindexed="true" empty="true"/>
  </column> 
The null_empty_unindexed_type column does not store nulls, but the empty flag is useless, the null_unindexed_type column does not store nulls, the null_normal_type column stores nulls and you can search by all values and the unindexed_type column does not store nulls - it is a <not-null/> column.

See also: XSD - gae element

5.3 Finders and Lists

AuDAO offers you more functionalities concerning Lists. We will describe here the rules how finder method signatures are constructed and how the finder filters are implemented.

5.3.1 Finder Signature - List or Element

The basic rule how finder signatures are generated is the following: the finder's parameter is a List if and only if you explicitly specify it.

The following example shows usage of index finders:

  <columns>
    <column name="location">
      <type class="gae:GeoPt">Serializable</type>
    </column>
    <column name="friends">
      <type class="gae:User">List</type>
    </column>
  </columns>
  <indexes>
    <index name="inx_location">
      <columns>
        <column name="location"/>
      </columns>
    </index>
    <index name="inx_friends">
      <columns>
        <column name="friends"/>
      </columns>
    </index>
  </indexes>
  <methods>
    <find>
      <index name="inx_location"/>
    </find>
    <find name="byOneFriend">
      <index name="inx_friends"/>
    </find>
    <find name="byAllFriends">
      <index name="inx_friends" list="true"/>
    </find>
  </methods> 
will generate these DAO methods:
  public MyEntity[] findByLocation( GeoPt location );
  public MyEntity[] findByOneFriend( User friends );
  public MyEntity[] findByAllFriends( List<User> friends ); 
You can see that only the last method findByAllFriends was generated with a List parameter, because the only one explicitly defined the list attribute.

The following example shows the same + two more methods generated using condition finders (same columns as above):

  <methods>
    <find name="byLocation">
      <condition>
        <query>location = :1</query>
        <params>
          <column name="location"/>
        </params>
      </condition>
    </find>
    <find name="byLocations">
      <condition>
        <query>location IN :1</query>
        <params>
          <!-- @java specifies the param name in java -->
          <column name="location" list="true" java="locations"/>
        </params>
      </condition>
    </find>
    <find name="byOneFriend">
      <condition>
        <query>friends = :1</query>
        <params>
          <column name="friends" java="friend"/>
        </params>
      </condition>
    </find>
    <find name="byAllFriends">
      <condition>
        <query>friends = :1</query>
        <params>
          <column name="friends" list="true"/>
        </params>
      </condition>
    </find>
    <find name="bySomeFriends">
      <condition>
        <query>friends IN :1</query>
        <params>
          <column name="friends" list="true"/>
        </params>
      </condition>
    </find>
  <methods>
will generate these DAO methods:
  public MyEntity[] findByLocation( GeoPt location );
  public MyEntity[] findByLocations( List<GeoPt> locations );
  public MyEntity[] findByOneFriend( User friend );
  public MyEntity[] findByAllFriends( List<User> friends );
  public MyEntity[] findBySomeFriends( List<User> friends ); 
Again, a List parameter is generated only when explicitly declared by the list attribute.

In the example above you can see that condition finders are more powerfull than index finders.

5.3.2 Expanded Conditions for List Properties

In the examples in the previous section you have seen methods findByAllFriends( List<User> users). It was an example of so called "expanded conditions".

Normally, when a non-list parameter is passed to the finder, then the parameter is used in a plain condition like equality condition e.g. location = myparam.

But when a list parameter is passed to the finder containing simple equality condition, then the simple equality condition is repeated and concatenated by 'AND' operator as many times as the list's values.

Example: calling of

dao.findByAllFriends( Arrays.asList( user1, user2, user3 ))
will expand condition into
friends=user1 AND friends=user2 AND friends=user3
It means that only entities which have all three users stored in their 'friends' property will be found.

Expanded conditions are allowed only for List properties.

5.3.3 "IN" Conditions

"IN" conditions are allowed for both List and non-list properties. In our example the corresponding methods were findByLocations( List<GeoPt> locations) and findBySomeFriends( List<User> friends).

As the result of the "IN" operator it is a union of all entities matching at least with one list's element. In our examples it is when the entities' location is int the 'locations' list and when the entities' list of friends has at least one common item with the list of friends passed as the parameter.

5.4 Caches

The GAE implementation currently uses 3-level caching mechanism. The caches are used only for entities fetched by primary keys. Entities fetched as results of Queries are not cached now.

All caches are synchronized so they can be accessed by several threads in parallel.

5.4.1 L0 Cache - Entity Memory Cache

This is a short term cache allowing to optimize subsequent calls of findByPrimaryKey and update.

The items are stored in memory only and there exists only one global instance for all non-transaction calls - serving for all kinds:

  protected static DtoCache<Key, Entity> entityCache = new ExpiringMemoryDtoCacheImpl<Key, Entity>( 100, 10 );

For explicit transaction calls (when an explicit Transaction exists) each DAO instance creates own entity cache instance:

  protected DtoCache<Key, Entity> entityCache;
  ...
  
  // when really needed:
  entityCache = new ExpiringMemoryDtoCacheImpl<Key, Entity>( 100, 10 );

To explicitly clear the non-transactional gloabl entity cache you can call static method clearEntityCache() of the class GaeAbstractDaoImpl (API).

5.4.2 L1 Cache - DTO Memory Cache

This cache must enabled by the config/dao-impl/default-cache element in the source XML file. The cached items are stored in the memory.

See also: Default DTO Cache

5.4.3 L2 Cache - DTO MemcacheService Cache

This cache is enabled whenever L1 cache is enabled. It uses MemcacheService and no expiration policy by default. To set expiration use the attribute l2-expire-millis:

  <config>
    <dao-impl>
      <default-cache max-size="50" expire-millis="60000" l2-expire-millis="300000"/>
    </dao-impl>
  </config> 

5.5 Dynamic GQL Parser

A part of AuDAO runtime libraries is a "Dynamic GQL Parser", which dynamically parses GQL string queries and converts them into GAE datastore API calls (GAE datastore API itself does not provide such functionality).

This parser is used by all generated dynamic finders. But the parser can be used as an independent module.

The GQL dynamic parser module (audao-runtime-gql-*.jar) has the following dependencies:

A bigger example of usage - GQL Console - you can find at http://vaclavb.blogspot.com/2010/02/google-app-engine-data-viewer-gql-java.html

Example - parses GQL string into low-level datastore Query:

  GqlDynamic gqld = new GqlDynamic();
  Query q = gqld.parseQuery( "SELECT * FROM MyEntity WHERE prop='test'" );

Example - parses GQL string into low-level datastore PreparedQuery:

  GqlDynamic gqld = new GqlDynamic();
  gqld.setDatastoreService( DatastoreServiceFactory.getDatastoreService());

  PreparedQuery pq = gqld.prepareQuery(
      "SELECT * FROM MyEntity WHERE propName='test'" );

Example - parses GQL string into PreparedQuery and FetchOptions:

  GqlDynamic gqld = new GqlDynamic();
  gqld.setDatastoreService( DatastoreServiceFactory.getDatastoreService());

  PreparedQuery pq = gqld.prepareQuery(
      "SELECT * FROM MyEntity WHERE propName='test' LIMIT 20" );

  for ( Entity ent : pq.asIterable( gqld.getFetchOptions())) {
      // process entities here
      ...
  }

Example - parses GQL string with parameters into PreparedQuery and FetchOptions:

  GqlDynamic gqld = new GqlDynamic();
  gqld.setDatastoreService( DatastoreServiceFactory.getDatastoreService());

  PreparedQuery pq = gqld.prepareQuery(
      "SELECT * FROM MyEntity WHERE propName=:1 LIMIT 20", "test" );
  for ( Entity ent : pq.asIterable( gqld.getFetchOptions())) {
      // process entities here
      ...
  }

Example - escaping kind and property names which conflict with GQL keywords:

  gqld.prepareQuery( "SELECT * FROM 'Order' WHERE 'limit'=:1", mylimit );

Example - collections cause exploding condition into multiple ANDed conditions:

  gqld.prepareQuery(
      "SELECT * FROM MyEntity WHERE listProp=:1 AND singleProp=:2",
      Arrays.asList( 1, 2, 3), 4);
the condition will be: listProp=1 AND listProp=2 AND listProp=3 AND singleProp=4

Example - the IN operator accepts collections:

  gqld.prepareQuery(
      "SELECT * FROM MyEntity WHERE prop IN :1", Arrays.asList( 1, 2, 3));

Example - the IN operator accepts hard-coded lists which values can be expressions (I heard that Python's parser did not allow this):

  gqld.prepareQuery(
      "SELECT * FROM MyEntity WHERE prop IN (:1, :2, KEY('Entity2', :3))",
      1, 2, "myKeyName");

See also: GQLDynamic (API)

5.6 Extended GQL Parser

A part of AuDAO runtime libraries is an "Extended GQL Parser", which dynamically parses GQL string statements and converts them into GAE datastore API calls (GAE datastore API itself does not provide such functionality).

The "extended" means that the statements are not limited to "SELECT" queries only, but you can use also other SQL commands like "INSERT", "DELETE" and UPDATE and also use so called soft conditions, expressions and nested queries.

We do not recommend to use this library in the real application as the way to access GAE's datastore. Instead of that we think that this library can be useful for administrators or can be used by some admin tasks to preprocess, migrate or download data.

The parser can be used as an independent module.

The extended GQL parser module (audao-runtime-gqlext-*.jar) has the following dependencies:

Example - parses GQL string and executes prepared query:

  import com.spoledge.audao.parser.gql.GqlExtDynamic;
  import com.spoledge.audao.parser.gql.PreparedGql;

  GqlExtDynamic gqld = new GqlExtDynamic( DatastoreServiceFactory.getDatastoreService());
  PreparedGql pq = gqld.prepare( "SELECT prop2 FROM MyEntity WHERE prop=:1" );

  for (String val : Arrays.asList( "value1", "value2" )) {
      for (Entity ent : pq.executeQuery( val )) {
          ...
      }
  } 

Example - updates entities - change indexed property to unindexed. Natively finds records by prop's value and then filters them again by expression (starting by a specified character).

  GqlExtDynamic gqld = new GqlExtDynamic( DatastoreServiceFactory.getDatastoreService());
  PreparedGql pq = gqld.prepare(
      "UPDATE MyEntity SET unindexed('prop')=prop WHERE prop=:1 SOFT substr(prop2,1,1)=:2" );

  int count = pq.executeUpdate( "value1", "A"); 

You can also use a simple tool - 'gqlext.jsp' within your appengine application.

  1. put gqlext.jsp to your's web application directory (the main war directory)
  2. put commons-logging-1.1.1.jar to your's web application WEB-INF/lib directory
  3. put antlr-runtime-3.2.jar to your's web application WEB-INF/lib directory
  4. put audao-runtime-gqlext-*.jar to your's web application WEB-INF/lib directory
To limit acccess to the GQL console, you should add this section to your WEB-INF/web.xml config:
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Admin Area</web-resource-name>
      <url-pattern>/gqlext.jsp</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>admin</role-name>
    </auth-constraint>
  </security-constraint>
Point your web-browser to the 'gqlext.jsp' page.

Unlike the standard GQL consle, the extended GQL console allows you to change data in the datastore. Be carefull !!!

5.6.1 SELECT Statement

SELECT getcolumns FROM kind_or_query [where] [orderby] [limitoffset]

It allows you to fetch entities from the datastore, nested query or just to evaluate an expression.

The implementation fetches data from the underlying datasource (datastore, nested query or virtual kind), optionally filters by soft conditions and optionally maps the source entity to a destination one.

Examples:

  // returns all unmodified records:
  SELECT * FROM MyKind

  // returns only selected columns - filters natively in GAE by 'property':
  SELECT __key__,property FROM MyKind WHERE property > 1

  // filters natively in GAE by 'property' and then softly by 'unindexed_property':
  SELECT * FROM MyKind WHERE property > 1 SOFT unindexed_property=1

  // filters natively in GAE by 'property' and then softly by several properties:
  SELECT * FROM
    (SELECT * FROM MyKind
       WHERE property > 1
         SOFT unindexed_property=1 OR unindexed_property2=1)
    WHERE SOFT unindexed_property3=1

  // evaluates expression:
  SELECT sysdate() AS now FROM dual 

5.6.2 INSERT Statement

INSERT INTO kind (columns) VALUES (values)

It allows you to insert one entity into the datastore.

You can create your own keys, or you can let the GAE to generate long id key for you.

Examples:

  // inserts a new record with generated key:
  INSERT INTO MyKind (bool_prop,int_prop,string_prop)
    VALUES (true,1,'test')

  // inserts a new record with manually created key:
  INSERT INTO MyKind (__key__,bool_prop,int_prop,string_prop)
    VALUES (key('MyKind', 'john'),true,1,'test')

  // inserts a new record - the 'string_prop' will be unindexed property:
  INSERT INTO MyKind (bool_prop,int_prop,unindexed(string_prop))
    VALUES (true,1,'test') 

5.6.3 UPDATE Statement

UPDATE kind SET setcolumns [where] [orderby] [limitoffset]

It allows you to update one or several entities at once.

The implementation fetches data from the datastore, optionally filters by soft conditions and then updates each entity by the SET rule(s). The entities are put one-by-one back to the datastore.

Examples:

  // simply updates all records it founds:
  UPDATE MyKind SET bool_prop=true WHERE bool_prop=false

  // simply updates all records it founds (max 100):
  UPDATE MyKind SET bool_prop=true WHERE bool_prop=false LIMIT 100

  // removes null values of the property 'int_prop' (null -> <missing>)
  UPDATE MyKind SET empty(int_prop)=int_prop, bool_prop=true
    WHERE bool_prop=false LIMIT 100

  // removes property 'int_prop' (whatever -> <missing>)
  UPDATE MyKind SET empty(int_prop)=null, bool_prop=true
    WHERE bool_prop=false LIMIT 100

  // changes property 'int_prop' from indexed to unindexed:
  UPDATE MyKind SET unindexed(int_prop)=int_prop, bool_prop=true
    WHERE bool_prop=false LIMIT 100

  // changes property 'int_prop' from unindexed to indexed:
  UPDATE MyKind SET int_prop=int_prop, bool_prop=true
    WHERE bool_prop=false LIMIT 100 

5.6.4 DELETE Statement

DELETE FROM kind [where] [orderby] [limitoffset]

It allows you to delete one or several entities at once.

The implementation fetches data from the datastore, optionally filters by soft conditions and then deletes each entity.

Examples:

  // simply deletes all records it founds:
  DELETE FROM MyKind WHERE bool_prop=false

  // simply deletes all records it founds (max 100):
  DELETE FROM MyKind WHERE bool_prop=false LIMIT 100 

5.6.5 Soft Conditions

Soft conditions are conditions evaluated in the library on the client (as opposite to the normal conditions which are evaluated in the appengine on the server).

You should use soft conditions carefully and with conjunction with the normal ones.

Examples:

  SELECT * FROM MyKind WHERE indexed_property=:1 SOFT unindexed_property=:2 

5.6.6 Nested Queries

Nested queries can be used in:

  • in SELECT query instead of the FROM kind
  • condition IN
  • soft condition IN

In conditions the nested query must return only one column/field.

Examples:

  SELECT * FROM (SELECT * from MyKind WHERE prop=:1) WHERE SOFT prop2=:2
  DELETE FROM MyKind WHERE id IN (SELECT id FROM MyKindType WHERE name=:1)

5.6.7 Functions

5.6.7.1 Standard GQL Functions

These functions can be used in both kinds of conditions (standard + soft) and also in expressions.

The full list of these functions is at http://code.google.com/appengine/docs/python/datastore/gqlreference.html.

5.6.7.2 Mathematic Functions

These functions can be used in expressions and therefore in the soft conditions.

ABS (API) y = ABS( x )

ACOS (API) y = ACOS( x )

ASIN (API) y = ASIN( x )

ATAN2 (API) z = ATAN2( y, x )

ATAN (API) y = ATAN( x )

CEIL (API) y = CEIL( x )

COSH (API) y = COSH( x )

COS (API) y = COS( x )

DIV (API) z = DIV( x, y )
The standard div operator '/' uses this function.

EXP (API) y = EXP( x )

FLOOR (API) y = FLOOR( x )

LN (API) y = LN( x )

MINUS (API) z = MINUS( x, y )
The standard minus operator '-' uses this function.

MOD (API) z = MOD( x, y )

MUL (API) z = MUL( x, y )
The standard mul operator '*' uses this function.

PLUS (API) z = PLUS( x, y )
The standard plus operator '+' uses this function.

POWER (API) z = POWER( x, y )

RAND (API) y = RAND()

SIGN (API) y = SIGN( x )

SINH (API) y = SINH( x )

SIN (API) y = SIN( x )

SQRT (API) y = SQRT( x )

TANH (API) y = TANH( x )

TAN (API) y = TAN( x )

5.6.7.3 String Functions

These functions can be used in expressions and therefore in the soft conditions.

INSTR (API) index = INSTR( text, substring[, position[, occurence ]])
Same as Oracle's INSTR function.

SUBSTR (API) s = SUBSTR( text, position[, length ])
Same as Oracle's SUBSTR function.

LENGTH (API) x = LENGTH( text )
Returns the length of the text in characters.

LOWER (API) s = LOWER( text )
Returns the text in lowercase.

MUL (API) s = MUL( text, x )
Concatenates text many times as is the value of x. Same as Perl's 'x' operator.

PLUS (API) s = PLUS( text, whatever )
Concatenates text and any other value. The standard plus operator '+' uses this function.

TO_CHAR (API) s = TO_CHAR( date[, fmt ])
Converts objects to string. Optional 'fmt' string - the format used for dates ans numbers - see java.text.SimpleDateFormat and java.text.DecimalFormat . The default date format string is 'yyyy-MM-dd HH:mm:ss', the default number format string is '0.####'. If the converted object is not date nor number, then the toString() method is called.

UPPER (API) s = UPPER( text )
Returns the text in uppercase.

5.6.7.4 Date Functions

These functions can be used in expressions and therefore in the soft conditions.

MINUS (API) d = MINUS( date, x )
Subtracts x days from the date. Same as Oracle's functionality. The standard plus operator '-' uses this function.

PLUS (API) d = PLUS( date, x )
Adds x days to the date. Same as Oracle's functionality. The standard plus operator '+' uses this function.

SYSDATE (API) d = SYSDATE()
Returns the current date and time.

5.6.7.5 Other Functions

These functions can be used in expressions and therefore in the soft conditions.

DECODE (API) o = DECODE( val, search1, value1[, search2, value2 ...[, default]])
Same as Oracle's functionality.

GEOPT_LAT (API) lat = GEOPT_LAT( geopt )
Extracts latitude (double) from a geo point.

GEOPT_LNG (API) lng = GEOPT_LNG( geopt )
Extracts longitude (double) from a geo point.

KEY_ID (API) x = KEY_ID( key )
Extracts id (long) from a key.

s = KEY_NAME key )
KEY_NAME (API) Extracts name (String) from a key.

KEY_PARENT (API) key = KEY_PARENT( key )
Returns the parent key.

KEY_VALUE (API) o = KEY_VALUE( key )
Extracts id (long) or name (String) from a key.

LIST (API) l = LIST( [item1[, item2...] )
Creates a list - stored as multi-value property.

LIST_JOIN (API) s = LIST_JOIN( string, list )
Joins a list items into a string.

NVL (API) o = NVL( val, val2 )
Same as Oracle's functionality.

NVL2 (API) o = NVL2( val, val1, val2 )
Same as Oracle's functionality.

5.6.7.6 Pseudo Functions

EMPTY( string )
Forces the property with name string and null value to be empty (missing). This function can be used only in aliases.

KIND( string )
Escapes the kind name. This function can be used only in the kind section.

PROP( string )
Escapes the property name. This function can be used almost everywhere (expressions, conditions, column names).

UNINDEXED( string )
Forces the property with name string to be unindexed. This function can be used only in aliases.

5.6.7.7 User Defined Functions

All standard functions are defined in the package com.spoledge.audao.parser.gql.impl.soft.func. The parser dynamically looks up the function by its name. Only several functions are defined directly in the parser - the standard GQL functions (KEY(), DATE(), ...) and pseudo-functions.

To add a new user-defined function, you must create an implementation of the SoftFunction (API) and register it in the SoftFunctionFactory (API):

  import java.util.List;
  import com.spoledge.audao.parser.gql.impl.soft.SoftFunction;
  import com.spoledge.audao.parser.gql.impl.soft.SoftFunctionFactory;
  ...
  SoftFunctionFactory.getDefaultFactory().defineSoftFunction(
      "MY_FUNC", new SoftFunction() {
          public Object getFunctionValue( List<Object> args ) {
              // concatenates all parameters:
              StringBuilder sb = new StringBuilder();
              for (Object o : args)
                  sb.append( o );
              }
              return sb.toString();
          }
      }); 

You can also redefine any existing function (like ABS) by your own implementation.

We recommend to extend existing parent functions like Func (API), Func0 (API), Func1 (API), Func2 (API), MathFunc1 (API), StringFunc1 (API).

Another approach is to define own SoftFunctionFactory (API), which is responsible for creating/mapping of functions:

  import com.spoledge.audao.parser.gql.impl.soft.SoftFunctionFactory;
  ...
  SoftFunctionFactory.setDefaultFactory( new MyFunctionFactory()); 

5.6.8 Grammar

The real grammar is defined in files GqlExtParser.g, GqlExtLexer.g and GqlLexer.g.

  • SELECT getcolumns FROM kind_or_query [where] [orderby] [limitoffset]
  • UPDATE kind SET setcolumns [where] [orderby] [limitoffset]
  • DELETE FROM kind [where] [orderby] [limitoffset]
  • INSERT INTO kind (columns) VALUES (values)

getcolumns: comma separated list of:

  • * (asterisk) = all available properties
  • __key__ = the key
  • property = property specified by its name
  • entity_expression AS alias = expression's result stored into property specified by the alias
Example:
  SELECT * FROM ...
  SELECT property FROM ...
  SELECT prop('property') FROM ...
  SELECT 1+property AS property FROM ...
  SELECT list(property,:1) AS unindexed('property') FROM ...
  SELECT cos(property) AS empty('property') FROM ...  

setcolumns: alias = entity_expression [, alias2 = entity_expression...]

  UPDATE ... SET property = 'value'...
  UPDATE ... SET property = cos(:1 + property2) ...
  UPDATE ... SET prop('property') = null ...
  UPDATE ... SET empty(property) = null ...
  UPDATE ... SET unindexed(property) = property ...

columns: alias [, alias2 ...]

  INSERT INTO ... (property, property2) ...
  INSERT INTO ... (prop('property'), unindexed(property2)) ...

values: expression [, expression2 ...]

  INSERT INTO ... VALUES ('test', 1, null)
  INSERT INTO ... VALUES (:1,:2,:3)
  INSERT INTO ... VALUES (:1,:2, cos(:3) + sin(:3))

kind_or_query: one of:

  • kind
  • (SELECT ...) = nested query
  • dual = a special virtual kind which always returns exactly one empty record
Example:
  SELECT * FROM MyKind ...
  SELECT * FROM kind('com.foo.MyKind') ...
  SELECT * FROM (SELECT property FROM MyKind) ...
  SELECT sysdate() AS now FROM dual

kind: one of:

  • identifier
  • kind(string) = for escaping special characters or keywords
Example:
  SELECT * FROM MyKind ...
  SELECT * FROM kind('com.foo.MyKind') ...

where:

  • WHERE conditions [SOFT softconditions]
  • WHERE SOFT softconditions
Example:
  ... WHERE property=1
  ... WHERE property=1 SOFT cos(:1+property2) = sin(property3)
  ... WHERE SOFT property1 < property2

orderby: ORDER BY property1 [ASC|DESC] [, property2 [ASC|DESC] Example:

  ... ORDER BY property
  ... ORDER BY property DESC, property2 ASC

limitoffset: ORDER BY property1 [ASC|DESC] [, property2 [ASC|DESC]

  • LIMIT [offset,] limit
  • [LIMIT limit] OFFSET offset
Example:
  ... LIMIT 1
  ... LIMIT 10,1
  ... LIMIT 1 OFFSET 10
  ... OFFSET 10

conditions: condition [AND condition ...]

condition: one of:

  • ANCESTOR IS key_or_param
  • __key__ {=|<|>|<=|>=|!=} key_or_param
  • __key__ IN (key_or_param[, key_or_param2 ...])
  • __key__ IN param
  • __key__ IN (SELECT ...)
  • property {=|<|>|<=|>=|!=} value_or_param
  • property IN (value_or_param[, value_or_param2 ...])
  • property IN param
  • property IN (SELECT ...)
Example:
  ... WHERE ANCESTOR IS key('test',1)
  ... WHERE ANCESTOR IS :1
  ... WHERE __key__ > key('test',:1)
  ... WHERE property = :1
  ... WHERE property IN (:1,:2)
  ... WHERE property IN :1
  ... WHERE property IN (SELECT prop FROM ...)

softconditions: [NOT] softcondition [{AND|OR} [NOT] softcondition ...]
NOTE: parentheses in softconditions are not implemented yet. As a workaround you can use nested queries:

  // invalid:
  SELECT * FROM MyKind WHERE SOFT (property=1 OR property2=1) AND property3=2

	// valid:
  SELECT * FROM (SELECT * FROM MyKind WHERE SOFT property=1 OR property2=1) WHERE SOFT property3=2
	

softcondition: one of:

  • ANCESTOR IS entity_expression
  • entity_expression {=|<|>|<=|>=|!=} entity_expression
  • entity_expression IN entity_expression
  • entity_expression IN (entity_expression[, entity_expression ...])
  • entity_expression IN (SELECT ...)
Example:
  ... WHERE SOFT ANCESTOR IS key_parent('test',1)
  ... WHERE SOFT property1+property2 = cos(:1)
  ... WHERE SOFT nvl(property1,property2) IN (:1,:2)
  ... WHERE SOFT property IN (SELECT prop FROM ...) 

expression: an expression which cannot refer to any existing properties (INSERT):

  • operators '+', '-', '/', '*'
  • parentheses '(', ')'
  • functions
  • literals
  • parameters
Example:
  SELECT (1+cos(:1)) / (1-cos(:1)) AS val FROM dual
  SELECT 'con' + 'cat' + :1 AS val FROM dual
  SELECT sysdate() - 1 AS yesterday FROM dual
  SELECT decode(sign(rand()-0.5), -1, true, false) AS random_bool FROM dual

alias: one of:

  • __key__ = the key
  • property = property name
  • string = property name encapsulated in string quotes
  • empty(string) = if the property value is null, then the property will not be stored at all
  • unindexed(string) = the property is stored as an unindexed property
Example:
  SELECT __key__ FROM ...
  SELECT property FROM ...
  UPDATE ... SET unindexed(property)=property ...
  UPDATE ... SET empty(property)=property ... 

property: one of:

  • identifier = property name
  • prop(string) = escapes characters
Example:
  SELECT property FROM ...
  UPDATE ... SET prop('property')= ...

Previous Index Next
Online Generátor

Vyzkoušejte zadarmo náš online DAO generátor.

Vytvořte konfigurační XML soubor a získejte zadarmo vygenerované SQL skripty a Java třídy !

GQL Parser

Stáhněte si zdrojové či binární soubory GQL parseru.

Query q =
 new GqlDynamic()
  .parseQuery(
   "SELECT * FROM Ent");
Plná podpora GQL. Více...