started stats on fields.

This commit is contained in:
didier.amyot committed 2014-05-20 09:13:24 -04:00
1 parent 2add8284e6
commit 37209f6779
11 files changed
+122 -11

No files matched your search

+41
View File
@@ -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 })
)
])
+5
View File
@@ -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)
+1
View File
@@ -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
View File
@@ -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>
+1 -4
View File
@@ -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>
+11
View File
@@ -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>
+1
View File
@@ -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
View File
@@ -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
+18 -3
View File
@@ -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
} }
+17
View File
@@ -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()
}
}