summaryrefslogtreecommitdiff
path: root/share/server/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'share/server/main.js')
-rw-r--r--share/server/main.js165
1 files changed, 165 insertions, 0 deletions
diff --git a/share/server/main.js b/share/server/main.js
new file mode 100644
index 00000000..91e13742
--- /dev/null
+++ b/share/server/main.js
@@ -0,0 +1,165 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+var cmd;
+var map_funs = []; // The map functions to compute against documents
+var map_results = [];
+
+try {
+ var sandbox = evalcx('');
+ sandbox.map = function(key, value) {
+ map_results.push([key, value]);
+ }
+} catch (e) {
+ // fallback for older versions of spidermonkey that don't have evalcx
+ var sandbox = null;
+ map = function(key, value) {
+ map_results.push([key, value]);
+ }
+}
+
+// Commands are in the form of json arrays:
+// ["commandname",..optional args...]\n
+//
+// Responses are json values followed by a new line ("\n")
+
+while (cmd = eval(readline())) {
+ switch (cmd[0]) {
+ case "reset":
+ // clear the map_functions and run gc
+ map_funs = [];
+ gc();
+ print("true"); // indicates success
+ break;
+ case "add_fun":
+ // The second arg is a string that will compile to a function.
+ // and then we add it to map_functions array
+ try {
+ var functionObject = sandbox ? evalcx(cmd[1], sandbox) : eval(cmd[1]);
+ } catch (err) {
+ print(toJSON({error: {id: "map_compilation_error",
+ reason: err.toString() + " (" + toJSON(cmd[1]) + ")"}}));
+ break;
+ }
+ if (typeof(functionObject) == "function") {
+ map_funs.push(functionObject);
+ print("true");
+ } else {
+ print(toJSON({error: "map_compilation_error",
+ reason: "expression does not eval to a function. (" + cmd[1] + ")"}));
+ }
+ break;
+ case "map_doc":
+ // The second arg is a document. We compute all the map functions against
+ // it.
+ //
+ // Each function can output multiple keys value, pairs for each document
+ //
+ // Example output of map_doc after three functions set by add_fun cmds:
+ // [
+ // [["Key","Value"]], <- fun 1 returned 1 key value
+ // [], <- fun 2 returned 0 key values
+ // [["Key1","Value1"],["Key2","Value2"]],<- fun 3 returned 2 key values
+ // ]
+ //
+ var doc = cmd[1];
+ seal(doc); // seal to prevent map functions from changing doc
+ var buf = [];
+ for (var i = 0; i < map_funs.length; i++) {
+ map_results = [];
+ try {
+ map_funs[i](doc);
+ buf.push(map_results.filter(function(pair) {
+ return pair[0] !== undefined && pair[1] !== undefined;
+ }));
+ } catch (err) {
+ if (err == "fatal_error") {
+ // Only if it's a "fatal_error" do we exit. What's a fatal error?
+ // That's for the query to decide.
+ //
+ // This will make it possible for queries to completely error out,
+ // by catching their own local exception and rethrowing a
+ // fatal_error. But by default if they don't do error handling we
+ // just eat the exception and carry on.
+ print(toJSON({error: "map_runtime_error",
+ reason: "function raised fatal exception"}));
+ quit();
+ }
+ print(toJSON({log: "function raised exception (" + err + ")"}));
+ buf.push([]);
+ }
+ }
+ print(toJSON(buf));
+ break;
+ default:
+ print(toJSON({error: "query_server_error",
+ reason: "unknown command '" + cmd[0] + "'"}));
+ quit();
+ }
+}
+
+function toJSON(val) {
+ if (typeof(val) == "undefined") {
+ throw new TypeError("Cannot encode undefined value as JSON");
+ }
+ var subs = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f',
+ '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
+ if (typeof(val) == "xml") { // E4X support
+ val = val.toXMLString();
+ }
+ return {
+ "Array": function(v) {
+ var buf = [];
+ for (var i = 0; i < v.length; i++) {
+ buf.push(toJSON(v[i]));
+ }
+ return "[" + buf.join(",") + "]";
+ },
+ "Boolean": function(v) {
+ return v.toString();
+ },
+ "Date": function(v) {
+ var f = function(n) { return n < 10 ? '0' + n : n }
+ return '"' + v.getUTCFullYear() + '-' +
+ f(v.getUTCMonth() + 1) + '-' +
+ f(v.getUTCDate()) + 'T' +
+ f(v.getUTCHours()) + ':' +
+ f(v.getUTCMinutes()) + ':' +
+ f(v.getUTCSeconds()) + 'Z"';
+ },
+ "Number": function(v) {
+ return isFinite(v) ? v.toString() : "null";
+ },
+ "Object": function(v) {
+ if (v === null) return "null";
+ var buf = [];
+ for (var k in v) {
+ if (!v.hasOwnProperty(k) || typeof(k) !== "string" || v[k] === undefined) {
+ continue;
+ }
+ buf.push(toJSON(k, val) + ": " + toJSON(v[k]));
+ }
+ return "{" + buf.join(",") + "}";
+ },
+ "String": function(v) {
+ if (/["\\\x00-\x1f]/.test(v)) {
+ v = v.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+ var c = subs[b];
+ if (c) return c;
+ c = b.charCodeAt();
+ return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
+ });
+ }
+ return '"' + v + '"';
+ }
+ }[val != null ? val.constructor.name : "Object"](val);
+}