Contents |
The Google App Engine (GAE) does not support relational databases. Instead, the GAE implements a datastore that stores and performs queries over data objects, known as entities, which have one or more properties. OpenBD maps both ColdFusion Structures and ColdFusion Component (CFC) instances to GAE entities. Specifically, the simple Key-value pairs of a struct, and the simple variables within a CFC "variables" scope are stored in the GAE datastore as entity properties. Here the simple refers to: number, boolean, string, date, or an array thereof. Currently it is not possible to store nested structs/cfc's. For a workaround to this limitation please see this forum post.
As a CFML programmer, you store struct and CFC instances in the datastore, and query the datastore to retrieve arrays of struct and CFC instances.
Structs and CFCs in the datastore are grouped into collections known as "kinds," which is somewhat of a misnomer since "kinds" are heterogenous collections of entities. That is, the entities within a "kind" do not all have to be of the same type. Multiple types of entities can be stored within a "kind". Therefore, a query of a "kind" may return an array containing both structs and CFCs where even the structs and/or CFCs may be of different types (which fits very nicely into the dynamic programming model supported by CFML).
OpenBD implements the GoogleWrite() function that is used to write a single struct or CFC instance to the datastore. The syntax of the GoogleWrite() function is:
googleKey = GoogleWrite( object [,kind [,keyName]] )
| Parameter | Description |
|---|---|
| object | Either a structure or a CFC instance |
| kind | Optional if object is a previously stored struct (obtained from a call to googleRead() for example). Required if object is a struct instance that has never been stored in the datastore before. Optional if object is a CFC. When omitted for a CFC, defaults to the name of the CFC without the package path. Case-insensitive (i.e. "MyKind" is the same as "MYKIND") |
| keyName | Optional. If you specify this parameter, you must also specify the "kind" parameter. Case-insensitive (i.e. "MyKeyName" is the same as "MYKEYNAME") |
The return value--"googleKey"--is a web-safe string that uniquely indentifies the CFC instance within the datastore. The googleKey can be used to read or delete a single entity from the datastore; the googleKey string is "web safe" and may be embedded within a URL or stored within a cookie.
Below is an example of using the GoogleWrite() function to write a CFC:
<cfscript> emp = createObject( "component", "Employee" ); emp.setFirstName( "John" ); emp.setLastName( "Smith" ); emp.setAge( 24 ); </cfscript>
Here's how to write the "emp" CFC created above to the datastore; the "kind" parameter defaults to the CFC name "Employee":
<cfset key = googleWrite( emp )>
As an alternative to letting the "kind" parameter default to the CFC name "Employee", it can be set explicitly; for example, to "MyCompany":
<cfset key = googleWrite( emp, "MyCompany" )>
The value of the "kind" parameter is later used when querying the datastore.
As an alternative to the GoogleWrite() function, the OpenBD "component.cfc" defines a "googleWrite" function that is inherited by all CFCs. Therefore, the examples above can be rewritten as:
<cfset key = emp.googleWrite()> <cfset key = emp.googleWrite( "MyCompany" )>
The GoogleWrite() function may be used to both create and update structs and CFCs within the datastore.
Here's an example of using the GoogleWrite() function to write 2 structs:
<cfscript> car = StructNew(); car["Class"] = "Sedan-Midsize"; car["Make"] = "Audi"; car["Model"] = "A6"; keyString = googleWrite(car, "MyCarLot"); car = StructNew(); //must do this else when you write car the 2nd time you'll just be updating rather than inserting car["Class"] = "Sedan-Compact"; car["Make"] = "Volkswagen"; car["Model"] = "Jetta"; keyString = googleWrite(car, "MyCarLot"); </cfscript>
As an alternative, the googleWrite() function may be called directly on a cfstruct instance:
keyString = car.googleWrite("MyCarLot");
OpenBD implements a subset of the JDOQL query language for querying CFCs within the datastore. The supported syntax is:
select from <kind> [where <filter>] [order by <sort clause>] [range <from index>, <to index>]
Every query must start with "select from <kind>", where <kind> is one of the values specified when adding entities to the datastore. The "where" and "order by" clauses are both optional, but must appear in that order if both are specified. From the examples above, the following would be valid queries (which will return every entity of the specified kind):
select from Employee select from MyCompany
The "where" clause is used to filter, or select subsets of struct or CFC instances, from within the specified "kind" collection; for example:
select from Employee where lastName == 'Smith'
The "where" clause supports a very limited set of operators. The supported equality operators are:
== equals > greater than >= greater than or equals < less than <= less than or equals
Note that there is no "not equals" operator. The only supported boolean operator is "&&" (and), for example:
select from Employee
where lastName == 'Smith'
&& age >= 25
The "order by" clause specifies one or more properties on which to sort the returned array:
select from Employee
where lastName == 'Smith'
&& age >= 25
order by age asc
The "range" clause specifies a subset of the results to return in the array. The <from index> and <to index> values are 1-based; <from index> must be 1 or greater, and <to index> must be greater than <from index>. The value at <to index> is not returned. For example the following query returns 5 CFCs from index positions 5 through 9 (the CFC at index position 10 is not returned):
select from Employee range 5, 10
There are additional restrictions on queries as described in the GAE documentation on queries and indexes.
The <kind> is case-insensitive; however all other parts of the query string, including property names and values, are case-sensitive.
Queries may be executed using either the GoogleQuery() function or the CFQUERY tag. The GoogleQuery() function has the following syntax:
resultArray = GoogleQuery( queryString )
The CFQUERY tag has the following syntax (the "dbtype" attribute is optional):
<cfquery dbtype="google" name="resultArray"> queryString </cfquery>
The "resultArray" is an array containing CFC's or structs (or both) that match the criteria specified in the "where" clause, sorted as specified in the "order by" clause. Note again that the elements in this array need not be all of the same type, but may be of multiple types as long as they satisfy the criteria specified in the "where" clause.
Here are some examples of the GoogleQuery() function and CFQUERY tag:
<cfset employees=GoogleQuery( "select from Employee" )>
<cfquery name="oldSmiths">
select from Employee
where lastName == 'Smith'
&& age >= 25
order by age asc
</cfquery>
<cfquery dbtype="google" name="resultArray">
select from MyCarLot where Make=="Volkswagen"
</cfquery>
As mentioned above, the "googleKey" value returned from the GoogleWrite() function is "web safe" and can be stored within a URL string or a cookie. This value can then be used to read a single struct or CFC from the datastore. As an alternative to using the googleKey, an entity that was written using a specified kind & keyName may be read back in using that same kind & keyName:
object = GoogleRead( googleKey | kind, keyName)
| Parameter | Description |
|---|---|
| googleKey | value returned from GoogleWrite(). If used, then kind and keyName must not be used. |
| kind | If used, then googleKey must not be used. kind is used by this function together with keyName to create a googleKey that is then used to read the entity from the datastore. |
| keyName | If used, then googleKey must not be used. keyName is used by this function together with kind to create a googleKey that is then used to read the entity from the datastore. |
The GoogleRead() function throws an exception if the entity specified by the key (or kind + keyName combination) does not exist in the datastore.
Alternatively, batch reads are supported by passing an array of keys to GoogleRead(). In this case a potentially heterogenous result array (the same as that returned by googleQuery) is returned. When passing an array of keys to GoogleRead(), no exception is thrown if a specified key is not found in the datastore. As typical for asynchronous batch reads, the order (and size) of the returned array is not guaranteed to match that of the passed-in array.
Here is an example of doing a batch read:
<cfscript> keys = arrayNew(1); keys[1] = key01; keys[2] = key02; resultArray = googleRead(keys); </cfscript>
Finally, the GoogleKey() function can be used to retrieve the key for a struct or CFC instance:
googleKey = GoogleKey( object )
The GoogleKey() function returns an empty string if the specified struct or CFC instance is not stored in the datastore.
The GoogleDelete() function is used to delete a single struct or CFC, or an array containing structs, CFCs, or both from the datastore, and has the following syntax:
GoogleDelete( googleKey | googleKey[] | object | object[] )
| Parameter | Description |
|---|---|
| googleKey | the value returned by the GoogleWrite() function (or an array thereof). |
| object | a struct or cfc (or array containing structs, cfc's, or both) |
For example, to write and then immediately delete a CFC from the datastore:
<cfscript> key = GoogleWrite( emp ); GoogleDelete( key ); //or GoogleDelete( emp ); </cfscript>
For example, to delete every entity from the "Employee" kind:
<cfscript> employees = GoogleQuery( "select from Employee" ); GoogleDelete( employees ); </cfscript>
As an alternative to the GoogleDelete() function, the OpenBD "component.cfc" defines a "googleDelete" function that is inherited by all CFCs. Therefore, an example above could be rewritten as:
<cfscript> key = GoogleWrite( emp ); emp.googleDelete(); </cfscript>
A complete working example of the concepts described above may be found within the openbd_google branch of the OpenBD
project. Specifically at this path: <module-root>\war\demo\gae\datastore\