Permalink
Browse files

all of gql in one nice commit. changes to gruntfile and webrunner.

  • Loading branch information...
1 parent 870e548 commit f8195d7ce9c7a87dcd8ae8e1257c01cd0a10d930 @briantoth briantoth committed Mar 8, 2013
Showing with 1,743 additions and 2 deletions.
  1. +13 −1 Gruntfile.js
  2. +286 −0 docs/gql.md
  3. +906 −0 src/plugins/pouchdb.gql.js
  4. +1 −1 src/plugins/pouchdb.mapreduce.js
  5. +537 −0 tests/test.gql.js
View
@@ -17,7 +17,8 @@ var srcFiles = [
var testFiles = fs.readdirSync("./tests").filter(function(name){
return (/^test\.([a-z0-9_])*\.js$/).test(name) &&
- name !== 'test.spatial.js' && name !== 'test.auth_replication.js';
+ name !== 'test.spatial.js' && name !== 'test.auth_replication.js' &&
+ name !== 'test.gql.js';
});
var browserConfig = [{
@@ -80,6 +81,12 @@ module.exports = function(grunt) {
"src/plugins/pouchdb.spatial.js"
]),
dest: 'dist/pouchdb.spatial-nightly.js'
+ },
+ gql: {
+ src: grunt.util._.flatten([
+ srcFiles, "src/plugins/pouchdb.gql.js"
+ ]),
+ dest: 'dist/pouchdb.gql-nightly.js'
}
},
@@ -91,6 +98,10 @@ module.exports = function(grunt) {
spatial: {
src: 'dist/pouchdb.spatial-nightly.js',
dest: 'dist/pouchdb.spatial-nightly.min.js'
+ },
+ gql: {
+ src: 'dist/pouchdb.gql-nightly.js',
+ dest: 'dist/pouchdb.gql-nightly.min.js'
}
},
@@ -259,5 +270,6 @@ module.exports = function(grunt) {
grunt.registerTask("test", ["testSetup", "node-qunit" ,"saucelabs-qunit", "publish-results"]);
grunt.registerTask("full", ["concat", "uglify"]);
grunt.registerTask("spatial", ["concat:spatial", "uglify:spatial"]);
+ grunt.registerTask("gql", ["concat:gql", "uglify:gql"]);
grunt.registerTask('default', 'build');
};
View
@@ -0,0 +1,286 @@
+---
+layout: default
+title: PouchDB, the JavaScript Database that Syncs!
+---
+
+# GQL Documentation
+
+The Google Query Language (GQL) interface provides an alternative method for accessing data.
+The version of GQL implemented here is based on the Google Visualization API Query Language
+(https://developers.google.com/chart/interactive/docs/querylanguage).
+The syntax of GQL queries should be familiar to those who have used SQL,
+but the capabilities of GQL are much more limited.
+
+GQL queries are performed by passing a query object to the gql method along with a callback.
+Callbacks are in the node.js idiom of `function(err, data)` Where the first argument will be undefined
+unless there is an error, and further arguments specify the result.
+Note that only identifiers and string literals are case-sensitive.
+
+### Language Syntax
+
+ * [Select](#select)
+ * [Where](#where)
+ * [Group By](#groupBy)
+ * [Pivot](#pivot)
+ * [Label](#label)
+ * [Functions](#functions)
+ * [Aggregation Functions](#aggregation_functions)
+ * [Scalar Functions](#scalar_functions)
+ * [Arithmetic Operators](#arithmetic_operators)
+ * [Miscellaneous](#miscellaneous)
+ * [Literals](#literals)
+ * [Identifiers](#identifiers)
+ * [Reserved Words](#reserved_words)
+
+## Perform a Query
+
+ db.gql(query, [options], [callback])
+
+Although only the query is mandatory, the callback is required to access the query result.
+Currently no query options are implemented.
+
+ var pouchdb;
+ Pouch('idb://test', function(err, db) {
+ pouchdb = db;
+ // Use pouchdb to call further functions
+ db.gql({select: "*", where: "type='Fire' and name is not null"}, function(err, result){
+ if(!err){
+ // Use the results of the query here
+ }
+ }
+ })
+
+## Select
+
+ db.gql({select: "`name!`, price-discount, upper(vendor)"}, callback)
+
+Select returns an object for each document in the database (unless limited by another clause).
+Each of these objects will be populated with the properties specified in the select clause.
+Arithmetic operators, aggregation functions, and scalar functions are all fair game.
+Properties that are missing from an object in the database are assigned null.
+
+With these documents in the database
+
+ {name!: "pencil", price: 2, discount: 0.7, vender: "store1"},
+ {name!: "pen", price:3, discount: 2, vendor: "store2"}
+
+The above query will return
+
+ {name!: "pen", price - discount: 1, upper(vendor): "STORE2"},
+ {name!: "pencil", price - discount: 1.3, upper(vendor): "STORE1"}
+
+
+## Where
+
+ db.gql({select: "*", where: "type='Fire' and name is not null"}, callback)
+
+Where allows filtering of the objects that are passed to the select clause. In this way, unwanted documents
+can be excluded from the query result. The where clause is composed of conditions which are joined by the
+logical operators AND and OR. An additional operator, NOT, provides negation.
+
+Comparison operators can be used in conditions to perform comparisons. The supported comparison operators are
+<=, <, >, >=, !=, and <>. != and <> are equivalent. Null is treated slightly differently; to check if
+something is null IS NULL is used. To check if something is not null, IS NOT NULL is used.
+
+With these documents in the database
+
+ {name: "charmander", type: "Fire"},
+ {type: "Fire", attack:"tail whip"},
+ {name: "charizard", type: "Fire", attack:"slash"}
+
+The above query will return
+
+ {_id: "0D715E2C-CEDD-46B4-A060-9C9C290BEBE8", _rev: "1-71d1e0f8ab00cf432306890a4116602b",
+ attack: "slash", name: "charizard", type: "Fire"},
+ {_id: "3153F94B-0568-4D4C-BFA1-83EDF6185915" _rev: "1-d24f7405c5a63943391eaff9a260139c",
+ name: "charmander", type: "Fire"}
+
+Note the inclusion of the \_rev and \_id fields. This is the result of using 'select \*' instead of naming the
+desired fields explicitly.
+
+## Group By
+
+ db.gql({select: "max(charizard), charmeleon", groupBy: "charmeleon"}, callback)
+
+Group by creates one object for each unique combination of values in the group by clause. For the query above,
+if every document in the database had the value "Level 22" for the property "charmeleon", only a single
+object would be generated.
+
+If a group by clause is present, every identifier in the select clause must either be the argument of an
+aggregation function or present in the group by clause. Otherwise, the composite objects formed by the group by
+clause could have multiple values for some identifiers.
+
+With these documents in the database
+
+ {charizard: 50, charmander: 24, charmeleon: 2, haunter:true},
+ {charizard: 40, charmeleon: 2, charmander: 50},
+ {charizard: 7, charmeleon: 20, charmander: 15}
+
+The above query will return
+
+ {charmeleon: 2, max(charizard): 50}
+ {charmeleon: 20, max(charizard): 7}
+
+## Pivot
+
+ db.gql({select: "max(charizard)", pivot: "charmeleon"}, callback)
+
+Pivot is essentially group by for properties. Each distinct value in the pivot clause gets its own property.
+Unless used with group by, the result will have only a single document.
+
+The same restriction for group by applies here; every identifier in the select clause must either be the
+argument of an aggregation function or preset in the group by clause. Additionally, identifiers in the pivot
+clause may not be used in the group by clause.
+
+Note that using pivot will generate novel property names. See below for an example.
+
+With these documents in the database
+
+ {charizard: 50, charmeleon: "hello"},
+ {charizard: 40, charmeleon: "hello"},
+ {charizard: 7, charmeleon: "world", charmander: 15}
+
+The above query will return
+
+ {'hello max(charizard)': 50, 'world max(charizard)': 7}
+
+## Label
+
+ db.gql({select: 'upper(dept), charizard',
+ label: "upper(dept) 'Department', charizard 'Maximum Charizard!'"}, callback)
+
+Label is used to transform cryptic identifiers into something that can be displayed directly to the end user.
+Items in the label clause can be identifiers, aggregation functions, scalar functions, or operators. The label
+clause is composed of any number of statement label pairs, where the statement corresponds to some statement in
+the select clause and the label is a string literal.
+
+With these documents in the database
+
+ {charizard: 50, dept: "eng", lunch:"2"},
+ {charizard: 40, lunch: "1", dept: "market"},·
+ {charizard: 99, dept: "eng", lunch: 1},
+ {charizard: 7, dept: "eng", lunch: 2}
+
+The above query will return
+
+ {Department: "ENG", Maximum Charizard!: 7},
+ {Department: "ENG", Maximum Charizard!: 99},
+ {Department: "MARKET", Maximum Charizard!: 40},
+ {Department: "ENG", Maximum Charizard!: 50}
+
+## Functions
+
+GQL contains a number of operators and functions that can operate on retrived documents.
+
+### Aggregation Functions
+
+ db.gql({select: "max(charizard), min(charizard), average(charizard), count(charizard), sum(charizard)"},
+ callback)
+
+The currently supported aggregation functions are avg, count, max, min, and sum. Each of these takes a single
+statement as an argument. A statement can be composed of one or more identifiers joined by operators.
+Avg and sum expect their arguments to evaluate to numbers; the other aggregators will accept any type of input.
+Aggregation functions operate on entire identifiers, returning only a single property. Aggregation functions
+may only appear in the select and label clauses.
+
+With these documents in the database
+
+ {charizard: 50},
+ {charizard: 40},
+ {charizard: 7}
+
+The above query will return
+
+ {average(charizard): 32.333333333333336, count(charizard): 3, max(charizard): 50,
+ min(charizard): 7, sum(charizard): 97}
+
+### Scalar Functions
+
+ db.gql({select: "`name!`, price-discount, upper(vendor)"}, callback)
+
+Currently only two scalar functions are supported, upper and lower. These change the characters in their inputs
+to uppercase and lowercase respectively. Unlike aggregator functions, scalar functions take only a single
+identifier as their input. Scalar functions may only appear in the select and label clauses.
+
+With these documents in the database
+
+ {name!: "pencil", price: 2, discount: 0.7, vender: "store1"},
+ {name!: "pen", price:3, discount: 2, vendor: "store2"}
+
+The above query will return
+
+ {name!: "pen", price - discount: 1, upper(vendor): "STORE2"},
+ {name!: "pencil", price - discount: 1.3, upper(vendor): "STORE1"}
+
+
+### Arithmetic Operators
+
+ db.gql({select: "*", where: "charizard <=charmander * charmeleon + 2 and (charmander - 7 != 24/3)"},
+ callback)
+
+Arithmetic operators are used to perform basic math on the values from documents. Their arguments must be
+numbers. Arithmetic operators may only appear in the select, label, and where clauses. The arguments are
+implicitly upcast to floats if necessary. The supported arithmetic operators are:
+
+* Addition: '+'
+* Subtraction: '-'
+* Multiplication: '\*'
+* Division: '/'
+
+
+With these documents in the database
+
+ {charizard: 50, charmander: 24, charmeleon: 2, haunter:true},
+ {charizard: 40, charmeleon: .5, charmander: 50},
+ {charizard: 7, charmeleon: 20, charmander: 15}
+
+The above query will return
+
+ {charizard: 50, charmander: 24, charmeleon: 2, haunter: true}
+
+## Miscellaneous
+
+Some features that are not covered in other sections.
+
+### Literals
+
+Literals are used for comparison or arithmetic. These are the supported literals:
+
+* string: Any characters surrounded by single or double quotes
+* number: Numbers in regular decimal form. They may have a single period, a single negative sign, and no commas
+* boolean: Either true or false
+
+### Identifiers
+
+Identifiers correspond to the properties of documents in the database. There are strict rules governing the
+way that identifiers can be expressed in queries. If your identifier has spaces, is a reserved word, contains
+any characters that are not letters or numbers or underscores, or starts with a digit, it must be surrounded
+by backquotes \(\`identifier\`\).
+
+### Reserved Words
+
+This is the current list of reserved words. Because the GQL implementation is currently under development,
+this list is likely to grow over time.
+
+ and
+ asc
+ by
+ date
+ datetime
+ desc
+ false
+ format
+ group
+ label
+ limit
+ not
+ offset
+ options
+ or
+ order
+ pivot
+ select
+ timeofday
+ timestamp
+ true
+ where
Oops, something went wrong.

0 comments on commit f8195d7

Please sign in to comment.