started stats on fields.
This commit is contained in:
1 parent
2add8284e6
commit
37209f6779
11 files changed
+122
-11
No files matched your search
@@ -131,6 +131,33 @@ func (this *VultureBackend) wrapDocumentWithMetadata(doc interface{}, query *mgo
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *VultureBackend) GetStat(key string) (interface{}, error) {
|
||||||
|
s := NewStatAggregator()
|
||||||
|
query := bson.M{key: bson.M{"$exists": true}}
|
||||||
|
iter := this.Collection.Find(query).Select(bson.M{key: 1}).Iter()
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
|
in := make(chan float64)
|
||||||
|
out := make(chan *Stats)
|
||||||
|
go s.AddStats(in, out)
|
||||||
|
|
||||||
|
for iter.Next(&result) {
|
||||||
|
|
||||||
|
if val, ok := result[key]; ok {
|
||||||
|
if fval, isFloat := val.(float64); isFloat {
|
||||||
|
in <- fval
|
||||||
|
} else if ival, isInt := val.(int); isInt {
|
||||||
|
in <- float64(ival)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(in)
|
||||||
|
if err := iter.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return <-out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (this *VultureBackend) GetAllDocuments(query interface{}) (interface{}, error) {
|
func (this *VultureBackend) GetAllDocuments(query interface{}) (interface{}, error) {
|
||||||
var result []map[string]interface{}
|
var result []map[string]interface{}
|
||||||
|
|
||||||
@@ -241,3 +268,17 @@ func getDocumentByQueryAndIndex(w http.ResponseWriter, request *http.Request) (i
|
|||||||
}
|
}
|
||||||
return vb.GetDocumentByIndex(index, query)
|
return vb.GetDocumentByIndex(index, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getStats(w http.ResponseWriter, request *http.Request) (interface{}, error) {
|
||||||
|
vb, err := GetVultureBackend(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
key, ok := vars["key"]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("key field not specified")
|
||||||
|
}
|
||||||
|
return vb.GetStat(key)
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
root = this;
|
||||||
|
|
||||||
|
humanValueOfStatisticalAbreviation =
|
||||||
|
mean: "mean"
|
||||||
|
std: "standard deviation"
|
||||||
|
var: "variance"
|
||||||
|
|
||||||
|
|
||||||
|
root.controllers.controller('keyStatCtrl', ['$scope', '$routeParams', '$http', ($scope, $routeParams, $http) ->
|
||||||
|
$scope.name = "didier!"
|
||||||
|
$scope.stats = []
|
||||||
|
|
||||||
|
url = "/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }"
|
||||||
|
url = "#{ url }/stats/#{$routeParams.key}"
|
||||||
|
|
||||||
|
$http.get(url).then((res) ->
|
||||||
|
for key, val of res.data
|
||||||
|
$scope.stats.push({name: humanValueOfStatisticalAbreviation[key], value: val })
|
||||||
|
)
|
||||||
|
|
||||||
|
])
|
||||||
|
|
||||||
@@ -15,6 +15,10 @@ hasIndexDesc = (keyName, indexes) ->
|
|||||||
has2dIndex = (keyName, indexes) ->
|
has2dIndex = (keyName, indexes) ->
|
||||||
hasIndex("$2dsphere:#{keyName}", indexes)
|
hasIndex("$2dsphere:#{keyName}", indexes)
|
||||||
|
|
||||||
|
getStatsUrl = (keyName, routeParams)->
|
||||||
|
r = routeParams
|
||||||
|
"#/#{r.server}/#{r.database}/#{r.collection}/stats/#{ keyName }"
|
||||||
|
|
||||||
directive = () ->
|
directive = () ->
|
||||||
directive =
|
directive =
|
||||||
templateUrl: "/partials/directives/key.html"
|
templateUrl: "/partials/directives/key.html"
|
||||||
@@ -27,6 +31,7 @@ directive = () ->
|
|||||||
indexes: "="
|
indexes: "="
|
||||||
controller:["$scope", "$routeParams", ($scope, $routeParams) ->
|
controller:["$scope", "$routeParams", ($scope, $routeParams) ->
|
||||||
$scope.canMakeHistogram = _.isNumber($scope.value)
|
$scope.canMakeHistogram = _.isNumber($scope.value)
|
||||||
|
$scope.statsUrl = getStatsUrl($scope.keyName, $routeParams)
|
||||||
$scope.hasIndexDesc = hasIndexDesc($scope.keyName, $scope.indexes)
|
$scope.hasIndexDesc = hasIndexDesc($scope.keyName, $scope.indexes)
|
||||||
$scope.hasIndexAsc = hasIndexAsc($scope.keyName, $scope.indexes)
|
$scope.hasIndexAsc = hasIndexAsc($scope.keyName, $scope.indexes)
|
||||||
$scope.has2DIndex = has2dIndex($scope.keyName, $scope.indexes)
|
$scope.has2DIndex = has2dIndex($scope.keyName, $scope.indexes)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ config(['$routeProvider', ($routeProvider) ->
|
|||||||
$routeProvider.when('/:server/:database/:collection/_id/:id', {templateUrl: 'partials/document.html', controller: 'documentCtrl'})
|
$routeProvider.when('/:server/:database/:collection/_id/:id', {templateUrl: 'partials/document.html', controller: 'documentCtrl'})
|
||||||
$routeProvider.when('/:server/:database/:collection/_id/:id/field/:field', {templateUrl: 'partials/field.html', controller: 'fieldCtrl'})
|
$routeProvider.when('/:server/:database/:collection/_id/:id/field/:field', {templateUrl: 'partials/field.html', controller: 'fieldCtrl'})
|
||||||
$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/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/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/:idx/geojson/:key', {templateUrl: 'partials/geojsonmap.html', controller: 'geojsonmapCtrl'})
|
$routeProvider.when('/:server/:database/:collection/idx/:idx/geojson/:key', {templateUrl: 'partials/geojsonmap.html', controller: 'geojsonmapCtrl'})
|
||||||
|
|||||||
+2
-1
@@ -31,7 +31,7 @@
|
|||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
<ul class="breadcrumbs">
|
<ul class="breadcrumbs">
|
||||||
<li ng-repeat="p in path" ng-class="{current: $last, unavailable: getHistoryUrl($index)== ''}">
|
<li ng-repeat="p in path" ng-class="{current: $last, unavailable: getHistoryUrl($index)== ''}">
|
||||||
<a ng-href="{{getHistoryUrl($index)}}"> {{ p }} </a>
|
<a ng-href="{{getHistoryUrl($index)}}" ng-bind="p"> </a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,6 +60,7 @@
|
|||||||
<script src="js/controllers/documentCtrl.js"></script>
|
<script src="js/controllers/documentCtrl.js"></script>
|
||||||
<script src="js/controllers/geojsonmapCtrl.js"></script>
|
<script src="js/controllers/geojsonmapCtrl.js"></script>
|
||||||
<script src="js/controllers/fieldCtrl.js"></script>
|
<script src="js/controllers/fieldCtrl.js"></script>
|
||||||
|
<script src="js/controllers/keyStatCtrl.js"></script>
|
||||||
|
|
||||||
<script src="js/directives/key.js"></script>
|
<script src="js/directives/key.js"></script>
|
||||||
<script src="js/directives/value.js"></script>
|
<script src="js/directives/value.js"></script>
|
||||||
|
|||||||
@@ -3,14 +3,11 @@
|
|||||||
|
|
||||||
<span ng-style="{visibility: showTools ? 'inline': 'hidden'}">
|
<span ng-style="{visibility: showTools ? 'inline': 'hidden'}">
|
||||||
<!-- place to but some hoover information-->
|
<!-- place to but some hoover information-->
|
||||||
<i class="fa fa-signal" ng-show="canMakeHistogram"></i>
|
<a ng-href="{{ statsUrl }}"><i class="fa fa-signal" ng-show="canMakeHistogram"></i></a>
|
||||||
</span>
|
</span>
|
||||||
<span class="right" style="color:#008CBA;">
|
<span class="right" style="color:#008CBA;">
|
||||||
<i class="fa fa-sort-numeric-asc" ng-show="hasIndexAsc"></i>
|
<i class="fa fa-sort-numeric-asc" ng-show="hasIndexAsc"></i>
|
||||||
<i class="fa fa-sort-numeric-desc" ng-show="hasIndexDesc"></i>
|
<i class="fa fa-sort-numeric-desc" ng-show="hasIndexDesc"></i>
|
||||||
<a class="fa fa-globe" ng-show="has2DIndex" ng-href="{{ getGeoJsonUrl() }}" target="_blank"></a>
|
<a class="fa fa-globe" ng-show="has2DIndex" ng-href="{{ getGeoJsonUrl() }}" target="_blank"></a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="columns large-12">
|
||||||
|
<ul class="pricing-table">
|
||||||
|
<li class="title" ng-bind="name"></li>
|
||||||
|
<li class="bullet-item" ng-repeat="stat in stats">
|
||||||
|
<code>{{stat.name}} : {{stat.value | number | replaceAllCommasBySpaces }}</code>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -91,6 +91,7 @@ func serveApi(router *mux.Router) {
|
|||||||
router.Handle("/{server}/{database}/collections", ApiHandler(getAvailableCollections))
|
router.Handle("/{server}/{database}/collections", ApiHandler(getAvailableCollections))
|
||||||
router.Handle("/{server}/{database}/{collection}/idx/{index:[0-9]+}", ApiHandler(getDocumentByIndex))
|
router.Handle("/{server}/{database}/{collection}/idx/{index:[0-9]+}", ApiHandler(getDocumentByIndex))
|
||||||
router.Handle("/{server}/{database}/{collection}/_id/{id}", ApiHandler(getDocumentById))
|
router.Handle("/{server}/{database}/{collection}/_id/{id}", ApiHandler(getDocumentById))
|
||||||
|
router.Handle("/{server}/{database}/{collection}/stats/{key}", ApiHandler(getStats))
|
||||||
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))
|
||||||
|
|||||||
+3
-3
@@ -18,6 +18,6 @@ MongoServers:
|
|||||||
-
|
-
|
||||||
name: localhost
|
name: localhost
|
||||||
url: localhost:27017
|
url: localhost:27017
|
||||||
# -
|
-
|
||||||
# name: example
|
name: mongolab
|
||||||
# url: mongodb://username:password@ds90210.mongolab.com:52652/database_name
|
url: mongodb://ustraca:9OApiiV9En85bBFGfVyYKH4C@ds049288.mongolab.com:49288/ustraca
|
||||||
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
@@ -10,6 +11,12 @@ const initialLength int = 1024
|
|||||||
const verySmall float64 = -100000
|
const verySmall float64 = -100000
|
||||||
const veryBig float64 = 100000
|
const veryBig float64 = 100000
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
Mean float64 `json:"mean"`
|
||||||
|
Var float64 `json:"var"`
|
||||||
|
Std float64 `json:"std"`
|
||||||
|
}
|
||||||
|
|
||||||
type StatAggregator struct {
|
type StatAggregator struct {
|
||||||
sum float64
|
sum float64
|
||||||
sumOfTheSquares float64
|
sumOfTheSquares float64
|
||||||
@@ -19,8 +26,6 @@ type StatAggregator struct {
|
|||||||
max float64
|
max float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stats struct{}
|
|
||||||
|
|
||||||
func NewStatAggregator() *StatAggregator {
|
func NewStatAggregator() *StatAggregator {
|
||||||
return &StatAggregator{0, 0, 0, make([]float64, initialLength), veryBig, verySmall}
|
return &StatAggregator{0, 0, 0, make([]float64, initialLength), veryBig, verySmall}
|
||||||
}
|
}
|
||||||
@@ -37,5 +42,15 @@ func (this *StatAggregator) AddStats(dataChannel chan float64, output chan *Stat
|
|||||||
this.max = datum
|
this.max = datum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output <- nil
|
output <- this.getFinalStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *StatAggregator) getFinalStats() *Stats {
|
||||||
|
stats := new(Stats)
|
||||||
|
stats.Mean = this.sum / float64(this.n)
|
||||||
|
stats.Var = this.sumOfTheSquares/float64(this.n) - stats.Mean*stats.Mean
|
||||||
|
stats.Std = math.Sqrt(stats.Var)
|
||||||
|
|
||||||
|
return stats
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -33,3 +33,20 @@ func TestAddStatsBasic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetFinalStats(t *testing.T) {
|
||||||
|
s := NewStatAggregator()
|
||||||
|
s.sumOfTheSquares = 5000
|
||||||
|
s.sum = 200
|
||||||
|
s.n = 10
|
||||||
|
stats := s.getFinalStats()
|
||||||
|
if stats.Mean != 20. {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if stats.Var != 100. {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if stats.Std != 10. {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user