Can now use pipeline as query

This commit is contained in:
didier amyot committed 2015-05-25 12:07:43 -04:00
1 parent 41552040cc
commit e305aa8f2a
15 files changed
+273 -73

No files matched your search

+2
View File
@@ -6,6 +6,8 @@ project is inspired by [mongs](http://whit537.org/mongs/) but has additional fea
* Support for [GeoJson](http://geojson.org/) indexes. * Support for [GeoJson](http://geojson.org/) indexes.
* View basic statistics * View basic statistics
* Easy filtering support * Easy filtering support
* Use of (aggregation pipeline)[http://docs.mongodb.org/manual/core/aggregation-pipeline/]
in the user interface
### run vulture ### run vulture
+122 -14
View File
@@ -3,7 +3,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
@@ -67,7 +66,6 @@ func (this *VultureBackend) GetDataBases() ([]string, error) {
} else if strings.Contains(err.Error(), "authorized") { } else if strings.Contains(err.Error(), "authorized") {
return this.getDatabaseFromMongoURL() return this.getDatabaseFromMongoURL()
} else { } else {
fmt.Println(">>> ", err.Error())
return nil, err return nil, err
} }
} }
@@ -114,7 +112,6 @@ func (this *VultureBackend) getMetaData(query *mgo.Query) (map[string]interface{
} }
return meta, nil return meta, nil
} }
func (this *VultureBackend) GetDocumentByIndex(index int, query interface{}) (map[string]interface{}, error) { func (this *VultureBackend) GetDocumentByIndex(index int, query interface{}) (map[string]interface{}, error) {
@@ -136,12 +133,8 @@ func (this *VultureBackend) wrapDocumentWithMetadata(doc interface{}, query *mgo
return ret, nil return ret, nil
} }
func (this *VultureBackend) GetStat(query bson.M, key string) (interface{}, error) { func (this *VultureBackend) getStat(iter *mgo.Iter, key string) (interface{}, error) {
s := NewStatAggregator() s := NewStatAggregator()
if _, ok := query[key]; !ok {
query[key] = bson.M{"$exists": true}
}
iter := this.Collection.Find(query).Select(bson.M{key: 1}).Iter()
result := make(map[string]interface{}) result := make(map[string]interface{})
in := make(chan float64) in := make(chan float64)
@@ -165,12 +158,8 @@ func (this *VultureBackend) GetStat(query bson.M, key string) (interface{}, erro
return <-out, nil return <-out, nil
} }
func (this *VultureBackend) GetHistogram(query bson.M, key string, min, max float64, numberOfBins int) (interface{}, error) { func (this *VultureBackend) getHistogram(iter *mgo.Iter, key string, min, max float64, numberOfBins int) (interface{}, error) {
hm := &HistogramMaker{min, max, numberOfBins} hm := &HistogramMaker{min, max, numberOfBins}
if _, ok := query[key]; !ok {
query[key] = bson.M{"$exists": true}
}
iter := this.Collection.Find(query).Select(bson.M{key: 1}).Iter()
result := make(map[string]interface{}) result := make(map[string]interface{})
in := make(chan float64) in := make(chan float64)
@@ -191,7 +180,32 @@ func (this *VultureBackend) GetHistogram(query bson.M, key string, min, max floa
return nil, err return nil, err
} }
return <-out, nil return <-out, nil
}
func (this *VultureBackend) GetStat(query bson.M, key string) (interface{}, error) {
if _, ok := query[key]; !ok {
query[key] = bson.M{"$exists": true}
}
iter := this.Collection.Find(query).Select(bson.M{key: 1}).Iter()
return this.getStat(iter, key)
}
func (this *VultureBackend) GetStatFromPipeline(pipeline []interface{}, key string) (interface{}, error) {
iter := this.Collection.Pipe(pipeline).Iter()
return this.getStat(iter, key)
}
func (this *VultureBackend) GetHistogram(query bson.M, key string, min, max float64, numberOfBins int) (interface{}, error) {
if _, ok := query[key]; !ok {
query[key] = bson.M{"$exists": true}
}
iter := this.Collection.Find(query).Select(bson.M{key: 1}).Iter()
return this.getHistogram(iter, key, min, max, numberOfBins)
}
func (this *VultureBackend) GetHistogramFromPipeline(pipeline []interface{}, key string, min, max float64, numberOfBins int) (interface{}, error) {
iter := this.Collection.Pipe(pipeline).Iter()
return this.getHistogram(iter, key, min, max, numberOfBins)
} }
func (this *VultureBackend) GetAllDocuments(query interface{}) (interface{}, error) { func (this *VultureBackend) GetAllDocuments(query interface{}) (interface{}, error) {
@@ -206,6 +220,16 @@ func (this *VultureBackend) GetAllDocuments(query interface{}) (interface{}, err
return this.wrapDocumentWithMetadata(result, nil) return this.wrapDocumentWithMetadata(result, nil)
} }
func (this *VultureBackend) GetAggregatePipelineResult(pipeline interface{}) (interface{}, error) {
var result []map[string]interface{}
pipe := this.Collection.Pipe(pipeline)
if err := pipe.All(&result); err != nil {
return nil, err
}
return this.wrapDocumentWithMetadata(result, nil)
}
func getAvailableServers(w http.ResponseWriter, request *http.Request) (interface{}, error) { func getAvailableServers(w http.ResponseWriter, request *http.Request) (interface{}, error) {
ret := make([]string, len(params.MongoServers)) ret := make([]string, len(params.MongoServers))
for i, ms := range params.MongoServers { for i, ms := range params.MongoServers {
@@ -280,6 +304,23 @@ func getDocumentById(w http.ResponseWriter, request *http.Request) (interface{},
return vb.GetDocumentByIndex(0, bson.M{"_id": bson.ObjectIdHex(id)}) return vb.GetDocumentByIndex(0, bson.M{"_id": bson.ObjectIdHex(id)})
} }
func getAggregatePipelireResult(w http.ResponseWriter, request *http.Request) (interface{}, error) {
vb, err := GetVultureBackend(request)
if err != nil {
return nil, err
}
vars := mux.Vars(request)
pipelineString, ok := vars["pipeline"]
if !ok {
return nil, errors.New("pipeline field not specified")
}
var pipeline []interface{}
if err := json.Unmarshal([]byte(pipelineString), &pipeline); err != nil {
return nil, err
}
return vb.GetAggregatePipelineResult(pipeline)
}
func getDocumentByQueryAndIndex(w http.ResponseWriter, request *http.Request) (interface{}, error) { func getDocumentByQueryAndIndex(w http.ResponseWriter, request *http.Request) (interface{}, error) {
vb, err := GetVultureBackend(request) vb, err := GetVultureBackend(request)
if err != nil { if err != nil {
@@ -305,6 +346,28 @@ func getDocumentByQueryAndIndex(w http.ResponseWriter, request *http.Request) (i
return vb.GetDocumentByIndex(index, query) return vb.GetDocumentByIndex(index, query)
} }
func getStatsFromPipeline(w http.ResponseWriter, request *http.Request) (interface{}, error) {
vb, err := GetVultureBackend(request)
if err != nil {
return nil, err
}
vars := mux.Vars(request)
pipelineString, ok := vars["pipeline"]
if !ok {
return nil, errors.New("pipeline field not specified")
}
var pipeline []interface{}
if err := json.Unmarshal([]byte(pipelineString), &pipeline); err != nil {
return nil, err
}
key, ok := vars["key"]
if !ok {
return nil, errors.New("key field not specified")
}
return vb.GetStatFromPipeline(pipeline, key)
}
func getStats(w http.ResponseWriter, request *http.Request) (interface{}, error) { func getStats(w http.ResponseWriter, request *http.Request) (interface{}, error) {
vb, err := GetVultureBackend(request) vb, err := GetVultureBackend(request)
if err != nil { if err != nil {
@@ -319,7 +382,6 @@ func getStats(w http.ResponseWriter, request *http.Request) (interface{}, error)
return nil, errors.New("invalid json") return nil, errors.New("invalid json")
} }
} }
fmt.Println(">>>", queryString)
vars := mux.Vars(request) vars := mux.Vars(request)
key, ok := vars["key"] key, ok := vars["key"]
@@ -374,3 +436,49 @@ func getHistogram(w http.ResponseWriter, request *http.Request) (interface{}, er
return vb.GetHistogram(query, key, min, max, numberOfBins) return vb.GetHistogram(query, key, min, max, numberOfBins)
} }
func getHistogramFromPipeline(w http.ResponseWriter, request *http.Request) (interface{}, error) {
vb, err := GetVultureBackend(request)
vars := mux.Vars(request)
smin, ok := vars["min"]
if !ok {
return nil, errors.New("min field not specified")
}
min, err := strconv.ParseFloat(smin, 64)
if err != nil {
return nil, errors.New("min value is not an float64")
}
smax, ok := vars["max"]
if !ok {
return nil, errors.New("max field not specified")
}
max, err := strconv.ParseFloat(smax, 64)
if err != nil {
return nil, errors.New("max value is not an float64")
}
sNumberOfBins, ok := vars["number_of_bins"]
if !ok {
return nil, errors.New("number_of_bins field not specified")
}
numberOfBins, err := strconv.Atoi(sNumberOfBins)
if err != nil {
return nil, errors.New("numberOfBins value is not an int")
}
key, ok := vars["key"]
if !ok {
return nil, errors.New("key field not specified")
}
pipelineString, ok := vars["pipeline"]
if !ok {
return nil, errors.New("pipeline field not specified")
}
var pipeline []interface{}
if err := json.Unmarshal([]byte(pipelineString), &pipeline); err != nil {
return nil, err
}
return vb.GetHistogramFromPipeline(pipeline, key, min, max, numberOfBins)
}
+26 -14
View File
@@ -9,24 +9,36 @@
"license": "MIT", "license": "MIT",
"private": true, "private": true,
"dependencies": { "dependencies": {
"underscore": "~1.6.0", "underscore": "~1.8.3",
"foundation": "~5.2.1", "foundation": "~5.5.2",
"angular": "1.3.x", "angular": "~1.3.15",
"angular-route": "~1.2.14", "angular-route": "~1.3.15",
"angular-local-storage": "~0.0.1", "angular-local-storage": "~0.2.1",
"qunit": "~1.14.0", "qunit": "~1.18.0",
"zepto": "~1.1.3", "zepto": "~1.1.3",
"fontawesome": "~4.0.3", "fontawesome": "~4.3.0",
"angular-leaflet-directive": "~0.7.7", "angular-leaflet-directive": "~0.8.1",
"leaflet": "~0.7.2", "leaflet": "~0.7.2",
"codemirror": "~3.19.0", "codemirror": "~5.3.0",
"angular-ui-codemirror": "~0.1.1", "angular-ui-codemirror": "~0.3.0",
"spin.js": "~2.0.1", "spin.js": "~2.1.0",
"angular-spinner": "~0.4.0", "angular-spinner": "~0.6.1",
"highcharts-ng": "~0.0.6", "highcharts-ng": "~0.0.6",
"highcharts": "~3.0.7" "highcharts": "~4.1.5"
}, },
"resolutions": { "resolutions": {
"angular": "1.3.x" "angular": "~1.3.15",
"underscore": "~1.8.3",
"foundation": "~5.5.2",
"angular-route": "~1.3.15",
"angular-local-storage": "~0.2.1",
"qunit": "~1.18.0",
"fontawesome": "~4.3.0",
"angular-leaflet-directive": "~0.8.1",
"codemirror": "~5.3.0",
"angular-ui-codemirror": "~0.3.0",
"spin.js": "~2.1.0",
"angular-spinner": "~0.6.1",
"highcharts": "~4.1.5"
} }
} }
+71 -22
View File
@@ -1,56 +1,105 @@
root = this; root = this;
root.controllers.controller('documentCtrl', ['$scope', '$routeParams', '$location', 'util', ($scope, $routeParams, $location, util) -> root.controllers.controller('documentCtrl', ['$scope', '$routeParams', '$location', 'util', 'documentCtrlUtil', ($scope, $routeParams, $location, util, documentCtrlUtil) ->
$scope.initScopeVariables = ->
$scope.idx = $routeParams.idx $scope.idx = $routeParams.idx
$scope.doc = {} $scope.doc = {}
$scope.idx = Number($routeParams.idx) or 1 $scope.idx = Number($routeParams.idx) or 1
$scope.meta = {} $scope.meta = {}
$scope.query = $routeParams.query or "" $scope.query = $routeParams.query or $routeParams.pipeline or ""
$scope.notfound = false $scope.notfound = false
$scope.error = "" $scope.error = ""
$scope.codeMirrorOptions = $scope.codeMirrorOptions =
lineWrapping : true lineWrapping : true
lineNumbers: true lineNumbers: true
mode: 'text/typescript' mode: 'text/javascript'
url = "/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }" $scope.init = ->
if $routeParams.query $scope.initScopeVariables()
url = "#{url}/query/#{$routeParams.query}" url = documentCtrlUtil.getUrl($routeParams)
if $routeParams.idx
url = "#{ url }/idx/#{$routeParams.idx - 1}"
else if $routeParams.id
url = "#{ url }/_id/#{$routeParams.id}"
$scope.raw_url = url $scope.raw_url = url
util.get(url).then((res) ->
if $scope.isDocumentFromPipeline()
promise = documentCtrlUtil.getPipeline(url, $scope.idx - 1)
else
promise = util.get(url)
promise.then((res) ->
$scope.doc = res.data.document $scope.doc = res.data.document
$scope.meta = res.data.meta $scope.meta = res.data.meta
$scope.total = res.data.meta.count + $scope.idx - 1
$scope.enlapsed_time = res.data.enlapsed_time $scope.enlapsed_time = res.data.enlapsed_time
).catch((res) -> ).catch((res) ->
if res.data.error == "not found"
$scope.notfound = true
else
$scope.error = res.data.error $scope.error = res.data.error
) )
$scope.isDocumentFromPipeline = ->
return $routeParams.pipeline
$scope.search = (query) -> $scope.search = (query) ->
if query == "" if query == ""
return "" return ""
$location.path "/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/1/query/#{query}" $location.path "/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/1/query/#{query}"
$scope.executePipeline = (pipeline) ->
if pipeline == ""
return
$location.path "/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/1/pipeline/#{pipeline}"
$scope.previousDocumentUrl = () -> $scope.previousDocumentUrl = () ->
url = "#/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$scope.idx - 1}" return '#' + $location.path().replace(/\/idx\/\d+/, "/idx/#{$scope.idx - 1}")
if $routeParams.query
url = "#{url}/query/#{$routeParams.query}"
return url
$scope.nextDocumentUrl = () -> $scope.nextDocumentUrl = () ->
url = "#/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$scope.idx + 1}" return '#' + $location.path().replace(/\/idx\/\d+/, "/idx/#{$scope.idx + 1}")
if $routeParams.query
url = "#{url}/query/#{$routeParams.query}"
return url
$scope.hasPreviousDocument = () -> $scope.hasPreviousDocument = () ->
$scope.idx > 1 $scope.idx > 1
$scope.hasNextDocument = ->
$scope.total > 0 and $scope.idx < $scope.total
$scope.init()
]) ])
root = this
root.services.factory('documentCtrlUtil', ['util', (util)->
service =
_cache: {}
getUrl: ($routeParams) ->
url = "/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }"
if $routeParams.query
url = "#{url}/query/#{$routeParams.query}"
else if $routeParams.pipeline
return "#{url}/pipeline/#{$routeParams.pipeline}"
if $routeParams.idx
url = "#{ url }/idx/#{$routeParams.idx - 1}"
else if $routeParams.id
url = "#{ url }/_id/#{$routeParams.id}"
return url
wrapPipelinePromise: (promise, idx) ->
return promise.then (res) ->
ret =
data:
document: res.data.document[idx]
meta:
count: res.data.document.length - idx
enlapsed_time: res.data.enlapsed_time
return promise
getPipeline: (url, idx) ->
if url in _.keys(service._cache)
return service.wrapPipelinePromise(service._cache[url], idx)
service._cache[url] = util.get(url)
return service.getPipeline(url, idx)
return service
]);
+6 -4
View File
@@ -22,12 +22,15 @@ getAxisLabel = (min, stepSize, numberOfBins) ->
root.controllers.controller('keyStatCtrl', ['$scope', '$routeParams', '$location', 'util', ($scope, $routeParams, $location, util) -> root.controllers.controller('keyStatCtrl', ['$scope', '$routeParams', '$location', 'util', ($scope, $routeParams, $location, util) ->
$scope.name = $routeParams.key $scope.name = $routeParams.key
$scope.stats = [] $scope.stats = []
$scope.queryObject = $routeParams.query or '{}' $scope.queryObject = $routeParams.query or $routeParams.pipeline or '{}'
base_url = "/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/query/#{$scope.queryObject}" base_url = "/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }"
if $routeParams.pipeline
base_url = "#{base_url}/pipeline/#{$routeParams.pipeline}"
else
base_url = "#{base_url}/query/#{$scope.queryObject}"
stat_url = "#{ base_url }/stats/#{$routeParams.key}" stat_url = "#{ base_url }/stats/#{$routeParams.key}"
$scope.alert=alert
$scope.chartConfig = $scope.chartConfig =
options: options:
@@ -70,4 +73,3 @@ root.controllers.controller('keyStatCtrl', ['$scope', '$routeParams', '$location
) )
]) ])
+2 -2
View File
@@ -10,11 +10,11 @@ root.controllers.controller('topBarCtrl', ['$scope', '$location', ($scope, $loca
$scope.$on('$routeChangeSuccess', (next, current) -> $scope.$on('$routeChangeSuccess', (next, current) ->
$scope.path = _.filter($location.path().split("/"), (s) -> s) $scope.path = _.filter($location.path().split("/"), (s) -> s)
if $scope.path[5] == "query" if $scope.path[5] in ["query", "pipeline"]
$scope.path = joinArray($scope.path[0...6], $scope.path[7...-1]) $scope.path = joinArray($scope.path[0...6], $scope.path[7...-1])
$scope.getHistoryUrl = (idx) -> $scope.getHistoryUrl = (idx) ->
ret = "#/" + ($scope.path[i] for i in [0...idx]).join("/") ret = "#/" + ($scope.path[i] for i in [0...idx]).join("/")
if _.indexOf(["idx", "geojson", "query", "_id", "field", "all", "stats"], $scope.path[idx]) != -1 if _.indexOf(["idx", "geojson", "query", "_id", "field", "all", "stats", "pipeline"], $scope.path[idx]) != -1
return "" return ""
if idx == 0 if idx == 0
ret += "servers" ret += "servers"
+3
View File
@@ -26,6 +26,8 @@ getStatsUrl = (keyName, routeParams)->
ret = "#/#{r.server}/#{r.database}/#{r.collection}/stats/#{ keyName }" ret = "#/#{r.server}/#{r.database}/#{r.collection}/stats/#{ keyName }"
if r.query if r.query
ret = "#{ ret }/query/#{ r.query }" ret = "#{ ret }/query/#{ r.query }"
else if r.pipeline
ret = "#{ret}/pipeline/#{r.pipeline}"
return ret return ret
directive = () -> directive = () ->
@@ -39,6 +41,7 @@ directive = () ->
value: "=" value: "="
indexes: "=" indexes: "="
controller:["$scope", "$routeParams", ($scope, $routeParams) -> controller:["$scope", "$routeParams", ($scope, $routeParams) ->
$scope.indexes = $scope.indexes or []
$scope.canMakeHistogram = _.isNumber($scope.value) $scope.canMakeHistogram = _.isNumber($scope.value)
$scope.statsUrl = getStatsUrl($scope.keyName, $routeParams) $scope.statsUrl = getStatsUrl($scope.keyName, $routeParams)
$scope.hasIndexDesc = hasIndexDesc($scope.keyName, $scope.indexes) $scope.hasIndexDesc = hasIndexDesc($scope.keyName, $scope.indexes)
+19 -6
View File
@@ -69,13 +69,15 @@ directive = () ->
restrict: 'E', restrict: 'E',
scope: scope:
query: "@" query: "@"
callback: "=" querycallback: "="
pipelinecallback: "="
controller:["$scope", "$routeParams", ($scope, $routeParams) -> controller:["$scope", "$routeParams", ($scope, $routeParams) ->
$scope.bigInput = false $scope.bigInput = false
$scope.newQuery = $scope.query or "" $scope.newQuery = $scope.query or ""
$scope.codeMirrorOptions = $scope.codeMirrorOptions =
lineWrapping : true lineWrapping : true
lineNumbers: true lineNumbers: true
indentUnit: 4
mode: 'text/typescript' mode: 'text/typescript'
$scope.parseQueryToJSON = (query) -> $scope.parseQueryToJSON = (query) ->
@@ -88,15 +90,26 @@ directive = () ->
$scope.error $scope.error
return "" return ""
$scope.isValidEntry = ()-> $scope.isValidQueryEntry = ()->
query = $scope.parseQueryToJSON($scope.newQuery) query = $scope.parseQueryToJSON($scope.newQuery)
return query != "" return query != "" and not _.isArray(JSON.parse(query))
$scope.callCallback = () -> $scope.isValidPipelineEntry = ()->
query = $scope.parseQueryToJSON($scope.newQuery)
query != "" and _.isArray(JSON.parse(query))
$scope.queryCallback = () ->
if $scope.isValidQueryEntry()
query = $scope.parseQueryToJSON($scope.newQuery) query = $scope.parseQueryToJSON($scope.newQuery)
if query != ""
f = () -> f = () ->
$scope.callback(query) $scope.querycallback(query)
$scope.$eval(f)
$scope.pipelineCallback = () ->
if $scope.isValidPipelineEntry()
query = $scope.parseQueryToJSON($scope.newQuery)
f = () ->
$scope.pipelinecallback(query)
$scope.$eval(f) $scope.$eval(f)
] ]
return directive return directive
+1 -1
View File
@@ -30,7 +30,7 @@ directive = () ->
return "#/#{rp.server}/#{rp.database}/#{rp.collection}/_id/#{s.docId}/field/#{s.key}" return "#/#{rp.server}/#{rp.database}/#{rp.collection}/_id/#{s.docId}/field/#{s.key}"
$scope.isObjectId = (val) -> $scope.isObjectId = (val) ->
objectIdRegexp = /^[01234567890abcdef]{24}$/ objectIdRegexp = /^[01234567890abcdef]{24}$/
return _.isString(val) and objectIdRegexp.test(val) _.isString(val) and objectIdRegexp.test(val)
$scope.getDateFromObjectId = (objectId) -> $scope.getDateFromObjectId = (objectId) ->
util.parseObjectId(objectId).date.toISOString() util.parseObjectId(objectId).date.toISOString()
+2
View File
@@ -6,7 +6,9 @@ root.filters.filter 'escape', () ->
root.filters.filter 'replaceAllCommasBySpaces', () -> root.filters.filter 'replaceAllCommasBySpaces', () ->
return (text) -> return (text) ->
if _.isString(text)
return text.replace(new RegExp(",","g"), " ") return text.replace(new RegExp(",","g"), " ")
return text
root.filters.filter 'niceJSON', () -> root.filters.filter 'niceJSON', () ->
return (obj) -> return (obj) ->
+2 -1
View File
@@ -23,7 +23,9 @@ config(['$routeProvider', ($routeProvider) ->
$routeProvider.when('/:server/:database/:collection/idx/:idx', {templateUrl: 'partials/document.html', controller: 'documentCtrl'}) $routeProvider.when('/:server/:database/:collection/idx/:idx', {templateUrl: 'partials/document.html', controller: 'documentCtrl'})
$routeProvider.when('/:server/:database/:collection/stats/:key', {templateUrl: 'partials/keyStat.html', controller: 'keyStatCtrl'}) $routeProvider.when('/:server/:database/:collection/stats/:key', {templateUrl: 'partials/keyStat.html', controller: 'keyStatCtrl'})
$routeProvider.when('/:server/:database/:collection/stats/:key/query/:query', {templateUrl: 'partials/keyStat.html', controller: 'keyStatCtrl'}) $routeProvider.when('/:server/:database/:collection/stats/:key/query/:query', {templateUrl: 'partials/keyStat.html', controller: 'keyStatCtrl'})
$routeProvider.when('/:server/:database/:collection/stats/:key/pipeline/:pipeline', {templateUrl: 'partials/keyStat.html', controller: 'keyStatCtrl'})
$routeProvider.when('/:server/:database/:collection/idx/:idx/query/:query', {templateUrl: 'partials/document.html', controller: 'documentCtrl'}) $routeProvider.when('/:server/:database/:collection/idx/:idx/query/:query', {templateUrl: 'partials/document.html', controller: 'documentCtrl'})
$routeProvider.when('/:server/:database/:collection/idx/:idx/pipeline/:pipeline', {templateUrl: 'partials/document.html', controller: 'documentCtrl'})
$routeProvider.when('/:server/:database/:collection/idx/all/geojson/:key', {templateUrl: 'partials/geojsonmap.html', controller: 'geojsonmapCtrl'}) $routeProvider.when('/:server/:database/:collection/idx/all/geojson/:key', {templateUrl: 'partials/geojsonmap.html', controller: 'geojsonmapCtrl'})
$routeProvider.when('/:server/:database/:collection/idx/all/geojson/:key/location/:loc', {templateUrl: 'partials/geojsonmap.html', controller: 'geojsonmapCtrl'}) $routeProvider.when('/:server/:database/:collection/idx/all/geojson/:key/location/:loc', {templateUrl: 'partials/geojsonmap.html', controller: 'geojsonmapCtrl'})
@@ -41,4 +43,3 @@ root.filters = angular.module('vulture.filters', [])
root.services = angular.module('vulture.services', []) root.services = angular.module('vulture.services', [])
root.directives = angular.module('vulture.directives', []) root.directives = angular.module('vulture.directives', [])
root.controllers = angular.module('vulture.controllers', []) root.controllers = angular.module('vulture.controllers', [])
+8 -3
View File
@@ -1,15 +1,20 @@
<div> <div>
<div class="row"> <div class="row">
<div class="large-9 columns"> <div class="large-8 columns">
<input type="text" ng-model="newQuery" style="font-family: monospace;font-size: 1.1em;"> <input type="text" ng-model="newQuery" style="font-family: monospace;font-size: 1.1em;">
</div> </div>
<div class="large-3 columns right"> <div class="large-4 columns right">
<ul class="button-group"> <ul class="button-group">
<li> <li>
<a class="button tiny" ng-click="callCallback()" ng-class="{disabled: !isValidEntry() }"> <a class="button tiny" ng-click="queryCallback()" ng-class="{disabled: !isValidQueryEntry() }">
Query <i class="fa fa-thumbs-o-down" style="color:red;" ng-show="getQueryPath()=='' && newQuery != ''"></i> Query <i class="fa fa-thumbs-o-down" style="color:red;" ng-show="getQueryPath()=='' && newQuery != ''"></i>
</a> </a>
</li> </li>
<li>
<a class="button tiny" ng-click="pipelineCallback()" ng-class="{disabled: !isValidPipelineEntry() }">
Pipeline <i class="fa fa-thumbs-o-down" style="color:red;" ng-show="getPipelinePath()=='' && newQuery != ''"></i>
</a>
</li>
<li> <li>
<a class="button tiny" ng-click="newQuery='{}'" ng-class="{disabled: newQuery==''}">Clear</a> <a class="button tiny" ng-click="newQuery='{}'" ng-class="{disabled: newQuery==''}">Clear</a>
</li> </li>
+1 -1
View File
@@ -1,5 +1,5 @@
<span> <span>
<span ng-show="isObjectId"> <span ng-if="isObjectId">
<pre> {{value}} [ {{getDateFromObjectId(value)}} ]</pre> <pre> {{value}} [ {{getDateFromObjectId(value)}} ]</pre>
</span> </span>
<span ng-show="isString"> <span ng-show="isString">
+5 -5
View File
@@ -1,18 +1,18 @@
<vul-search-bar query="{{ query }}" callback="search"></vul-search-bar> <vul-search-bar query="{{ query }}" querycallback="search" pipelinecallback="executePipeline" ></vul-search-bar>
<div class="row"> <div class="row">
<div class="large-12 columns" style="text-align: center;"> <div class="large-12 columns" style="text-align: center;">
<a class="fa fa-chevron-left" ng-href="{{ previousDocumentUrl() }}" ng-show="{{ hasPreviousDocument() }}"></a> <a class="fa fa-chevron-left" ng-href="{{ previousDocumentUrl() }}" ng-show="{{ hasPreviousDocument() }}"></a>
<kbd>{{idx}} / {{ meta.count + idx - 1 || 0 | number | replaceAllCommasBySpaces }}</kbd> <kbd ng-show="total > 0"> {{idx}} / {{ total | number | replaceAllCommasBySpaces }}</kbd>
<a class="fa fa-chevron-right" ng-href="{{ nextDocumentUrl() }}"></a> <a class="fa fa-chevron-right" ng-href="{{ nextDocumentUrl() }}" ng-show="hasNextDocument()"></a>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="large-12 columns" style="text-align: center;" ng-show="!closeAlert && notfound"> <div class="large-12 columns" style="text-align: center;" ng-show="!closeAlert && error">
<div class="alert-box"> <div class="alert-box">
No document Found! {{ error }}
<a class="close" ng-click="closeAlert=true">&times;</a> <a class="close" ng-click="closeAlert=true">&times;</a>
</div> </div>
</div> </div>
+3
View File
@@ -94,9 +94,12 @@ func serveApi(router *mux.Router) {
router.Handle("/{server}/{database}/{collection}/_id/{id}", ApiHandler(getDocumentById)) router.Handle("/{server}/{database}/{collection}/_id/{id}", ApiHandler(getDocumentById))
router.Handle("/{server}/{database}/{collection}/query/{query}/stats/{key}", ApiHandler(getStats)) router.Handle("/{server}/{database}/{collection}/query/{query}/stats/{key}", ApiHandler(getStats))
router.Handle("/{server}/{database}/{collection}/query/{query}/histogram/{key}/{min}/{max}/{number_of_bins}", ApiHandler(getHistogram)) router.Handle("/{server}/{database}/{collection}/query/{query}/histogram/{key}/{min}/{max}/{number_of_bins}", ApiHandler(getHistogram))
router.Handle("/{server}/{database}/{collection}/pipeline/{pipeline}/histogram/{key}/{min}/{max}/{number_of_bins}", ApiHandler(getHistogramFromPipeline))
router.Handle("/{server}/{database}/{collection}/all", ApiHandler(getAllDocuments)) router.Handle("/{server}/{database}/{collection}/all", ApiHandler(getAllDocuments))
router.Handle("/{server}/{database}/{collection}/query/{query}/idx/{index:[0-9]+}", ApiHandler(getDocumentByQueryAndIndex)) router.Handle("/{server}/{database}/{collection}/query/{query}/idx/{index:[0-9]+}", ApiHandler(getDocumentByQueryAndIndex))
router.Handle("/{server}/{database}/{collection}/query/{query}/all", ApiHandler(getAllDocuments)) router.Handle("/{server}/{database}/{collection}/query/{query}/all", ApiHandler(getAllDocuments))
router.Handle("/{server}/{database}/{collection}/pipeline/{pipeline}", ApiHandler(getAggregatePipelireResult))
router.Handle("/{server}/{database}/{collection}/pipeline/{pipeline}/stats/{key}", ApiHandler(getStatsFromPipeline))
} }