mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
commit
9f2a028756
28 changed files with 1336 additions and 330 deletions
13
src/bower_components/jsonpath/.bower.json
vendored
Normal file
13
src/bower_components/jsonpath/.bower.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "jsonpath",
|
||||
"homepage": "https://github.com/s3u/JSONPath",
|
||||
"_release": "308ef21860",
|
||||
"_resolution": {
|
||||
"type": "branch",
|
||||
"branch": "master",
|
||||
"commit": "308ef21860ae21584c0f808d67cf63006101149a"
|
||||
},
|
||||
"_source": "git://github.com/s3u/JSONPath.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "jsonpath"
|
||||
}
|
9
src/bower_components/jsonpath/.gitignore
vendored
Normal file
9
src/bower_components/jsonpath/.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
*.iml
|
||||
.DS_Store
|
||||
.idea
|
||||
temp
|
||||
node_modules
|
||||
pids
|
||||
reports
|
||||
target
|
||||
*.log
|
2
src/bower_components/jsonpath/.npmignore
vendored
Normal file
2
src/bower_components/jsonpath/.npmignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
test
|
4
src/bower_components/jsonpath/.travis.yml
vendored
Normal file
4
src/bower_components/jsonpath/.travis.yml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 0.6
|
||||
- 0.8
|
14
src/bower_components/jsonpath/CHANGES.md
vendored
Normal file
14
src/bower_components/jsonpath/CHANGES.md
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
## Oct 23, 2013
|
||||
|
||||
* Support for parent selection via `^`
|
||||
* Access current path via `@path` in test statements
|
||||
* Allowing for multi-statement evals
|
||||
* Performance improvements
|
||||
* Version 0.10
|
||||
|
||||
## Mar 28, 2012
|
||||
|
||||
* Support a sandbox arg to eval
|
||||
* Use vm.runInNewContext in place of eval
|
||||
* Version 0.9.0
|
111
src/bower_components/jsonpath/README.md
vendored
Normal file
111
src/bower_components/jsonpath/README.md
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
JSONPath [](http://travis-ci.org/s3u/JSONPath)
|
||||
========
|
||||
|
||||
Analyse, transform, and selectively extract data from JSON documents (and JavaScript objects).
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
npm install JSONPath
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
In node.js:
|
||||
|
||||
```js
|
||||
var jsonPath = require('JSONPath');
|
||||
jsonPath.eval(obj, path);
|
||||
```
|
||||
|
||||
For browser usage you can directly include `lib/jsonpath.js`, no browserify
|
||||
magic necessary:
|
||||
|
||||
```html
|
||||
<script type="text/javascript" src="lib/jsonpath.js"></script>
|
||||
<script type="text/javascript">
|
||||
jsonPath.eval(obj, path);
|
||||
</script>
|
||||
```
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Given the following JSON, taken from http://goessner.net/articles/JsonPath/ :
|
||||
|
||||
```json
|
||||
{
|
||||
"store": {
|
||||
"book": [
|
||||
{
|
||||
"category": "reference",
|
||||
"author": "Nigel Rees",
|
||||
"title": "Sayings of the Century",
|
||||
"price": 8.95
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "Evelyn Waugh",
|
||||
"title": "Sword of Honour",
|
||||
"price": 12.99
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "Herman Melville",
|
||||
"title": "Moby Dick",
|
||||
"isbn": "0-553-21311-3",
|
||||
"price": 8.99
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "J. R. R. Tolkien",
|
||||
"title": "The Lord of the Rings",
|
||||
"isbn": "0-395-19395-8",
|
||||
"price": 22.99
|
||||
}
|
||||
],
|
||||
"bicycle": {
|
||||
"color": "red",
|
||||
"price": 19.95
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
XPath | JSONPath | Result
|
||||
------------------- | ---------------------- | -------------------------------------
|
||||
/store/book/author | $.store.book[*].author | the authors of all books in the store
|
||||
//author | $..author | all authors
|
||||
/store/* | $.store.* | all things in store, which are some books and a red bicycle.
|
||||
/store//price | $.store..price | the price of everything in the store.
|
||||
//book[3] | $..book[2] | the third book
|
||||
//book[last()] | $..book[(@.length-1)] | the last book in order.
|
||||
| $..book[-1:] |
|
||||
//book[position()<3]| $..book[0,1] | the first two books
|
||||
| $..book[:2] |
|
||||
//book[isbn] | $..book[?(@.isbn)] | filter all books with isbn number
|
||||
//book[price<10] | $..book[?(@.price<10)] | filter all books cheapier than 10
|
||||
//*[price>19]/.. | $..[?(@.price>19)]^ | categories with things more expensive than 19
|
||||
//* | $..* | all Elements in XML document. All members of JSON structure.
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
Running the tests on node: `npm test`. For in-browser tests:
|
||||
|
||||
* Ensure that nodeunit is browser-compiled: `cd node_modules/nodeunit; make browser;`
|
||||
* Serve the js/html files:
|
||||
|
||||
```sh
|
||||
node -e "require('http').createServer(function(req,res) { \
|
||||
var s = require('fs').createReadStream('.' + req.url); \
|
||||
s.pipe(res); s.on('error', function() {}); }).listen(8082);"
|
||||
```
|
||||
* To run the tests visit [http://localhost:8082/test/test.html]().
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
[MIT License](http://www.opensource.org/licenses/mit-license.php).
|
172
src/bower_components/jsonpath/lib/jsonpath.js
vendored
Normal file
172
src/bower_components/jsonpath/lib/jsonpath.js
vendored
Normal file
|
@ -0,0 +1,172 @@
|
|||
/* JSONPath 0.8.0 - XPath for JSON
|
||||
*
|
||||
* Copyright (c) 2007 Stefan Goessner (goessner.net)
|
||||
* Licensed under the MIT (MIT-LICENSE.txt) licence.
|
||||
*/
|
||||
|
||||
var isNode = false;
|
||||
(function(exports, require) {
|
||||
|
||||
// Keep compatibility with old browsers
|
||||
if (!Array.isArray) {
|
||||
Array.isArray = function(vArg) {
|
||||
return Object.prototype.toString.call(vArg) === "[object Array]";
|
||||
};
|
||||
}
|
||||
|
||||
// Make sure to know if we are in real node or not (the `require` variable
|
||||
// could actually be require.js, for example.
|
||||
var isNode = typeof module !== 'undefined' && !!module.exports;
|
||||
|
||||
var vm = isNode ?
|
||||
require('vm') : {
|
||||
runInNewContext: function(expr, context) { with (context) return eval(expr); }
|
||||
};
|
||||
exports.eval = jsonPath;
|
||||
|
||||
var cache = {};
|
||||
|
||||
function push(arr, elem) { arr = arr.slice(); arr.push(elem); return arr; }
|
||||
function unshift(elem, arr) { arr = arr.slice(); arr.unshift(elem); return arr; }
|
||||
|
||||
function jsonPath(obj, expr, arg) {
|
||||
var P = {
|
||||
resultType: arg && arg.resultType || "VALUE",
|
||||
flatten: arg && arg.flatten || false,
|
||||
wrap: (arg && arg.hasOwnProperty('wrap')) ? arg.wrap : true,
|
||||
sandbox: (arg && arg.sandbox) ? arg.sandbox : {},
|
||||
normalize: function(expr) {
|
||||
if (cache[expr]) return cache[expr];
|
||||
var subx = [];
|
||||
var normalized = expr.replace(/[\['](\??\(.*?\))[\]']/g, function($0,$1){return "[#"+(subx.push($1)-1)+"]";})
|
||||
.replace(/'?\.'?|\['?/g, ";")
|
||||
.replace(/(;)?(\^+)(;)?/g, function(_, front, ups, back) { return ';' + ups.split('').join(';') + ';'; })
|
||||
.replace(/;;;|;;/g, ";..;")
|
||||
.replace(/;$|'?\]|'$/g, "");
|
||||
var exprList = normalized.split(';').map(function(expr) {
|
||||
var match = expr.match(/#([0-9]+)/);
|
||||
return !match || !match[1] ? expr : subx[match[1]];
|
||||
})
|
||||
return cache[expr] = exprList;
|
||||
},
|
||||
asPath: function(path) {
|
||||
var x = path, p = "$";
|
||||
for (var i=1,n=x.length; i<n; i++)
|
||||
p += /^[0-9*]+$/.test(x[i]) ? ("["+x[i]+"]") : ("['"+x[i]+"']");
|
||||
return p;
|
||||
},
|
||||
trace: function(expr, val, path) {
|
||||
// no expr to follow? return path and value as the result of this trace branch
|
||||
if (!expr.length) return [{path: path, value: val}];
|
||||
|
||||
var loc = expr[0], x = expr.slice(1);
|
||||
// the parent sel computation is handled in the frame above using the
|
||||
// ancestor object of val
|
||||
if (loc === '^') return path.length ? [{path: path.slice(0,-1), expr: x, isParentSelector: true}] : [];
|
||||
|
||||
// we need to gather the return value of recursive trace calls in order to
|
||||
// do the parent sel computation.
|
||||
var ret = [];
|
||||
function addRet(elems) { ret = ret.concat(elems); }
|
||||
|
||||
if (val && val.hasOwnProperty(loc)) // simple case, directly follow property
|
||||
addRet(P.trace(x, val[loc], push(path, loc)));
|
||||
else if (loc === "*") { // any property
|
||||
P.walk(loc, x, val, path, function(m,l,x,v,p) {
|
||||
addRet(P.trace(unshift(m, x), v, p)); });
|
||||
}
|
||||
else if (loc === "..") { // all chid properties
|
||||
addRet(P.trace(x, val, path));
|
||||
P.walk(loc, x, val, path, function(m,l,x,v,p) {
|
||||
if (typeof v[m] === "object")
|
||||
addRet(P.trace(unshift("..", x), v[m], push(p, m)));
|
||||
});
|
||||
}
|
||||
else if (loc[0] === '(') { // [(expr)]
|
||||
addRet(P.trace(unshift(P.eval(loc, val, path[path.length], path),x), val, path));
|
||||
}
|
||||
else if (loc.indexOf('?(') === 0) { // [?(expr)]
|
||||
P.walk(loc, x, val, path, function(m,l,x,v,p) {
|
||||
if (P.eval(l.replace(/^\?\((.*?)\)$/,"$1"),v[m],m, path))
|
||||
addRet(P.trace(unshift(m,x),v,p));
|
||||
});
|
||||
}
|
||||
else if (loc.indexOf(',') > -1) { // [name1,name2,...]
|
||||
for (var parts = loc.split(','), i = 0; i < parts.length; i++)
|
||||
addRet(P.trace(unshift(parts[i], x), val, path));
|
||||
}
|
||||
else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) { // [start:end:step] python slice syntax
|
||||
addRet(P.slice(loc, x, val, path));
|
||||
}
|
||||
|
||||
// we check the resulting values for parent selections. for parent
|
||||
// selections we discard the value object and continue the trace with the
|
||||
// current val object
|
||||
return ret.reduce(function(all, ea) {
|
||||
return all.concat(ea.isParentSelector ? P.trace(ea.expr, val, ea.path) : [ea]);
|
||||
}, []);
|
||||
},
|
||||
walk: function(loc, expr, val, path, f) {
|
||||
if (Array.isArray(val))
|
||||
for (var i = 0, n = val.length; i < n; i++)
|
||||
f(i, loc, expr, val, path);
|
||||
else if (typeof val === "object")
|
||||
for (var m in val)
|
||||
if (val.hasOwnProperty(m))
|
||||
f(m, loc, expr, val, path);
|
||||
},
|
||||
slice: function(loc, expr, val, path) {
|
||||
if (!Array.isArray(val)) return;
|
||||
var len = val.length, parts = loc.split(':'),
|
||||
start = (parts[0] && parseInt(parts[0])) || 0,
|
||||
end = (parts[1] && parseInt(parts[1])) || len,
|
||||
step = (parts[2] && parseInt(parts[2])) || 1;
|
||||
start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start);
|
||||
end = (end < 0) ? Math.max(0,end+len) : Math.min(len,end);
|
||||
var ret = [];
|
||||
for (var i = start; i < end; i += step)
|
||||
ret = ret.concat(P.trace(unshift(i,expr), val, path));
|
||||
return ret;
|
||||
},
|
||||
eval: function(code, _v, _vname, path) {
|
||||
if (!$ || !_v) return false;
|
||||
if (code.indexOf("@path") > -1) {
|
||||
P.sandbox["_path"] = P.asPath(path.concat([_vname]));
|
||||
code = code.replace(/@path/g, "_path");
|
||||
}
|
||||
if (code.indexOf("@") > -1) {
|
||||
P.sandbox["_v"] = _v;
|
||||
code = code.replace(/@/g, "_v");
|
||||
}
|
||||
try {
|
||||
return vm.runInNewContext(code, P.sandbox);
|
||||
}
|
||||
catch(e) {
|
||||
console.log(e);
|
||||
throw new Error("jsonPath: " + e.message + ": " + code);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var $ = obj;
|
||||
var resultType = P.resultType.toLowerCase();
|
||||
if (expr && obj && (resultType == "value" || resultType == "path")) {
|
||||
var exprList = P.normalize(expr);
|
||||
if (exprList[0] === "$" && exprList.length > 1) exprList.shift();
|
||||
var result = P.trace(exprList, obj, ["$"]);
|
||||
result = result.filter(function(ea) { return ea && !ea.isParentSelector; });
|
||||
if (!result.length) return P.wrap ? [] : false;
|
||||
if (result.length === 1 && !P.wrap && !Array.isArray(result[0].value)) return result[0][resultType] || false;
|
||||
return result.reduce(function(result, ea) {
|
||||
var valOrPath = ea[resultType];
|
||||
if (resultType === 'path') valOrPath = P.asPath(valOrPath);
|
||||
if (P.flatten && Array.isArray(valOrPath)) {
|
||||
result = result.concat(valOrPath);
|
||||
} else {
|
||||
result.push(valOrPath);
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
})(typeof exports === 'undefined' ? this['jsonPath'] = {} : exports, typeof require == "undefined" ? null : require);
|
36
src/bower_components/jsonpath/package.json
vendored
Normal file
36
src/bower_components/jsonpath/package.json
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"author": "Stefan Goessner",
|
||||
"name": "JSONPath",
|
||||
"description": "A JS implementation of JSONPath",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Prof. Gössner",
|
||||
"email": "stefan.goessner@fh-dortmund.de"
|
||||
},
|
||||
{
|
||||
"name": "Subbu Allamaraju",
|
||||
"email": "subbu@subbu.org"
|
||||
},
|
||||
{
|
||||
"name": "Mike Brevoort",
|
||||
"email": "mike@brevoort.com"
|
||||
},
|
||||
{
|
||||
"name": "Robert Krahn",
|
||||
"email": "robert.krahn@gmail.com"
|
||||
}
|
||||
],
|
||||
"version": "0.10.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/s3u/JSONPath.git"
|
||||
},
|
||||
"main": "./lib/jsonpath",
|
||||
"dependencies": {
|
||||
"underscore": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodeunit": "*"
|
||||
},
|
||||
"scripts": {"test": "node_modules/nodeunit/bin/nodeunit test/test.*.js"}
|
||||
}
|
35
src/bower_components/jsonpath/test/test.arr.js
vendored
Normal file
35
src/bower_components/jsonpath/test/test.arr.js
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
var jsonpath = require("../").eval,
|
||||
testCase = require('nodeunit').testCase
|
||||
|
||||
var json = {
|
||||
"store": {
|
||||
"book": { "category": "reference",
|
||||
"author": "Nigel Rees",
|
||||
"title": "Sayings of the Century",
|
||||
"price": [8.95, 8.94, 8.93]
|
||||
},
|
||||
"books": [
|
||||
{ "category": "reference",
|
||||
"author": "Nigel Rees",
|
||||
"title": "Sayings of the Century",
|
||||
"price": [8.95, 8.94, 8.93]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = testCase({
|
||||
"get single": function (test) {
|
||||
var expected = json.store.book;
|
||||
var result = jsonpath(json, "store.book", {flatten: true, wrap: false});
|
||||
test.deepEqual(expected, result);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"get arr": function (test) {
|
||||
var expected = json.store.books;
|
||||
var result = jsonpath(json, "store.books", {flatten: true, wrap: false});
|
||||
test.deepEqual(expected, result);
|
||||
test.done();
|
||||
}
|
||||
});
|
54
src/bower_components/jsonpath/test/test.at_and_dollar.js
vendored
Normal file
54
src/bower_components/jsonpath/test/test.at_and_dollar.js
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
var jsonpath = require("../").eval
|
||||
, testCase = require('nodeunit').testCase
|
||||
|
||||
|
||||
var t1 = {
|
||||
simpleString: "simpleString",
|
||||
"@" : "@asPropertyName",
|
||||
"$" : "$asPropertyName",
|
||||
"a$a": "$inPropertyName",
|
||||
"$": {
|
||||
"@": "withboth",
|
||||
},
|
||||
a: {
|
||||
b: {
|
||||
c: "food"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = testCase({
|
||||
|
||||
|
||||
// ============================================================================
|
||||
"test undefined, null": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(5);
|
||||
test.equal(undefined, jsonpath(undefined, "foo"));
|
||||
test.equal(null, jsonpath(null, "foo"));
|
||||
test.equal(undefined, jsonpath({}, "foo")[0]);
|
||||
test.equal(undefined, jsonpath({ a: "b" }, "foo")[0]);
|
||||
test.equal(undefined, jsonpath({ a: "b" }, "foo")[100]);
|
||||
test.done();
|
||||
},
|
||||
|
||||
|
||||
// ============================================================================
|
||||
"test $ and @": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(7);
|
||||
test.equal(t1["$"], jsonpath(t1, "\$")[0]);
|
||||
test.equal(t1["$"], jsonpath(t1, "$")[0]);
|
||||
test.equal(t1["a$a"], jsonpath(t1, "a$a")[0]);
|
||||
test.equal(t1["@"], jsonpath(t1, "\@")[0]);
|
||||
test.equal(t1["@"], jsonpath(t1, "@")[0]);
|
||||
test.equal(t1["$"]["@"], jsonpath(t1, "$.$.@")[0]);
|
||||
test.equal(undefined, jsonpath(t1, "\@")[1]);
|
||||
|
||||
test.done();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
45
src/bower_components/jsonpath/test/test.eval.js
vendored
Normal file
45
src/bower_components/jsonpath/test/test.eval.js
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
var jsonpath = require("../").eval,
|
||||
testCase = require('nodeunit').testCase
|
||||
|
||||
var json = {
|
||||
"store": {
|
||||
"book": {
|
||||
"category": "reference",
|
||||
"author": "Nigel Rees",
|
||||
"title": "Sayings of the Century",
|
||||
"price": [8.95, 8.94]
|
||||
},
|
||||
"books": [{
|
||||
"category": "fiction",
|
||||
"author": "Evelyn Waugh",
|
||||
"title": "Sword of Honour",
|
||||
"price": [10.99, 12.29]
|
||||
}, {
|
||||
"category": "fiction",
|
||||
"author": "Herman Melville",
|
||||
"title": "Moby Dick",
|
||||
"isbn": "0-553-21311-3",
|
||||
"price": [8.99, 6.95]
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = testCase({
|
||||
"multi statement eval": function (test) {
|
||||
var expected = json.store.books[0];
|
||||
var selector = "$..[?("
|
||||
+ "var sum = @.price && @.price[0]+@.price[1];"
|
||||
+ "sum > 20;)]"
|
||||
var result = jsonpath(json, selector, {wrap: false});
|
||||
test.deepEqual(expected, result);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"accessing current path": function (test) {
|
||||
var expected = json.store.books[1];
|
||||
var result = jsonpath(json, "$..[?(@path==\"$['store']['books'][1]\")]", {wrap: false});
|
||||
test.deepEqual(expected, result);
|
||||
test.done();
|
||||
}
|
||||
});
|
177
src/bower_components/jsonpath/test/test.examples.js
vendored
Normal file
177
src/bower_components/jsonpath/test/test.examples.js
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
var jsonpath = require("../").eval
|
||||
, testCase = require('nodeunit').testCase
|
||||
|
||||
// tests based on examples at http://goessner.net/articles/JsonPath/
|
||||
|
||||
var json = {"store": {
|
||||
"book": [
|
||||
{ "category": "reference",
|
||||
"author": "Nigel Rees",
|
||||
"title": "Sayings of the Century",
|
||||
"price": 8.95
|
||||
},
|
||||
{ "category": "fiction",
|
||||
"author": "Evelyn Waugh",
|
||||
"title": "Sword of Honour",
|
||||
"price": 12.99
|
||||
},
|
||||
{ "category": "fiction",
|
||||
"author": "Herman Melville",
|
||||
"title": "Moby Dick",
|
||||
"isbn": "0-553-21311-3",
|
||||
"price": 8.99
|
||||
},
|
||||
{ "category": "fiction",
|
||||
"author": "J. R. R. Tolkien",
|
||||
"title": "The Lord of the Rings",
|
||||
"isbn": "0-395-19395-8",
|
||||
"price": 22.99
|
||||
}
|
||||
],
|
||||
"bicycle": {
|
||||
"color": "red",
|
||||
"price": 19.95
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = testCase({
|
||||
|
||||
// ============================================================================
|
||||
"wildcards": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var books = json.store.book;
|
||||
var expected = [books[0].author, books[1].author, books[2].author, books[3].author];
|
||||
var result = jsonpath(json, "$.store.book[*].author");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"all properties, entire tree": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var books = json.store.book;
|
||||
var expected = [books[0].author, books[1].author, books[2].author, books[3].author];
|
||||
var result = jsonpath(json, "$..author");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"all sub properties, single level": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var expected = [json.store.book, json.store.bicycle];
|
||||
var result = jsonpath(json, "$.store.*");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"all sub properties, entire tree": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var books = json.store.book;
|
||||
var expected = [books[0].price, books[1].price, books[2].price, books[3].price, json.store.bicycle.price];
|
||||
var result = jsonpath(json, "$.store..price");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"n property of entire tree": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var books = json.store.book;
|
||||
var expected = [books[2]];
|
||||
var result = jsonpath(json, "$..book[2]");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"last property of entire tree": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(2);
|
||||
var books = json.store.book;
|
||||
var expected = [books[3]];
|
||||
var result = jsonpath(json, "$..book[(@.length-1)]");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
result = jsonpath(json, "$..book[-1:]");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"range of property of entire tree": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(2);
|
||||
var books = json.store.book;
|
||||
var expected = [books[0], books[1]];
|
||||
var result = jsonpath(json, "$..book[0,1]");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
result = jsonpath(json, "$..book[:2]");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"filter all properties if sub property exists,o entire tree": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var books = json.store.book;
|
||||
var expected = [books[2], books[3]];
|
||||
var result = jsonpath(json, "$..book[?(@.isbn)]");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"filter all properties if sub property greater than of entire tree": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var books = json.store.book;
|
||||
var expected = [books[0], books[2]];
|
||||
var result = jsonpath(json, "$..book[?(@.price<10)]");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"all properties of a json structure": function(test) {
|
||||
// ============================================================================
|
||||
// test.expect(1);
|
||||
var expected = [
|
||||
json.store,
|
||||
json.store.book,
|
||||
json.store.bicycle,
|
||||
];
|
||||
json.store.book.forEach(function(book) { expected.push(book); });
|
||||
json.store.book.forEach(function(book) { Object.keys(book).forEach(function(p) { expected.push(book[p]); })});
|
||||
expected.push(json.store.bicycle.color);
|
||||
expected.push(json.store.bicycle.price);
|
||||
|
||||
var result = jsonpath(json, "$..*");
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
59
src/bower_components/jsonpath/test/test.html
vendored
Normal file
59
src/bower_components/jsonpath/test/test.html
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>JSONPath Tests</title>
|
||||
<link rel="stylesheet" href="../node_modules/nodeunit/dist/browser/nodeunit.css" type="text/css" />
|
||||
<script src="../node_modules/nodeunit/dist/browser/nodeunit.js"></script>
|
||||
<script src="../lib/jsonpath.js"></script>
|
||||
<script type="text/javascript">
|
||||
// helper to get all the test cases
|
||||
var suites = [], _testCase = nodeunit.testCase;
|
||||
nodeunit.testCase = function(tc) {
|
||||
suites.push(tc); return _testCase(tc) };
|
||||
// stubs to load nodejs tests
|
||||
function require(path) {
|
||||
if (path === 'nodeunit') return nodeunit;
|
||||
if (path.match(/^\.\.\/?$/)) return jsonPath;
|
||||
}
|
||||
var module = {exports: {}};
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
// synchronous load function for JS code, uses XMLHttpRequest abstraction from
|
||||
// http://www.quirksmode.org/js/xmlhttp.html
|
||||
// Since the tests are written in node.js style we need to wrap their code into
|
||||
// a function, otherwise they would pollute the global NS and interfere with each
|
||||
// other
|
||||
function get(url, callback) {
|
||||
function sendRequest(url,callback) {
|
||||
var req = createXMLHTTPObject();
|
||||
req.open("GET",url,false/*sync*/);
|
||||
req.onreadystatechange = function () { req.readyState == 4 && callback(req); }
|
||||
if (req.readyState != 4) req.send();
|
||||
};
|
||||
function createXMLHTTPObject() {
|
||||
var XMLHttpFactories = [
|
||||
function () {return new XMLHttpRequest()},
|
||||
function () {return new ActiveXObject("Msxml2.XMLHTTP")},
|
||||
function () {return new ActiveXObject("Msxml3.XMLHTTP")},
|
||||
function () {return new ActiveXObject("Microsoft.XMLHTTP")}];
|
||||
for (var i=0;i<XMLHttpFactories.length;i++)
|
||||
try { return XMLHttpFactories[i](); } catch (e) { }
|
||||
return false;
|
||||
}
|
||||
sendRequest(url, callback);
|
||||
}
|
||||
function loadJS(url) { get(url, function(req) { new Function(req.responseText)(); })}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="nodeunit-header">JSONPath Tests</h1>
|
||||
<script>
|
||||
loadJS('test.arr.js');
|
||||
loadJS("test.at_and_dollar.js");
|
||||
loadJS("test.eval.js");
|
||||
loadJS("test.examples.js");
|
||||
loadJS("test.intermixed.arr.js");
|
||||
loadJS("test.parent-selector.js");
|
||||
nodeunit.run(suites);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
53
src/bower_components/jsonpath/test/test.intermixed.arr.js
vendored
Normal file
53
src/bower_components/jsonpath/test/test.intermixed.arr.js
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
var jsonpath = require("../").eval,
|
||||
testCase = require('nodeunit').testCase
|
||||
|
||||
// tests based on examples at http://goessner.net/articles/JsonPath/
|
||||
|
||||
var json = {"store":{
|
||||
"book":[
|
||||
{ "category":"reference",
|
||||
"author":"Nigel Rees",
|
||||
"title":"Sayings of the Century",
|
||||
"price":[8.95, 8.94, 8.93]
|
||||
},
|
||||
{ "category":"fiction",
|
||||
"author":"Evelyn Waugh",
|
||||
"title":"Sword of Honour",
|
||||
"price":12.99
|
||||
},
|
||||
{ "category":"fiction",
|
||||
"author":"Herman Melville",
|
||||
"title":"Moby Dick",
|
||||
"isbn":"0-553-21311-3",
|
||||
"price":8.99
|
||||
},
|
||||
{ "category":"fiction",
|
||||
"author":"J. R. R. Tolkien",
|
||||
"title":"The Lord of the Rings",
|
||||
"isbn":"0-395-19395-8",
|
||||
"price":22.99
|
||||
}
|
||||
],
|
||||
"bicycle":{
|
||||
"color":"red",
|
||||
"price":19.95
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = testCase({
|
||||
|
||||
// ============================================================================
|
||||
"all sub properties, entire tree":function (test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var books = json.store.book;
|
||||
var expected = [books[1].price, books[2].price, books[3].price, json.store.bicycle.price];
|
||||
expected = books[0].price.concat(expected);
|
||||
var result = jsonpath(json, "$.store..price", {flatten: true});
|
||||
test.deepEqual(expected, result);
|
||||
|
||||
test.done();
|
||||
}
|
||||
});
|
64
src/bower_components/jsonpath/test/test.parent-selector.js
vendored
Normal file
64
src/bower_components/jsonpath/test/test.parent-selector.js
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
var jsonpath = require("../").eval,
|
||||
testCase = require('nodeunit').testCase
|
||||
|
||||
var json = {
|
||||
"name": "root",
|
||||
"children": [
|
||||
{"name": "child1", "children": [{"name": "child1_1"},{"name": "child1_2"}]},
|
||||
{"name": "child2", "children": [{"name": "child2_1"}]},
|
||||
{"name": "child3", "children": [{"name": "child3_1"}, {"name": "child3_2"}]}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
module.exports = testCase({
|
||||
|
||||
// ============================================================================
|
||||
"simple parent selection": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var result = jsonpath(json, "$.children[0]^", {flatten: true});
|
||||
test.deepEqual(json.children, result);
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"parent selection with multiple matches": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var expected = [json.children,json.children];
|
||||
var result = jsonpath(json, "$.children[1:3]^");
|
||||
test.deepEqual(expected, result);
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"select sibling via parent": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var expected = [{"name": "child3_2"}];
|
||||
var result = jsonpath(json, "$..[?(@.name && @.name.match(/3_1$/))]^[?(@.name.match(/_2$/))]");
|
||||
test.deepEqual(expected, result);
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"parent parent parent": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var expected = json.children[0].children;
|
||||
var result = jsonpath(json, "$..[?(@.name && @.name.match(/1_1$/))].name^^", {flatten: true});
|
||||
test.deepEqual(expected, result);
|
||||
test.done();
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
"no such parent": function(test) {
|
||||
// ============================================================================
|
||||
test.expect(1);
|
||||
var result = jsonpath(json, "name^^");
|
||||
test.deepEqual([], result);
|
||||
test.done();
|
||||
}
|
||||
|
||||
});
|
|
@ -6,22 +6,22 @@ define(function (require) {
|
|||
var nextTick = require('utils/next_tick');
|
||||
|
||||
function DataSource(courier, initialState) {
|
||||
var state;
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
// state can be serialized as JSON, and passed back in to restore
|
||||
if (initialState) {
|
||||
if (typeof initialState === 'string') {
|
||||
state = JSON.parse(initialState);
|
||||
this._state = (function () {
|
||||
// state can be serialized as JSON, and passed back in to restore
|
||||
if (initialState) {
|
||||
if (typeof initialState === 'string') {
|
||||
return JSON.parse(initialState);
|
||||
} else {
|
||||
return _.cloneDeep(initialState);
|
||||
}
|
||||
} else {
|
||||
state = _.cloneDeep(initialState);
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
state = {};
|
||||
}
|
||||
}());
|
||||
|
||||
this._state = state;
|
||||
this._dynamicState = this._dynamicState || {};
|
||||
this._courier = courier;
|
||||
|
||||
// before newListener to prevent unnecessary "emit" when added
|
||||
|
@ -64,9 +64,9 @@ define(function (require) {
|
|||
this._methods.forEach(function (name) {
|
||||
this[name] = function (val) {
|
||||
if (val == null) {
|
||||
delete state[name];
|
||||
delete this._state[name];
|
||||
} else {
|
||||
state[name] = val;
|
||||
this._state[name] = val;
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -87,6 +87,7 @@ define(function (require) {
|
|||
var current = this;
|
||||
while (current) {
|
||||
if (current._state[name] !== void 0) return current._state[name];
|
||||
if (current._dynamicState[name] !== void 0) return current._dynamicState[name]();
|
||||
current = current._parent;
|
||||
}
|
||||
};
|
||||
|
@ -108,7 +109,7 @@ define(function (require) {
|
|||
* Clear the disabled flag, you do not need to call this unless you
|
||||
* explicitly disabled the DataSource
|
||||
*/
|
||||
DataSource.prototype.enableFetch = function () {
|
||||
DataSource.prototype.enableAuthFetch = function () {
|
||||
delete this._fetchDisabled;
|
||||
return this;
|
||||
};
|
||||
|
@ -116,7 +117,7 @@ define(function (require) {
|
|||
/**
|
||||
* Disable the DataSource, preventing it or any of it's children from being searched
|
||||
*/
|
||||
DataSource.prototype.disableFetch = function () {
|
||||
DataSource.prototype.disableAutoFetch = function () {
|
||||
this._fetchDisabled = true;
|
||||
return this;
|
||||
};
|
||||
|
|
|
@ -210,16 +210,16 @@ define(function (require) {
|
|||
* @param {Function} cb - callback
|
||||
*/
|
||||
DocSource.prototype._sendToEs = function (method, validateVersion, body, cb) {
|
||||
cb = this._wrapcb(cb)
|
||||
|
||||
var source = this;
|
||||
var courier = this._courier;
|
||||
var client = courier._getClient();
|
||||
var params = {
|
||||
id: this._state.id,
|
||||
type: this._state.type,
|
||||
index: this._state.index,
|
||||
body: body,
|
||||
ignore: [409]
|
||||
};
|
||||
|
||||
// straight assignment will causes undefined values
|
||||
var params = _.pick(this._state, 'id', 'type', 'index');
|
||||
params.body = body;
|
||||
params.ignore = [409];
|
||||
|
||||
if (validateVersion) {
|
||||
params.version = source._getVersion();
|
||||
|
|
217
src/kibana/apps/discover/controllers/discover.js
Normal file
217
src/kibana/apps/discover/controllers/discover.js
Normal file
|
@ -0,0 +1,217 @@
|
|||
define(function (require) {
|
||||
var _ = require('utils/mixins');
|
||||
|
||||
var settingsHtml = require('text!../partials/settings.html');
|
||||
|
||||
var app = require('modules').get('app/discover');
|
||||
|
||||
var intervals = [
|
||||
{ display: '', val: null },
|
||||
{ display: 'Hourly', val: 'hourly' },
|
||||
{ display: 'Daily', val: 'daily' },
|
||||
{ display: 'Weekly', val: 'weekly' },
|
||||
{ display: 'Monthly', val: 'monthly' },
|
||||
{ display: 'Yearly', val: 'yearly' }
|
||||
];
|
||||
|
||||
app.controller('discover', function ($scope, config, $q, $route, savedSearches, courier, createNotifier, $location) {
|
||||
var notify = createNotifier({
|
||||
location: 'Discover'
|
||||
});
|
||||
|
||||
var search = $route.current.locals.search;
|
||||
if (!search) return notify.fatal('search failed to load');
|
||||
|
||||
$scope.opts = {
|
||||
// number of records to fetch, then paginate through
|
||||
sampleSize: 500,
|
||||
// max length for summaries in the table
|
||||
maxSummaryLength: 100,
|
||||
// Index to match
|
||||
index: 'logstash-*',
|
||||
timefield: '@timestamp',
|
||||
savedSearch: search
|
||||
};
|
||||
|
||||
// track the initial state of the search
|
||||
var searchIsPhantom = search.phantom;
|
||||
$scope.opts.saveDataSource = function () {
|
||||
search.save()
|
||||
.then(function () {
|
||||
notify.info('Saved Data Source "' + search.details.name + '"');
|
||||
if (searchIsPhantom) {
|
||||
searchIsPhantom = false;
|
||||
$location.url('/discover/' + search.get('id'));
|
||||
}
|
||||
}, notify.error);
|
||||
};
|
||||
|
||||
// stores the complete list of fields
|
||||
$scope.fields = null;
|
||||
|
||||
// stores the fields we want to fetch
|
||||
$scope.columns = null;
|
||||
|
||||
// index pattern interval options
|
||||
$scope.intervals = intervals;
|
||||
$scope.interval = intervals[0];
|
||||
|
||||
var initialQuery = search.get('query');
|
||||
$scope.query = initialQuery ? initialQuery.query_string.query : '';
|
||||
|
||||
// the index to use when they don't specify one
|
||||
config.$watch('discover.defaultIndex', function (val) {
|
||||
if (!val) return config.set('discover.defaultIndex', '_all');
|
||||
if (!$scope.opts.index) {
|
||||
$scope.opts.index = val;
|
||||
$scope.fetch();
|
||||
}
|
||||
});
|
||||
|
||||
search
|
||||
.$scope($scope)
|
||||
.inherits(courier.rootSearchSource)
|
||||
.on('results', function (res) {
|
||||
if (!$scope.fields) getFields();
|
||||
|
||||
$scope.rows = res.hits.hits;
|
||||
});
|
||||
|
||||
$scope.sort = ['_score', 'desc'];
|
||||
|
||||
$scope.getSort = function () {
|
||||
return $scope.sort;
|
||||
};
|
||||
|
||||
$scope.setSort = function (field, order) {
|
||||
var sort = {};
|
||||
sort[field] = order;
|
||||
search.sort([sort]);
|
||||
$scope.sort = [field, order];
|
||||
$scope.fetch();
|
||||
};
|
||||
|
||||
$scope.toggleConfig = function () {
|
||||
// Close if already open
|
||||
if ($scope.configTemplate === settingsHtml) {
|
||||
delete $scope.configTemplate;
|
||||
} else {
|
||||
$scope.configTemplate = settingsHtml;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.fetch = function () {
|
||||
if (!$scope.fields) getFields();
|
||||
search
|
||||
.size($scope.opts.sampleSize)
|
||||
.query(!$scope.query ? null : {
|
||||
query_string: {
|
||||
query: $scope.query
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.opts.index !== search.get('index')) {
|
||||
// set the index on the savedSearch
|
||||
search.index($scope.opts.index);
|
||||
// clear the columns and fields, then refetch when we do a search
|
||||
$scope.columns = $scope.fields = null;
|
||||
}
|
||||
|
||||
// fetch just this savedSearch
|
||||
search.fetch();
|
||||
};
|
||||
|
||||
var activeGetFields;
|
||||
function getFields() {
|
||||
var defer = $q.defer();
|
||||
|
||||
if (activeGetFields) {
|
||||
activeGetFields.then(function () {
|
||||
defer.resolve();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var currentState = _.transform($scope.fields || [], function (current, field) {
|
||||
current[field.name] = {
|
||||
display: field.display
|
||||
};
|
||||
}, {});
|
||||
|
||||
search
|
||||
.getFields()
|
||||
.then(function (fields) {
|
||||
if (!fields) return;
|
||||
|
||||
$scope.fields = [];
|
||||
$scope.columns = $scope.columns || [];
|
||||
|
||||
// Inject source into list;
|
||||
$scope.fields.push({name: '_source', type: 'source', display: false});
|
||||
|
||||
_(fields)
|
||||
.keys()
|
||||
.sort()
|
||||
.each(function (name) {
|
||||
var field = fields[name];
|
||||
field.name = name;
|
||||
|
||||
_.defaults(field, currentState[name]);
|
||||
$scope.fields.push(field);
|
||||
});
|
||||
|
||||
|
||||
refreshColumns();
|
||||
defer.resolve();
|
||||
}, defer.reject);
|
||||
|
||||
return defer.promise.then(function () {
|
||||
activeGetFields = null;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.toggleField = function (name) {
|
||||
var field = _.find($scope.fields, { name: name });
|
||||
|
||||
// toggle the display property
|
||||
field.display = !field.display;
|
||||
|
||||
if ($scope.columns.length === 1 && $scope.columns[0] === '_source') {
|
||||
$scope.columns = _.toggleInOut($scope.columns, name);
|
||||
$scope.columns = _.toggleInOut($scope.columns, '_source');
|
||||
_.find($scope.fields, {name: '_source'}).display = false;
|
||||
|
||||
} else {
|
||||
$scope.columns = _.toggleInOut($scope.columns, name);
|
||||
}
|
||||
|
||||
refreshColumns();
|
||||
};
|
||||
|
||||
$scope.refreshFieldList = function () {
|
||||
search.clearFieldCache(function () {
|
||||
getFields(function () {
|
||||
$scope.fetch();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function refreshColumns() {
|
||||
// Get all displayed field names;
|
||||
var fields = _.pluck(_.filter($scope.fields, function (field) {
|
||||
return field.display;
|
||||
}), 'name');
|
||||
|
||||
// Make sure there are no columns added that aren't in the displayed field list.
|
||||
$scope.columns = _.intersection($scope.columns, fields);
|
||||
|
||||
// If no columns remain, use _source
|
||||
if (!$scope.columns.length) {
|
||||
$scope.toggleField('_source');
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$emit('application.load');
|
||||
});
|
||||
});
|
116
src/kibana/apps/discover/factories/saved_search.js
Normal file
116
src/kibana/apps/discover/factories/saved_search.js
Normal file
|
@ -0,0 +1,116 @@
|
|||
define(function (require) {
|
||||
var app = require('modules').get('app/discover');
|
||||
var bind = require('lodash').bind;
|
||||
var assign = require('lodash').assign;
|
||||
var nextTick = require('utils/next_tick');
|
||||
|
||||
app.factory('SavedSearch', function (configFile, courier, $q) {
|
||||
|
||||
function SavedSearch(id) {
|
||||
var search = courier.createSource('search');
|
||||
search._doc = courier.createSource('doc')
|
||||
.index(configFile.kibanaIndex)
|
||||
.type('saved_searches')
|
||||
.id(id || void 0)
|
||||
.on('results', function onResults(resp) {
|
||||
if (!resp.found) {
|
||||
search._doc.removeListener('results', onResults);
|
||||
search.emit('noconfig', new Error('Unable to find that Saved Search...'));
|
||||
}
|
||||
|
||||
search.set(resp._source.state);
|
||||
search.details = resp._source.details;
|
||||
assign(search.deatils, resp._source.details);
|
||||
|
||||
if (!id) {
|
||||
id = resp._id;
|
||||
// it's no longer a phantom
|
||||
search.phantom = false;
|
||||
}
|
||||
|
||||
if (!search.ready()) {
|
||||
search.ready(true);
|
||||
// allow the search to be fetched automatically
|
||||
search.enableAuthFetch();
|
||||
}
|
||||
});
|
||||
|
||||
search._dynamicState.id = function () {
|
||||
return search._doc.get('id');
|
||||
};
|
||||
|
||||
search.phantom = true;
|
||||
search.details = {
|
||||
name: '',
|
||||
hits: 0
|
||||
};
|
||||
|
||||
search.ready = (function () {
|
||||
var queue = id ? [] : false;
|
||||
var err;
|
||||
return function (cb) {
|
||||
switch (typeof cb) {
|
||||
// check if we are ready yet
|
||||
case 'undefined':
|
||||
return !queue;
|
||||
|
||||
// call or queue a function once ready
|
||||
case 'function':
|
||||
if (queue) {
|
||||
// queue will be false once complete
|
||||
queue.push(cb);
|
||||
} else {
|
||||
// always callback async
|
||||
nextTick(cb, err);
|
||||
}
|
||||
return;
|
||||
|
||||
// indicate that we are ready, or there was a failure loading
|
||||
default:
|
||||
if (queue && cb) {
|
||||
if (cb instanceof Error) {
|
||||
err = cb;
|
||||
}
|
||||
|
||||
// if queued functions are confused, and ask us if
|
||||
// we are ready, we should tell them yes
|
||||
var fns = queue;
|
||||
queue = false;
|
||||
|
||||
// be sure to send out the error we got if there was one
|
||||
fns.forEach(function (fn) { fn(err); });
|
||||
}
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
search.save = function () {
|
||||
var defer = $q.defer();
|
||||
|
||||
search._doc.doIndex({
|
||||
details: search.details,
|
||||
state: search.toJSON()
|
||||
}, function (err, id) {
|
||||
if (err) return defer.reject(err);
|
||||
|
||||
search._doc.id(id);
|
||||
defer.resolve();
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
};
|
||||
|
||||
if (!id) {
|
||||
// we have nothing left to load
|
||||
search.ready(true);
|
||||
} else {
|
||||
// before this search is fetched, it's config needs to be loaded
|
||||
search.disableAutoFetch();
|
||||
// get the config doc now
|
||||
search._doc.fetch();
|
||||
}
|
||||
return search;
|
||||
}
|
||||
return SavedSearch;
|
||||
});
|
||||
});
|
|
@ -1,222 +1,20 @@
|
|||
define(function (require, module, exports) {
|
||||
var _ = require('lodash');
|
||||
|
||||
require('directives/table');
|
||||
require('./field_chooser');
|
||||
require('services/saved_searches');
|
||||
require('utils/mixins');
|
||||
|
||||
require('./services/saved_searches');
|
||||
require('./timechart');
|
||||
require('./controllers/discover');
|
||||
|
||||
var app = require('modules').get('app/discover');
|
||||
|
||||
var intervals = [
|
||||
{ display: '', val: null },
|
||||
{ display: 'Hourly', val: 'hourly' },
|
||||
{ display: 'Daily', val: 'daily' },
|
||||
{ display: 'Weekly', val: 'weekly' },
|
||||
{ display: 'Monthly', val: 'monthly' },
|
||||
{ display: 'Yearly', val: 'yearly' }
|
||||
];
|
||||
|
||||
app.controller('discover', function ($scope, config, $q, $routeParams, savedSearches, courier) {
|
||||
var source;
|
||||
if ($routeParams.id) {
|
||||
source = savedSearches.get($routeParams.id);
|
||||
} else {
|
||||
source = savedSearches.create();
|
||||
}
|
||||
|
||||
$scope.opts = {
|
||||
// number of records to fetch, then paginate through
|
||||
sampleSize: 500,
|
||||
// max length for summaries in the table
|
||||
maxSummaryLength: 100,
|
||||
// Index to match
|
||||
index: '_all',
|
||||
timefield: '@timestamp'
|
||||
};
|
||||
|
||||
// stores the complete list of fields
|
||||
$scope.fields = null;
|
||||
|
||||
// stores the fields we want to fetch
|
||||
$scope.columns = null;
|
||||
|
||||
// index pattern interval options
|
||||
$scope.intervals = intervals;
|
||||
$scope.interval = $scope.intervals[0];
|
||||
|
||||
// the index to use when they don't specify one
|
||||
config.$watch('discover.defaultIndex', function (val) {
|
||||
if (!val) return config.set('discover.defaultIndex', '_all');
|
||||
if (!$scope.opts.index) {
|
||||
$scope.opts.index = val;
|
||||
$scope.fetch();
|
||||
app.config(function ($routeProvider) {
|
||||
$routeProvider.when('/discover/:id?', {
|
||||
templateUrl: 'kibana/apps/discover/index.html',
|
||||
resolve: {
|
||||
search: function (savedSearches, $route) {
|
||||
return savedSearches.get($route.current.params.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
source
|
||||
.$scope($scope)
|
||||
.inherits(courier.rootSearchSource)
|
||||
.on('results', function (res) {
|
||||
if (!$scope.fields) getFields();
|
||||
|
||||
$scope.rows = res.hits.hits;
|
||||
});
|
||||
|
||||
|
||||
var init = function () {
|
||||
$scope.fetch();
|
||||
};
|
||||
|
||||
$scope.sort = ['_score', 'desc'];
|
||||
|
||||
$scope.getSort = function () {
|
||||
return $scope.sort;
|
||||
};
|
||||
|
||||
$scope.setSort = function (field, order) {
|
||||
var sort = {};
|
||||
sort[field] = order;
|
||||
source.sort([sort]);
|
||||
$scope.sort = [field, order];
|
||||
$scope.fetch();
|
||||
};
|
||||
|
||||
var setConfigTemplate = function (template) {
|
||||
// Close if already open
|
||||
if ($scope.configTemplate === template) {
|
||||
delete $scope.configTemplate;
|
||||
return;
|
||||
} else {
|
||||
$scope.configTemplate = template;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleConfig = function () {
|
||||
setConfigTemplate(require('text!./partials/settings.html'));
|
||||
/*
|
||||
$scope.configSubmit = function () {
|
||||
$scope.save($scope.dashboard.title);
|
||||
};
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
$scope.fetch = function () {
|
||||
if (!$scope.fields) getFields();
|
||||
source
|
||||
.size($scope.opts.sampleSize)
|
||||
.query(!$scope.query ? null : {
|
||||
query_string: {
|
||||
query: $scope.query
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.opts.index !== source.get('index')) {
|
||||
// set the index on the data source
|
||||
source.index($scope.opts.index);
|
||||
// clear the columns and fields, then refetch when we do a search
|
||||
$scope.columns = $scope.fields = null;
|
||||
}
|
||||
|
||||
// fetch just this datasource
|
||||
source.fetch();
|
||||
};
|
||||
|
||||
var activeGetFields;
|
||||
function getFields() {
|
||||
var defer = $q.defer();
|
||||
|
||||
if (activeGetFields) {
|
||||
activeGetFields.then(function () {
|
||||
defer.resolve();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var currentState = _.transform($scope.fields || [], function (current, field) {
|
||||
current[field.name] = {
|
||||
display: field.display
|
||||
};
|
||||
}, {});
|
||||
|
||||
source
|
||||
.getFields()
|
||||
.then(function (fields) {
|
||||
if (!fields) return;
|
||||
|
||||
$scope.fields = [];
|
||||
$scope.columns = $scope.columns || [];
|
||||
|
||||
// Inject source into list;
|
||||
$scope.fields.push({name: '_source', type: 'source', display: false});
|
||||
|
||||
_(fields)
|
||||
.keys()
|
||||
.sort()
|
||||
.each(function (name) {
|
||||
var field = fields[name];
|
||||
field.name = name;
|
||||
|
||||
_.defaults(field, currentState[name]);
|
||||
$scope.fields.push(field);
|
||||
});
|
||||
|
||||
|
||||
refreshColumns();
|
||||
defer.resolve();
|
||||
}, defer.reject);
|
||||
|
||||
return defer.promise.then(function () {
|
||||
activeGetFields = null;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.toggleField = function (name) {
|
||||
var field = _.find($scope.fields, { name: name });
|
||||
|
||||
// toggle the display property
|
||||
field.display = !field.display;
|
||||
|
||||
if ($scope.columns.length === 1 && $scope.columns[0] === '_source') {
|
||||
$scope.columns = _.toggleInOut($scope.columns, name);
|
||||
$scope.columns = _.toggleInOut($scope.columns, '_source');
|
||||
_.find($scope.fields, {name: '_source'}).display = false;
|
||||
|
||||
} else {
|
||||
$scope.columns = _.toggleInOut($scope.columns, name);
|
||||
}
|
||||
|
||||
refreshColumns();
|
||||
};
|
||||
|
||||
$scope.refreshFieldList = function () {
|
||||
source.clearFieldCache(function () {
|
||||
getFields(function () {
|
||||
$scope.fetch();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function refreshColumns() {
|
||||
// Get all displayed field names;
|
||||
var fields = _.pluck(_.filter($scope.fields, function (field) {
|
||||
return field.display;
|
||||
}), 'name');
|
||||
|
||||
// Make sure there are no columns added that aren't in the displayed field list.
|
||||
$scope.columns = _.intersection($scope.columns, fields);
|
||||
|
||||
// If no columns remain, use _source
|
||||
if (!$scope.columns.length) {
|
||||
$scope.toggleField('_source');
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
$scope.$emit('application.load');
|
||||
});
|
||||
});
|
|
@ -1,7 +1,19 @@
|
|||
<label class="control-label">Index</label>
|
||||
<input class="form-control" ng-model="opts.index">
|
||||
|
||||
<!--
|
||||
<label class="control-label">Max Summary Length</label>
|
||||
<input type="number" name class="form-control" ng-model="opts.maxSummaryLength">
|
||||
-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label class="control-label">Index</label>
|
||||
<input class="form-control" ng-model="opts.index">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Name</label>
|
||||
<input ng-model="opts.savedSearch.details.name" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button ng-click="opts.saveDataSource()" type="button" class="btn btn-primary">
|
||||
{{opts.savedSearch.phantom ? 'Create' : 'Save'}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
25
src/kibana/apps/discover/services/saved_searches.js
Normal file
25
src/kibana/apps/discover/services/saved_searches.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
define(function (require) {
|
||||
|
||||
var module = require('modules').get('kibana/services');
|
||||
var _ = require('lodash');
|
||||
|
||||
require('../factories/saved_search');
|
||||
|
||||
module.service('savedSearches', function (courier, configFile, $q, createNotifier, SavedSearch) {
|
||||
var notify = createNotifier({
|
||||
location: 'Saved Searches'
|
||||
});
|
||||
|
||||
this.get = function (id) {
|
||||
var defer = $q.defer();
|
||||
var search = new SavedSearch(id);
|
||||
|
||||
search.ready(function (err) {
|
||||
if (err) defer.reject(err);
|
||||
else defer.resolve(search);
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
};
|
||||
});
|
||||
});
|
|
@ -10,55 +10,81 @@ define(function (require) {
|
|||
location: 'Visualize Controller'
|
||||
});
|
||||
|
||||
// the object detailing the visualization
|
||||
var vis = $scope.vis = window.vis = new Vis({
|
||||
metric: {
|
||||
label: 'Y-Axis',
|
||||
min: 1,
|
||||
max: 1
|
||||
},
|
||||
segment: {
|
||||
label: 'X-Axis',
|
||||
min: 1,
|
||||
max: 1
|
||||
},
|
||||
group: {
|
||||
label: 'Color',
|
||||
max: 10
|
||||
},
|
||||
split: {
|
||||
label: 'Rows & Columns',
|
||||
max: 2
|
||||
}
|
||||
}, {
|
||||
split: [
|
||||
{
|
||||
field: 'response',
|
||||
size: 5,
|
||||
agg: 'terms'
|
||||
config: {
|
||||
metric: {
|
||||
label: 'Y-Axis',
|
||||
min: 1,
|
||||
max: 1
|
||||
},
|
||||
{
|
||||
field: '_type',
|
||||
size: 5,
|
||||
agg: 'terms'
|
||||
segment: {
|
||||
label: 'X-Axis',
|
||||
min: 1,
|
||||
max: 1
|
||||
},
|
||||
group: {
|
||||
label: 'Color',
|
||||
max: 1
|
||||
},
|
||||
split: {
|
||||
label: 'Rows & Columns',
|
||||
max: 2
|
||||
}
|
||||
],
|
||||
segment: [
|
||||
{
|
||||
field: '@timestamp',
|
||||
interval: 'week'
|
||||
}
|
||||
],
|
||||
group: [
|
||||
{
|
||||
field: 'extension',
|
||||
size: 5,
|
||||
agg: 'terms',
|
||||
global: true
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// the object detailing the visualization
|
||||
// var vis = $scope.vis = window.vis = new Vis({
|
||||
// config: {
|
||||
// metric: {
|
||||
// label: 'Y-Axis',
|
||||
// min: 1,
|
||||
// max: 1
|
||||
// },
|
||||
// segment: {
|
||||
// label: 'X-Axis',
|
||||
// min: 1,
|
||||
// max: 1
|
||||
// },
|
||||
// group: {
|
||||
// label: 'Color',
|
||||
// max: 10
|
||||
// },
|
||||
// split: {
|
||||
// label: 'Rows & Columns',
|
||||
// max: 2
|
||||
// }
|
||||
// },
|
||||
// state: {
|
||||
// split: [
|
||||
// {
|
||||
// field: 'response',
|
||||
// size: 5,
|
||||
// agg: 'terms'
|
||||
// },
|
||||
// {
|
||||
// field: '_type',
|
||||
// size: 5,
|
||||
// agg: 'terms'
|
||||
// }
|
||||
// ],
|
||||
// segment: [
|
||||
// {
|
||||
// field: '@timestamp',
|
||||
// interval: 'week'
|
||||
// }
|
||||
// ],
|
||||
// group: [
|
||||
// {
|
||||
// field: 'extension',
|
||||
// size: 5,
|
||||
// agg: 'terms',
|
||||
// global: true
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// });
|
||||
|
||||
vis.dataSource.$scope($scope);
|
||||
|
||||
$scope.refreshFields = function () {
|
||||
|
|
|
@ -22,6 +22,8 @@ define(function (require) {
|
|||
// only link if the dataSource isn't already linked
|
||||
vis.dataSource.$scope($scope);
|
||||
}
|
||||
|
||||
vis.dataSource.fetch();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@ define(function (require) {
|
|||
location: 'Visualization'
|
||||
});
|
||||
|
||||
function Vis(config, state) {
|
||||
config = config || {};
|
||||
function Vis(opts) {
|
||||
opts = opts || {};
|
||||
var config = opts.config || {};
|
||||
var state = opts.state || null;
|
||||
|
||||
// the visualization type
|
||||
this.type = config.type || 'histogram';
|
||||
|
|
|
@ -118,7 +118,8 @@ define(function (require) {
|
|||
var rendering = false;
|
||||
|
||||
return function renderRows(rows) {
|
||||
[].push.apply(queue, rows);
|
||||
// overwrite the queue, don't keep old rows
|
||||
queue = rows.slice(0);
|
||||
if (!rendering) {
|
||||
onTick();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ define(function (require) {
|
|||
});
|
||||
|
||||
configFile.apps.forEach(function (app) {
|
||||
if (app.id === 'discover') return;
|
||||
$routeProvider.when('/' + app.id, {
|
||||
templateUrl: 'kibana/apps/' + app.id + '/index.html'
|
||||
});
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
define(function (require) {
|
||||
|
||||
var module = require('modules').get('kibana/services');
|
||||
|
||||
module.service('savedSearches', function (courier, configFile, $q) {
|
||||
this.get = function (id) {
|
||||
var docLoaded = id ? false : true;
|
||||
var doc = courier.createSource('doc')
|
||||
.index(configFile.kibanaIndex)
|
||||
.type('saved_searches')
|
||||
.id(id)
|
||||
.on('results', function (doc) {
|
||||
search.set(doc._source.state);
|
||||
|
||||
// set the
|
||||
id = doc._id;
|
||||
if (!docLoaded) {
|
||||
docLoaded = true;
|
||||
search.enable();
|
||||
}
|
||||
});
|
||||
|
||||
var search = courier.createSource('search');
|
||||
search.save = function () {
|
||||
var defer = $q.defer();
|
||||
|
||||
doc.doIndex({
|
||||
state: search.toJSON()
|
||||
}, function (err, id) {
|
||||
if (err) return defer.reject(err);
|
||||
defer.resolve();
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
};
|
||||
|
||||
if (!docLoaded) search.disableFetch();
|
||||
return search;
|
||||
};
|
||||
|
||||
this.create = this.get;
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue