Commit 4a6e56c2 authored by Tristan Cavelier's avatar Tristan Cavelier

Merge branch 'queries'

parents 7ca3f513 ffc30121
...@@ -11,6 +11,8 @@ COMPLEX_MIN = complex_queries.min.js ...@@ -11,6 +11,8 @@ COMPLEX_MIN = complex_queries.min.js
PARSER_PAR = $(QUERIES_DIR)/parser.par PARSER_PAR = $(QUERIES_DIR)/parser.par
PARSER_OUT = $(QUERIES_DIR)/parser.js PARSER_OUT = $(QUERIES_DIR)/parser.js
## install npm package system wide -> npm -g install <package>
## js/cc using rhino ## js/cc using rhino
#JSCC_CMD = rhino ~/modules/jscc/jscc.js -t ~/modules/jscc/driver_web.js_ #JSCC_CMD = rhino ~/modules/jscc/jscc.js -t ~/modules/jscc/driver_web.js_
# sh -c 'cd ; npm install jscc-node' # sh -c 'cd ; npm install jscc-node'
...@@ -19,6 +21,8 @@ JSCC_CMD = node ~/node_modules/jscc-node/jscc.js -t ~/node_modules/jscc-node/ ...@@ -19,6 +21,8 @@ JSCC_CMD = node ~/node_modules/jscc-node/jscc.js -t ~/node_modules/jscc-node/
LINT_CMD = $(shell which jslint || echo node ~/node_modules/jslint/bin/jslint.js) --terse LINT_CMD = $(shell which jslint || echo node ~/node_modules/jslint/bin/jslint.js) --terse
# sh -c 'cd ; npm install uglify-js' # sh -c 'cd ; npm install uglify-js'
UGLIFY_CMD = $(shell which uglifyjs || echo node ~/node_modules/uglify-js/bin/uglifyjs) UGLIFY_CMD = $(shell which uglifyjs || echo node ~/node_modules/uglify-js/bin/uglifyjs)
# sh -c 'cd ; npm install phantomjs'
PHANTOM_CMD = $(shell which phantomjs || echo ~/node_modules/phantomjs/bin/phantomjs)
auto: compile build lint auto: compile build lint
build: concat uglify build: concat uglify
...@@ -26,7 +30,7 @@ build: concat uglify ...@@ -26,7 +30,7 @@ build: concat uglify
# The order is important! # The order is important!
CONCAT_JIO_NAMES = intro exceptions jio.intro storages/* commands/* jobs/status/* jobs/job announcements/announcement activityUpdater announcements/announcer jobs/jobIdHandler jobs/jobManager jobs/jobRules jio.core jio.outro jioNamespace outro CONCAT_JIO_NAMES = intro exceptions jio.intro storages/* commands/* jobs/status/* jobs/job announcements/announcement activityUpdater announcements/announcer jobs/jobIdHandler jobs/jobManager jobs/jobRules jio.core jio.outro jioNamespace outro
CONCAT_STORAGE_NAMES = * CONCAT_STORAGE_NAMES = *
CONCAT_QUERIES_NAMES = begin parser-begin parser parser-end serializer query end CONCAT_QUERIES_NAMES = begin parser-begin parser parser-end tool queryfactory query simplequery complexquery end
LINT_NAMES = exceptions storages/* commands/* jobs/status/* jobs/* announcements/* activityUpdater jio.core jioNamespace LINT_NAMES = exceptions storages/* commands/* jobs/status/* jobs/* announcements/* activityUpdater jio.core jioNamespace
CONCAT_QUERIES_FILES = $(CONCAT_QUERIES_NAMES:%=$(QUERIES_DIR)/%.js) CONCAT_QUERIES_FILES = $(CONCAT_QUERIES_NAMES:%=$(QUERIES_DIR)/%.js)
...@@ -57,7 +61,7 @@ lint: ...@@ -57,7 +61,7 @@ lint:
$(LINT_CMD) $(LINT_FILES) $(LINT_CMD) $(LINT_FILES)
phantom: phantom:
~/node_modules/phantomjs/bin/phantomjs test/run-qunit.js test/jiotests_withoutrequirejs.html | awk 'BEGIN {print "<!DOCTYPE html><html>"} /^<head>$$/, /^<\/body>$$/ {print} END {print "</html>"}' | sed -e 's,^ *<\(/\|\)script.*>$$,,g' > test/unit_test_result.html $(PHANTOM_CMD) test/run-qunit.js test/jiotests_withoutrequirejs.html | awk 'BEGIN {print "<!DOCTYPE html><html>"} /^<head>$$/, /^<\/body>$$/ {print} END {print "</html>"}' | sed -e 's,^ *<\(/\|\)script.*>$$,,g' > test/unit_test_result.html
grep '^ <title>✔ ' test/unit_test_result.html > /dev/null grep '^ <title>✔ ' test/unit_test_result.html > /dev/null
.phony: clean .phony: clean
......
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
<table> <table>
<tr> <tr>
<td>Query (String):<br /><textarea id="str">1:abc AND 2:def</textarea></td> <td>Query (String):<br /><textarea id="str">1:abc AND 2:def</textarea></td>
<td>Query (Object):<br /><textarea id="obj">{&quot;type&quot;:&quot;complex&quot;,&quot;operator&quot;:&quot;AND&quot;,&quot;query_list&quot;:[{&quot;type&quot;:&quot;simple&quot;,&quot;operator&quot;:&quot;=&quot;,&quot;id&quot;:&quot;1&quot;,&quot;value&quot;:&quot;abc&quot;},{&quot;type&quot;:&quot;simple&quot;,&quot;operator&quot;:&quot;=&quot;,&quot;id&quot;:&quot;2&quot;,&quot;value&quot;:&quot;def&quot;}]}</textarea></td> <td>Query (Object):<br /><textarea id="obj">{&quot;type&quot;:&quot;complex&quot;,&quot;operator&quot;:&quot;AND&quot;,&quot;query_list&quot;:[{&quot;type&quot;:&quot;simple&quot;,&quot;operator&quot;:&quot;=&quot;,&quot;key&quot;:&quot;1&quot;,&quot;value&quot;:&quot;abc&quot;},{&quot;type&quot;:&quot;simple&quot;,&quot;operator&quot;:&quot;=&quot;,&quot;key&quot;:&quot;2&quot;,&quot;value&quot;:&quot;def&quot;}]}</textarea></td>
</tr> </tr>
<tr> <tr>
<td>Object List:<br /><textarea id="list">[{&quot;1&quot;:&quot;abc&quot;,&quot;2&quot;:&quot;def&quot;},{&quot;1&quot;:&quot;def&quot;,&quot;2&quot;:&quot;abc&quot;}]</textarea></td> <td>Item list (to filter, from 'Query (Object)'):<br /><textarea id="list">[{&quot;1&quot;:&quot;abc&quot;,&quot;2&quot;:&quot;def&quot;},{&quot;1&quot;:&quot;def&quot;,&quot;2&quot;:&quot;abc&quot;}]</textarea></td>
<td>Result (Query String):<br /><textarea id="result">[{&quot;1&quot;:&quot;abc&quot;,&quot;2&quot;:&quot;def&quot;}]</textarea></td> <td>Result list:<br /><textarea id="result">[{&quot;1&quot;:&quot;abc&quot;,&quot;2&quot;:&quot;def&quot;}]</textarea></td>
</tr> </tr>
<tr> <tr>
<td><label for="wildcard">Wildcard char: </label></td> <td><label for="wildcard">Wildcard char: </label></td>
...@@ -39,8 +39,8 @@ ...@@ -39,8 +39,8 @@
<td><input type="text" id="limit" name="limit" value="[0,100]" /></td> <td><input type="text" id="limit" name="limit" value="[0,100]" /></td>
</tr> </tr>
</table> </table>
<button onclick="parse()">Parse</button> <button onclick="searchTextToJson()">Search text to JSON</button>
<button onclick="serialize()">Serialize</button> <button onclick="jsonToSearchText()">JSON to Search text</button>
<button onclick="query()">Query</button> <button onclick="query()">Query</button>
<script type="text/javascript" src="../lib/md5/md5.js"></script> <script type="text/javascript" src="../lib/md5/md5.js"></script>
<script type="text/javascript" src="../lib/jsSha2/sha2.js"></script> <script type="text/javascript" src="../lib/jsSha2/sha2.js"></script>
...@@ -51,26 +51,24 @@ ...@@ -51,26 +51,24 @@
src="http://code.jquery.com/jquery-1.8.2.min.js"></script> src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
<!-- <!--
var parse = function () { var searchTextToJson = function () {
$('#obj').attr('value',JSON.stringify(jIO.ComplexQueries.parse($('#str').attr('value')))); $("#obj").attr("value", JSON.stringify(complex_queries.Query.parseStringToObject($("#str").attr("value"))));
}; };
var serialize = function () { var jsonToSearchText = function () {
$('#str').attr('value',jIO.ComplexQueries.serialize(JSON.parse($('#obj').attr('value')))); $("#str").attr("value", complex_queries.QueryFactory.create(JSON.parse($("#obj").attr("value"))).toString());
}; };
var query = function () { var query = function () {
$('#result').attr('value',JSON.stringify( var list = JSON.parse($("#list").attr("value"));
jIO.ComplexQueries.query( $("#str").attr("value", complex_queries.QueryFactory.create(JSON.parse($("#obj").attr("value"))).exec(
list,
{ {
query:$('#str').attr('value'), "wildcard_character": $('#wildcard').attr('value'),
filter:{ "sort_on": JSON.parse($("#sort_on").attr("value")),
sort_on:JSON.parse($('#sort_on').attr('value')), "limit": JSON.parse($("#limit").attr("value")),
limit:JSON.parse($('#limit').attr('value')), "select_list": JSON.parse($("#select_list").attr("value"))
select_list:JSON.parse($('#select_list').attr('value')) }
},
wildcard_character:$('#wildcard').attr('value')
}, JSON.parse($('#list').attr('value'))
)
)); ));
$("#result").attr("value", JSON.stringify(list));
}; };
// --> // -->
</script> </script>
......
...@@ -92,8 +92,6 @@ var clearlog = function () { ...@@ -92,8 +92,6 @@ var clearlog = function () {
</td> </td>
<td colspan="1" style="text-align: center;"> <td colspan="1" style="text-align: center;">
Options:<br /> Options:<br />
<label for="include_docs">Include Docs</label>
<input type="checkbox" id="include_docs" /><br />
<label for="show_conflicts">Get Conflicts</label> <label for="show_conflicts">Get Conflicts</label>
<input type="checkbox" id="show_conflicts" /><br /> <input type="checkbox" id="show_conflicts" /><br />
<label for="show_revision_history">Get Revision History</label> <label for="show_revision_history">Get Revision History</label>
...@@ -101,7 +99,7 @@ var clearlog = function () { ...@@ -101,7 +99,7 @@ var clearlog = function () {
<label for="show_revision_info">Get Revision Info</label> <label for="show_revision_info">Get Revision Info</label>
<input type="checkbox" id="show_revision_info" /><br /> <input type="checkbox" id="show_revision_info" /><br />
<label for="max_retry">Max Retry</label> <label for="max_retry">Max Retry</label>
<input type="number" id="max_retry" value="0" style="width:30px;"/> <input type="number" id="max_retry" value="0" style="width: 3em;"/>
(0 = infinite) (0 = infinite)
</td> </td>
</tr> </tr>
...@@ -111,14 +109,36 @@ var clearlog = function () { ...@@ -111,14 +109,36 @@ var clearlog = function () {
<button onclick="put()">put</button> <button onclick="put()">put</button>
<button onclick="get()">get</button> <button onclick="get()">get</button>
<button onclick="remove()">remove</button> <button onclick="remove()">remove</button>
<button onclick="allDocs()">allDocs</button>
<!-- </td> -->
<!-- <td style="text-align: center;"> -->
- <button onclick="putAttachment()">putAttachment</button> - <button onclick="putAttachment()">putAttachment</button>
<button onclick="getAttachment()">getAttachment</button> <button onclick="getAttachment()">getAttachment</button>
<button onclick="removeAttachment()">removeAttachment</button> <button onclick="removeAttachment()">removeAttachment</button>
</td> </td>
</tr> </tr>
<tr>
<td colspan="1" style="width: 50%;">
<label for="query">AllDocs Query:</label>
<textarea id="query" rows="3" style="width: 98%;">a: 2</textarea>
</td>
<td colspan="1" style="text-align: center;">
AllDocs Options:<br />
<label for="include_docs">Include Docs</label>
<input type="checkbox" id="include_docs" /><br />
<label for="wildcard">Wildcard char: </label>
<input type="text" id="wildcard" name="wildcard" value="%" maxlength="1" style="width: 1em;"/><br />
<label for="sort_on">Sort on: </label>
<input type="text" id="sort_on" name="sort_on" value="[[&quot;author&quot;, &quot;ascending&quot;], [&quot;title&quot;, &quot;descending&quot;]]"
style="width: 80%;"/><br />
<label for="select_list">Select_list: </label>
<input type="text" id="select_list" name="select_list" value="[&quot;author&quot;, &quot;title&quot;]" style="width: 80%;"/><br />
<label for="limit">Limit: </label>
<input type="text" id="limit" name="limit" value="[0, 100]" style="width: 80%;"/><br />
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center;">
<button onclick="allDocs()">allDocs</button>
</td>
</tr>
</table> </table>
<br /> <br />
<div style="text-align: center;"> <div style="text-align: center;">
...@@ -130,6 +150,7 @@ var clearlog = function () { ...@@ -130,6 +150,7 @@ var clearlog = function () {
<div id="log"> <div id="log">
</div> </div>
<script type="text/javascript" src="../lib/md5/md5.js"></script> <script type="text/javascript" src="../lib/md5/md5.js"></script>
<script type="text/javascript" src="../complex_queries.js"></script>
<script type="text/javascript" src="../jio.js"></script> <script type="text/javascript" src="../jio.js"></script>
<script type="text/javascript" src="../src/jio.storage/localstorage.js"> <script type="text/javascript" src="../src/jio.storage/localstorage.js">
</script> </script>
...@@ -223,7 +244,13 @@ var command = function (method) { ...@@ -223,7 +244,13 @@ var command = function (method) {
return error('no jio set'); return error('no jio set');
} }
opts.query = $('#query').attr('value');
opts.include_docs = $('#include_docs').attr('checked') ? true : false; opts.include_docs = $('#include_docs').attr('checked') ? true : false;
opts.wildcard_character = $('#wildcard').attr('value') || "";
opts.sort_on = JSON.parse($('#sort_on').attr('value') || null);
opts.select_list = JSON.parse($('#select_list').attr('value') || null);
opts.limit = JSON.parse($('#limit').attr('value') || null);
opts.conflicts = $('#show_conflicts').attr('checked') ? true : false; opts.conflicts = $('#show_conflicts').attr('checked') ? true : false;
opts.revs = $('#show_revision_history').attr('checked') ? true : false; opts.revs = $('#show_revision_history').attr('checked') ? true : false;
opts.revs_info = $('#show_revision_info').attr('checked') ? true : false; opts.revs_info = $('#show_revision_info').attr('checked') ? true : false;
......
JSCCDIR = /opt/jscc
JSCCCMD = rhino $(JSCCDIR)/jscc.js -t $(JSCCDIR)/driver_web.js_
SRC_DIR = ../../src/queries
BUILD_DIR = ../../built/queries
OUT = $(BUILD_DIR)/complex-queries.js
PARSER_PAR = $(SRC_DIR)/parser.par
PARSER_OUT = $(BUILD_DIR)/parser.js
auto: prepare parser grunt
prepare:
mkdir -p $(BUILD_DIR)
parser:
$(JSCCCMD) $(PARSER_PAR) >> $(PARSER_OUT)
grunt:
grunt
/*global module:false*/
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: '<json:package.json>',
meta: {
banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - '+
'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
'<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
'* Copyright (c) <%= grunt.template.today("yyyy") %> Nexedi;' +
' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */'
},
concat: {
dist: {
src: ['<banner:meta.banner>',
// Wrapper top
'<file_strip_banner:../../src/queries/begin.js>',
// code
'<file_strip_banner:../../src/queries/parser-begin.js>',
'<file_strip_banner:../../built/queries/parser.js>',
'<file_strip_banner:../../src/queries/parser-end.js>',
'<file_strip_banner:../../src/queries/serializer.js>',
'<file_strip_banner:../../src/queries/query.js>',
// Wrapper bottom
'<file_strip_banner:../../src/queries/end.js>'],
dest: '../../<%= pkg.name %>.js'
}
},
min: {
dist: {
src: ['<banner:meta.banner>', '<config:concat.dist.dest>'],
dest: '../../<%= pkg.name %>.min.js'
}
},
qunit: {
files: ['../../test/cq-tests.html']
},
lint: {
files: ['grunt.js',
'../../src/queries/serializer.js',
'../../src/queries/query.js']
},
watch: {
files: '<config:lint.files>',
tasks: 'lint qunit'
},
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
boss: true,
eqnull: true,
browser: true
},
globals: {
scope: true,
console: true,
unescape: true,
// Needed to avoid "not defined error" with requireJs
define: true,
require: true,
// Needed to avoid "not defined error" with sinonJs
sinon: true,
module: true,
test: true,
ok: true,
deepEqual: true,
expect: true,
stop: true,
start: true,
equal: true
}
},
uglify: {}
});
// Default task.
grunt.registerTask('default', 'lint concat min');
};
{
"name": "complex-queries",
"title": "Complex Queries",
"description": "Complex Queries",
"version": "0.1.0",
"homepage": "",
"author": {
"name": "Tristan Cavelier",
"email": "tristan.cavelier@tiolive.com"
},
"repository": {
"type": "git",
"url": "http://git.erp5.org/repos/jio.git"
},
"bugs": {
"url": ""
},
"licenses": [
],
"dependencies": {
},
"keywords": []
}
...@@ -301,9 +301,16 @@ jIO.addStorageType("erp5", function (spec, my) { ...@@ -301,9 +301,16 @@ jIO.addStorageType("erp5", function (spec, my) {
* @param {string} method The ERP5 request method * @param {string} method The ERP5 request method
*/ */
priv.genericCommand = function (command, method) { priv.genericCommand = function (command, method) {
var option = command.cloneOption();
if (complex_queries !== undefined &&
method === 'allDocs' &&
option.query) {
option.query =
complex_queries.QueryFactory.create(option.query || "").serialized();
}
erp5.genericRequest( erp5.genericRequest(
command.cloneDoc(), command.cloneDoc(),
command.cloneOption(), option,
method method
).always(function (err, response) { ).always(function (err, response) {
if (err) { if (err) {
......
This diff is collapsed.
...@@ -3,8 +3,11 @@ ...@@ -3,8 +3,11 @@
* Released under the LGPL license. * Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html * http://www.gnu.org/licenses/lgpl.html
*/ */
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global jIO: true, localStorage: true, setTimeout: true */ /*global jIO: true, localStorage: true, setTimeout: true,
complex_queries: true */
/** /**
* JIO Local Storage. Type = 'local'. * JIO Local Storage. Type = 'local'.
* Local browser "database" storage. * Local browser "database" storage.
...@@ -134,9 +137,11 @@ jIO.addStorageType('local', function (spec, my) { ...@@ -134,9 +137,11 @@ jIO.addStorageType('local', function (spec, my) {
} }
doc = localstorage.getItem(priv.localpath + "/" + doc_id); doc = localstorage.getItem(priv.localpath + "/" + doc_id);
if (doc === null) { if (doc === null) {
doc = command.cloneDoc();
doc._id = doc_id;
// the document does not exist // the document does not exist
localstorage.setItem(priv.localpath + "/" + doc_id, localstorage.setItem(priv.localpath + "/" + doc_id,
command.cloneDoc()); doc);
that.success({ that.success({
"ok": true, "ok": true,
"id": doc_id "id": doc_id
...@@ -359,47 +364,66 @@ jIO.addStorageType('local', function (spec, my) { ...@@ -359,47 +364,66 @@ jIO.addStorageType('local', function (spec, my) {
* @param {object} command The JIO command * @param {object} command The JIO command
*/ */
that.allDocs = function (command) { that.allDocs = function (command) {
var i, j, file, items = 0, var i, row, path_re, rows = [], document_list = [], option, document_object;
s = new RegExp("^" + priv.localpath + "\\/[^/]+$"), path_re = new RegExp(
all_doc_response = {}, "^" + complex_queries.stringEscapeRegexpCharacters(priv.localpath) +
query_object = [], query_syntax, query_response = []; "/[^/]+$"
);
query_syntax = command.getOption('query'); option = command.cloneOption();
if (query_syntax === undefined) { if (typeof complex_queries !== "object" ||
all_doc_response.rows = []; (option.query === undefined && option.sort_on === undefined &&
option.select_list === undefined &&
option.include_docs === undefined)) {
rows = [];
for (i in localStorage) { for (i in localStorage) {
if (localStorage.hasOwnProperty(i)) { if (localStorage.hasOwnProperty(i)) {
// filter non-documents // filter non-documents
if (s.test(i)) { if (path_re.test(i)) {
items += 1; row = { value: {} };
j = i.split('/').slice(-1)[0]; row.id = i.split('/').slice(-1)[0];
row.key = row.id;
file = { value: {} };
file.id = j;
file.key = j;
if (command.getOption('include_docs')) { if (command.getOption('include_docs')) {
file.doc = JSON.parse(localStorage.getItem(i)); row.doc = JSON.parse(localStorage.getItem(i));
} }
all_doc_response.rows.push(file); rows.push(row);
} }
} }
} }
all_doc_response.total_rows = items; that.success({"rows": rows, "total_rows": rows.length});
that.success(all_doc_response);
} else { } else {
// create complex query object from returned results // create complex query object from returned results
for (i in localStorage) { for (i in localStorage) {
if (localStorage.hasOwnProperty(i)) { if (localStorage.hasOwnProperty(i)) {
if (s.test(i)) { if (path_re.test(i)) {
items += 1; document_list.push(localstorage.getItem(i));
j = i.split('/').slice(-1)[0]; }
query_object.push(localstorage.getItem(i)); }
} }
option.select_list = option.select_list || [];
option.select_list.push("_id");
if (option.include_docs === true) {
document_object = {};
document_list.forEach(function (meta) {
document_object[meta._id] = meta;
});
} }
complex_queries.QueryFactory.create(option.query || "").
exec(document_list, option);
document_list = document_list.map(function (value) {
var o = {
"id": value._id,
"key": value._id
};
if (option.include_docs === true) {
o.doc = document_object[value._id];
delete document_object[value._id];
} }
query_response = jIO.ComplexQueries.query(query_syntax, query_object); delete value._id;
that.success(query_response); o.value = value;
return o;
});
that.success({"total_rows": document_list.length,
"rows": document_list});
} }
}; };
......
...@@ -4,11 +4,27 @@ ...@@ -4,11 +4,27 @@
* http://www.gnu.org/licenses/lgpl.html * http://www.gnu.org/licenses/lgpl.html
*/ */
(function (scope) { /**
* Provides some function to use complex queries with item list
*
* @module complex_queries
*/
var complex_queries;
(function () {
"use strict"; "use strict";
Object.defineProperty(scope, "ComplexQueries", { var to_export = {}, module_name = "complex_queries";
configurable: false, /**
enumerable: false, * Add a secured (write permission denied) property to an object.
writable: false, *
value: {} * @param {Object} object The object to fill
* @param {String} key The object key where to store the property
* @param {Any} value The value to store
*/
function _export(key, value) {
Object.defineProperty(to_export, key, {
"configurable": false,
"enumerable": true,
"writable": false,
"value": value
}); });
}
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global newClass: true, Query: true, query_class_dict: true,
_export: true, QueryFactory: true */
/**
* The ComplexQuery inherits from Query, and compares one or several metadata
* values.
*
* @class ComplexQuery
* @extends Query
* @param {Object} [spec={}] The specifications
* @param {String} [spec.operator="AND"] The compare method to use
* @param {String} spec.key The metadata key
* @param {String} spec.value The value of the metadata to compare
*/
var ComplexQuery = newClass(Query, function (spec) {
/**
* Logical operator to use to compare object values
*
* @attribute operator
* @type String
* @default "AND"
* @optional
*/
this.operator = spec.operator || "AND";
/**
* The sub Query list which are used to query an item.
*
* @attribute query_list
* @type Array
* @default []
* @optional
*/
this.query_list = spec.query_list || [];
this.query_list = this.query_list.map(QueryFactory.create);
/**
* #crossLink "Query/match:method"
*/
this.match = function (item, wildcard_character) {
return this[this.operator](item, wildcard_character);
};
/**
* #crossLink "Query/toString:method"
*/
this.toString = function () {
var str_list = ["("], this_operator = this.operator;
this.query_list.forEach(function (query) {
str_list.push(query.toString());
str_list.push(this_operator);
});
str_list.pop(); // remove last operator
str_list.push(")");
return str_list.join(" ");
};
/**
* #crossLink "Query/serialized:method"
*/
this.serialized = function () {
var s = {
"type": "complex",
"operator": this.operator,
"query_list": []
};
this.query_list.forEach(function (query) {
s.query_list.push(query.serialized());
});
return s;
};
/**
* Comparison operator, test if all sub queries match the
* item value
*
* @method AND
* @param {Object} item The item to match
* @param {String} wildcard_character The wildcard character
* @return {Boolean} true if all match, false otherwise
*/
this.AND = function (item, wildcard_character) {
var i;
for (i = 0; i < this.query_list.length; i += 1) {
if (!this.query_list[i].match(item, wildcard_character)) {
return false;
}
}
return true;
};
/**
* Comparison operator, test if one of the sub queries matches the
* item value
*
* @method OR
* @param {Object} item The item to match
* @param {String} wildcard_character The wildcard character
* @return {Boolean} true if one match, false otherwise
*/
this.OR = function (item, wildcard_character) {
var i;
for (i = 0; i < this.query_list.length; i += 1) {
if (this.query_list[i].match(item, wildcard_character)) {
return true;
}
}
return false;
};
/**
* Comparison operator, test if the sub query does not match the
* item value
*
* @method NOT
* @param {Object} item The item to match
* @param {String} wildcard_character The wildcard character
* @return {Boolean} true if one match, false otherwise
*/
this.NOT = function (item, wildcard_character) {
return !this.query_list[0].match(item, wildcard_character);
};
});
query_class_dict.complex = ComplexQuery;
_export("ComplexQuery", ComplexQuery);
}(jIO)); if (typeof define === "function" && define.amd) {
define(to_export);
} else if (typeof window === "object") {
Object.defineProperty(window, module_name, {
configurable: false,
enumerable: true,
writable: false,
value: to_export
});
} else if (typeof exports === "object") {
var i;
for (i in to_export) {
if (to_export.hasOwnProperty(i)) {
exports[i] = to_export[i];
}
}
} else {
complex_queries = to_export;
}
}());
Object.defineProperty(scope.ComplexQueries, "parse", { function parseStringToObject(string) {
configurable: false,
enumerable: false,
writable: false,
value: function (string) {
return result; return result;
} } // parseStringToObject
});
...@@ -37,7 +37,7 @@ boolean_expression ...@@ -37,7 +37,7 @@ boolean_expression
expression expression
: LEFT_PARENTHESE search_text RIGHT_PARENTHESE [* %% = %2; *] : LEFT_PARENTHESE search_text RIGHT_PARENTHESE [* %% = %2; *]
| COLUMN expression [* simpleQuerySetId(%2,%1.split(':').slice(0,-1).join(':')); %% = %2; *] | COLUMN expression [* simpleQuerySetKey(%2,%1.split(':').slice(0,-1).join(':')); %% = %2; *]
| value [* %% = %1; *] | value [* %% = %1; *]
; ;
...@@ -53,59 +53,60 @@ string ...@@ -53,59 +53,60 @@ string
[* [*
var arrayExtend = function () { var arrayExtend = function () {
var j,i,newlist=[],listoflists = arguments; var j, i, newlist = [], list_list = arguments;
for (j=0; j<listoflists.length; ++j) { for (j = 0; j < list_list.length; j += 1) {
for (i=0; i<listoflists[j].length; ++i) { for (i = 0; i < list_list[j].length; i += 1) {
newlist.push(listoflists[j][i]); newlist.push(list_list[j][i]);
} }
} }
return newlist; return newlist;
};
var mkSimpleQuery = function (id,value,operator) { }, mkSimpleQuery = function (key, value, operator) {
return {type:'simple',operator:'=',id:id,value:value}; return {"type": "simple", "operator": "=", "key": key, "value": value};
};
var mkNotQuery = function (query) { }, mkNotQuery = function (query) {
if (query.operator === 'NOT') { if (query.operator === "NOT") {
return query.query_list[0]; return query.query_list[0];
} }
return {type:'complex',operator:'NOT',query_list:[query]}; return {"type": "complex", "operator": "NOT", "query_list": [query]};
};
var mkComplexQuery = function (operator,query_list) { }, mkComplexQuery = function (operator, query_list) {
var i,query_list2 = []; var i, query_list2 = [];
for (i=0; i<query_list.length; ++i) { for (i = 0; i < query_list.length; i += 1) {
if (query_list[i].operator === operator) { if (query_list[i].operator === operator) {
query_list2 = arrayExtend(query_list2,query_list[i].query_list); query_list2 = arrayExtend(query_list2, query_list[i].query_list);
} else { } else {
query_list2.push(query_list[i]); query_list2.push(query_list[i]);
} }
} }
return {type:'complex',operator:operator,query_list:query_list2}; return {type:"complex",operator:operator,query_list:query_list2};
};
var simpleQuerySetId = function (query, id) { }, simpleQuerySetKey = function (query, key) {
var i; var i;
if (query.type === 'complex') { if (query.type === "complex") {
for (i = 0; i < query.query_list.length; ++i) { for (i = 0; i < query.query_list.length; ++i) {
simpleQuerySetId (query.query_list[i],id); simpleQuerySetKey (query.query_list[i],key);
} }
return true; return true;
} }
if (query.type === 'simple' && !query.id) { if (query.type === "simple" && !query.key) {
query.id = id; query.key = key;
return true; return true;
} }
return false; return false;
}; },
var error_offsets = []; error_offsets = [],
var error_lookaheads = []; error_lookaheads = [],
var error_count = 0; error_count = 0,
var result; result;
if ( ( error_count = __##PREFIX##parse( string, error_offsets, error_lookaheads ) ) > 0 ) {
if ((error_count = __##PREFIX##parse(string, error_offsets, error_lookaheads)) > 0) {
var i; var i;
for (i = 0; i < error_count; ++i) { for (i = 0; i < error_count; i += 1) {
throw new Error ( "Parse error near \"" + throw new Error("Parse error near \"" +
string.substr ( error_offsets[i] ) + string.substr(error_offsets[i]) +
"\", expecting \"" + "\", expecting \"" +
error_lookaheads[i].join() + "\"" ); error_lookaheads[i].join() + "\"");
} }
} }
*] *]
This diff is collapsed.
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global _export: true, ComplexQuery: true, SimpleQuery: true,
newClass: true, Query: true */
var query_class_dict = {}, QueryFactory;
/**
* Provides static methods to create Query object
*
* @class QueryFactory
*/
QueryFactory = newClass({
"static_methods": {
/**
* Creates Query object from a search text string or a serialized version
* of a Query.
*
* @method create
* @static
* @param {Object,String} object The search text or the serialized version
* of a Query
* @return {Query} A Query object
*/
"create": function (object) {
if (object === "") {
return new Query();
}
if (typeof object === "string") {
object = Query.parseStringToObject(object);
}
if (typeof (object || {}).type === "string" &&
query_class_dict[object.type]) {
return new query_class_dict[object.type](object);
}
return null;
}
}
}, function () {});
_export("QueryFactory", QueryFactory);
Object.defineProperty(scope.ComplexQueries,"serialize",{ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
configurable:false,enumerable:false,writable:false,value:function(query){ /*global _export: true, to_export: true */
var str_list = [], i;
if (query.type === 'complex') { function objectToSearchText(query) {
str_list.push ( '(' ); var str_list = [];
for (i=0; i<query.query_list.length; ++i) { if (query.type === "complex") {
str_list.push( scope.ComplexQueries.serialize(query.query_list[i]) ); str_list.push("(");
str_list.push( query.operator ); (query.query_list || []).forEach(function (sub_query) {
str_list.push(objectToSearchText(sub_query));
str_list.push(query.operator);
});
str_list.length -= 1;
str_list.push(")");
return str_list.join(" ");
} }
str_list.length --; if (query.type === "simple") {
str_list.push ( ')' ); return query.id + (query.id ? ": " : "") + (query.operator || "=") + ' "' +
return str_list.join(' '); query.value + '"';
} else if (query.type === 'simple') {
return query.id + (query.id?': ':'') + query.operator + ' "' + query.value + '"';
} }
return query; throw new TypeError("This object is not a query");
} }
}); _export("objectToSearchText", objectToSearchText);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global newClass: true, Query: true,
query_class_dict: true, _export: true */
/**
* The SimpleQuery inherits from Query, and compares one metadata value
*
* @class SimpleQuery
* @extends Query
* @param {Object} [spec={}] The specifications
* @param {String} [spec.operator="="] The compare method to use
* @param {String} spec.key The metadata key
* @param {String} spec.value The value of the metadata to compare
*/
var SimpleQuery = newClass(Query, function (spec) {
/**
* Operator to use to compare object values
*
* @attribute operator
* @type String
* @default "="
* @optional
*/
this.operator = spec.operator || "=";
/**
* Key of the object which refers to the value to compare
*
* @attribute key
* @type String
*/
this.key = spec.key;
/**
* Value is used to do the comparison with the object value
*
* @attribute value
* @type String
*/
this.value = spec.value;
/**
* #crossLink "Query/match:method"
*/
this.match = function (item, wildcard_character) {
return this[this.operator](item[this.key], this.value, wildcard_character);
};
/**
* #crossLink "Query/toString:method"
*/
this.toString = function () {
return (this.key ? this.key + ": " : "") + (this.operator || "=") + ' "' +
this.value + '"';
};
/**
* #crossLink "Query/serialized:method"
*/
this.serialized = function () {
return {
"type": "simple",
"operator": this.operator,
"key": this.key,
"value": this.value
};
};
/**
* Comparison operator, test if this query value matches the item value
*
* @method =
* @param {String} object_value The value to compare
* @param {String} comparison_value The comparison value
* @param {String} wildcard_character The wildcard_character
* @return {Boolean} true if match, false otherwise
*/
this["="] = function (object_value, comparison_value,
wildcard_character) {
return Query.convertStringToRegExp(
comparison_value.toString(),
wildcard_character || "%"
).test(object_value.toString());
};
/**
* Comparison operator, test if this query value does not match the item value
*
* @method !=
* @param {String} object_value The value to compare
* @param {String} comparison_value The comparison value
* @param {String} wildcard_character The wildcard_character
* @return {Boolean} true if not match, false otherwise
*/
this["!="] = function (object_value, comparison_value,
wildcard_character) {
return !Query.convertStringTextToRegExp(
comparison_value.toString(),
wildcard_character || "%"
).test(object_value.toString());
};
/**
* Comparison operator, test if this query value is lower than the item value
*
* @method <
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if lower, false otherwise
*/
this["<"] = function (object_value, comparison_value) {
return object_value < comparison_value;
};
/**
* Comparison operator, test if this query value is equal or lower than the
* item value
*
* @method <=
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if equal or lower, false otherwise
*/
this["<="] = function (object_value, comparison_value) {
return object_value <= comparison_value;
};
/**
* Comparison operator, test if this query value is greater than the item
* value
*
* @method >
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if greater, false otherwise
*/
this[">"] = function (object_value, comparison_value) {
return object_value > comparison_value;
};
/**
* Comparison operator, test if this query value is equal or greater than the
* item value
*
* @method >=
* @param {Number, String} object_value The value to compare
* @param {Number, String} comparison_value The comparison value
* @return {Boolean} true if equal or greater, false otherwise
*/
this[">="] = function (object_value, comparison_value) {
return object_value >= comparison_value;
};
});
query_class_dict.simple = SimpleQuery;
_export("SimpleQuery", SimpleQuery);
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global _export: true */
/**
* Create a class, manage inheritance, static methods,
* protected attributes and can hide methods or/and secure methods
*
* @param {Class} Class Classes to inherit from (0..n). The last class
* parameter will inherit from the previous one, and so on
* @param {Object} option Class option (0..n)
* @param {Boolean} [option.secure_methods=false] Make methods not configurable
* and not writable
* @param {Boolean} [option.hide_methods=false] Make methods not enumerable
* @param {Boolean} [option.secure_static_methods=true] Make static methods not
* configurable and not
* writable
* @param {Boolean} [option.hide_static_methods=false] Make static methods not
* enumerable
* @param {Object} [option.static_methods={}] Object of static methods
* @param {Function} constructor The new class constructor
* @return {Class} The new class
*/
function newClass() {
var j, k, constructors = [], option, new_class;
for (j = 0; j < arguments.length; j += 1) {
if (typeof arguments[j] === "function") {
constructors.push(arguments[j]);
} else if (typeof arguments[j] === "object") {
option = option || {};
for (k in arguments[j]) {
if (arguments[j].hasOwnProperty(k)) {
option[k] = arguments[j][k];
}
}
}
}
function postObjectCreation(that) {
// modify the object according to 'option'
var key;
if (option) {
for (key in that) {
if (that.hasOwnProperty(key)) {
if (typeof that[key] === "function") {
Object.defineProperty(that, key, {
"configurable": option.secure_methods ? false : true,
"enumerable": option.hide_methods ? false : true,
"writable": option.secure_methods ? false : true,
"value": that[key]
});
}
}
}
}
}
function postClassCreation(that) {
// modify the object according to 'option'
var key;
if (option) {
for (key in that) {
if (that.hasOwnProperty(key)) {
if (typeof that[key] === "function") {
Object.defineProperty(that, key, {
"configurable": option.secure_static_methods ===
false ? true : false,
"enumerable": option.hide_static_methods ? false : true,
"writable": option.secure_static_methods === false ? true : false,
"value": that[key]
});
}
}
}
}
}
new_class = function (spec, my) {
var i;
spec = spec || {};
my = my || {};
// don't use forEach !
for (i = 0; i < constructors.length; i += 1) {
constructors[i].apply(this, [spec, my]);
}
postObjectCreation(this);
return this;
};
option = option || {};
option.static_methods = option.static_methods || {};
for (j in option.static_methods) {
if (option.static_methods.hasOwnProperty(j)) {
new_class[j] = option.static_methods[j];
}
}
postClassCreation(new_class);
return new_class;
}
/**
* Escapes regexp special chars from a string.
*
* @param {String} string The string to escape
* @return {String} The escaped string
*/
function stringEscapeRegexpCharacters(string) {
if (typeof string === "string") {
return string.replace(/([\\\.\$\[\]\(\)\{\}\^\?\*\+\-])/g, "\\$1");
}
}
_export("stringEscapeRegexpCharacters", stringEscapeRegexpCharacters);
/**
* A sort function to sort items by key
*
* @param {String} key The key to sort on
* @param {String} [way="ascending"] 'ascending' or 'descending'
* @return {Function} The sort function
*/
function sortFunction(key, way) {
if (way === 'descending') {
return function (a, b) {
return a[key] < b[key] ? 1 : a[key] > b[key] ? -1 : 0;
};
}
return function (a, b) {
return a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : 0;
};
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment