diff --git a/api.go b/api.go index 9f6a8ef..453e714 100644 --- a/api.go +++ b/api.go @@ -2,6 +2,7 @@ package main import ( "bitbucket.org/damyot/vulture/shared" + "encoding/json" "errors" "github.com/gorilla/mux" "labix.org/v2/mgo" @@ -17,6 +18,7 @@ type VultureBackend struct { func GetVultureBackend(req *http.Request) (*VultureBackend, error) { vars := mux.Vars(req) + serverURL, ok := vars["server"] if !ok { return nil, errors.New("server field not specified") @@ -83,13 +85,13 @@ func (this *VultureBackend) getMetaData(query *mgo.Query) (map[string]interface{ } -func (this *VultureBackend) GetDocumentByIndex(index int) (map[string]interface{}, error) { +func (this *VultureBackend) GetDocumentByIndex(index int, query interface{}) (map[string]interface{}, error) { doc := make(map[string]interface{}) - query := this.Collection.Find(nil) - if err := query.Skip(index).One(&doc); err != nil { + queryResult := this.Collection.Find(query) + if err := queryResult.Skip(index).One(&doc); err != nil { return nil, err } - return this.wrapDocumentWithMetadata(doc, query) + return this.wrapDocumentWithMetadata(doc, queryResult) } func (this *VultureBackend) wrapDocumentWithMetadata(doc map[string]interface{}, query *mgo.Query) (map[string]interface{}, error) { @@ -136,5 +138,30 @@ func getDocumentByIndex(w http.ResponseWriter, request *http.Request) (interface if err != nil { return nil, errors.New("index field is not an inteder ( " + indexAsString + ")") } - return vb.GetDocumentByIndex(index) + return vb.GetDocumentByIndex(index, nil) +} + +func getDocumentByQueryAndIndex(w http.ResponseWriter, request *http.Request) (interface{}, error) { + vb, err := GetVultureBackend(request) + if err != nil { + return nil, err + } + vars := mux.Vars(request) + indexAsString, ok := vars["index"] + if !ok { + return nil, errors.New("index field not specified") + } + index, err := strconv.Atoi(indexAsString) + if err != nil { + return nil, errors.New("index field is not an inteder ( " + indexAsString + ")") + } + queryString, ok := vars["query"] + if !ok || queryString == "" { + return vb.GetDocumentByIndex(index, nil) + } + query := make(map[string]interface{}) + if err := json.Unmarshal([]byte(queryString), &query); err != nil { + return nil, errors.New("invalid json") + } + return vb.GetDocumentByIndex(index, query) } diff --git a/client/bower.json b/client/bower.json index a4d0d07..cc6e582 100644 --- a/client/bower.json +++ b/client/bower.json @@ -16,6 +16,8 @@ "angular-local-storage": "~0.0.1", "qunit": "~1.14.0", "zepto": "~1.1.3", - "fontawesome": "~4.0.3" + "fontawesome": "~4.0.3", + "angular-leaflet-directive": "~0.7.7", + "leaflet": "~0.7.2" } } diff --git a/client/coffee/controllers/documentCtrl.coffee b/client/coffee/controllers/documentCtrl.coffee index 20df927..e2bc423 100644 --- a/client/coffee/controllers/documentCtrl.coffee +++ b/client/coffee/controllers/documentCtrl.coffee @@ -1,24 +1,112 @@ root = this; +mongoJsToMongoJSON = (queryString) -> + ObjectId = (id) -> + return {"$oid": id} + + DBRef = (name, id) -> + return {"$ref": name, "$id": id} + + Timestamp = (t, i) -> + return {"t": t, "i": i} + NumberLong = (number) -> + return { "$numberLong": String(number)} + MaxKey = { "$maxKey": 1 } + MinKey = { "$minKey": 1 } + + query = "" + eval("query = " + queryString) + fixQuery = (query) -> + if _.isArray(query) + for v, i in query + query[i] = fixQuery(v) + return query + if _.isRegExp(query) + options = "" + for k, option of {multiline: "m", global: "g", ignoreCase: "i"} + if query[k] + options += option + return { "$regex": query.source, "$options": options } + if _.isDate(query) + return { "$date": query.getTime()} + if query == undefined + return { "$undefined": true } + if _.isObject(query) + for k, v of query + query[k] = fixQuery(v) + return query + return query + + return fixQuery(query) + +testmongoJsToMongoJSON = () -> + assertEqual = (a, b) -> + if not _.isEqual(a, b) + console.log("error! ", a, " != ", b) + s = """{a: 89}""" + assertEqual(mongoJsToMongoJSON(s), {a : 89}) + s = """{a: [1,3,{reg: /allo/gim}]}""" + assertEqual(mongoJsToMongoJSON(s), {a: [1,3, {reg: { "$regex":"allo", "$options": "mgi" }}]}) + s = """{a: [ + 1, + 3, + {reg: + [/allo/gim, + new Date("2014-05-08T21:52:24.320Z") + ] + } + ] + }""" + assertEqual(mongoJsToMongoJSON(s), {a: [1,3, {reg: [{ "$regex":"allo", "$options": "mgi" }, {"$date": 1399585944320}]}]}) + + +testmongoJsToMongoJSON() root.controllers.controller('documentCtrl', ['$scope', '$routeParams', '$http', ($scope, $routeParams, $http) -> $scope.idx = $routeParams.idx $scope.doc = {} $scope.idx = Number($routeParams.idx) $scope.meta = {} - $http.get("/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$routeParams.idx - 1}").then((res) -> + $scope.newQuery = $routeParams.query or "" + + url = "/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }" + if $routeParams.query + url = "#{url}/query/#{$routeParams.query}" + url = "#{ url }/idx/#{$routeParams.idx - 1}" + + $http.get(url).then((res) -> $scope.doc = res.data.document $scope.meta = res.data.meta ) + $scope.parseQueryToJSON = (query) -> + try + ret = JSON.stringify(mongoJsToMongoJSON(query)) + $scope.error = "" + return ret + catch error + $scope.error + return "" + + $scope.getQueryPath = ()-> + query = $scope.parseQueryToJSON($scope.newQuery) + if query == "" + return "" + return "#/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/1/query/#{query}" + $scope.previousDocumentUrl = () -> - "#/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$scope.idx - 1}" + url = "#/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$scope.idx - 1}" + if $routeParams.query + url = "/query/#{$routeParams.query}" + return url $scope.nextDocumentUrl = () -> - "#/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$scope.idx + 1}" + url = "#/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$scope.idx + 1}" + if $routeParams.query + url = "/query/#{$routeParams.query}" + return url $scope.hasPreviousDocument = () -> $scope.idx > 1 +]) - -]) \ No newline at end of file diff --git a/client/coffee/controllers/geojsonmapCtrl.coffee b/client/coffee/controllers/geojsonmapCtrl.coffee new file mode 100644 index 0000000..227f82c --- /dev/null +++ b/client/coffee/controllers/geojsonmapCtrl.coffee @@ -0,0 +1,40 @@ +root = this; + + +root.controllers.controller('geojsonmapCtrl', ['$scope', '$routeParams', '$http', ($scope, $routeParams, $http) -> + $scope.geojson = {} + $scope.geojsonData = {} + $scope.idx = Number($routeParams.idx) + $scope.center = # experimental point around Québec + lat: 46.1227 + lng: -72.6169 + zoom: 7 + + + $http.get("/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$routeParams.idx - 1}").then((res) -> + $scope.geojson = res.data.document[$routeParams.key] + if not $scope.geojson.properties + $scope.geojson.properties = {} + $scope.geojson.properties["_vulture_url_link"] = "" + $scope.meta = res.data.meta + $scope.geojsonData = + data: $scope.geojson + style: undefined + resetStyleOnMouseout: true + ) + + $scope.previousDocumentUrl = () -> + "#/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$scope.idx - 1}/geojson/#{$routeParams.key}" + + $scope.nextDocumentUrl = () -> + "#/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }/idx/#{$scope.idx + 1}/geojson/#{$routeParams.key}" + + $scope.hasPreviousDocument = () -> + $scope.idx > 1 + + $scope.$on "leafletDirectiveMap.geojsonClick", (ev, featureSelected, leafletEvent) -> + a = 90 + b = 89 + +]) + diff --git a/client/coffee/controllers/topBarCtrl.coffee b/client/coffee/controllers/topBarCtrl.coffee index 4fe1719..a3d2dd3 100644 --- a/client/coffee/controllers/topBarCtrl.coffee +++ b/client/coffee/controllers/topBarCtrl.coffee @@ -3,9 +3,11 @@ root = this; root.controllers.controller('topBarCtrl', ['$scope', '$location', ($scope, $location) -> $scope.$on('$routeChangeSuccess', (next, current) -> $scope.path = _.filter($location.path().split("/"), (s) -> s) + if $scope.path[5] == "query" + $scope.path = $scope.path[0...-1] $scope.getHistoryUrl = (idx) -> ret = "#/" + ($scope.path[i] for i in [0...idx]).join("/") - if $scope.path[idx] == "idx" + if _.indexOf(["idx", "geojson"], $scope.path[idx]) != -1 return "" if idx == 0 ret += "servers" @@ -13,6 +15,8 @@ root.controllers.controller('topBarCtrl', ['$scope', '$location', ($scope, $loca ret += "/databases" else if idx == 2 ret += "/collections" + else if idx == 4 + ret += "/#{ $scope.path[4] }" return ret ) ]) \ No newline at end of file diff --git a/client/coffee/directives/key.coffee b/client/coffee/directives/key.coffee index 8f235b2..1db5877 100644 --- a/client/coffee/directives/key.coffee +++ b/client/coffee/directives/key.coffee @@ -1,4 +1,20 @@ +hasIndex = (keyName, indexes) -> + for index in indexes + if _.indexOf(index.keys, keyName) != -1 + return true + return false + +hasIndexAsc = (keyName, indexes) -> + hasIndex(keyName, indexes) + +hasIndexDesc = (keyName, indexes) -> + hasIndex("-#{keyName}", indexes) + + +has2dIndex = (keyName, indexes) -> + hasIndex("$2dsphere:#{keyName}", indexes) + directive = () -> directive = templateUrl: "/partials/directives/key.html" @@ -6,12 +22,17 @@ directive = () -> transclude: false, restrict: 'E', scope: - value: "=" - controller:["$scope", ($scope) -> - $scope.hasIndexDown = false - $scope.hasIndexUp = false - $scope.hasIndex = false - $scope.has2DIndexUp = false + keyName: "=" + indexes: "=" + controller:["$scope", "$routeParams", ($scope, $routeParams) -> + $scope.hasIndexDesc = hasIndexDesc($scope.keyName, $scope.indexes) + $scope.hasIndexAsc = hasIndexAsc($scope.keyName, $scope.indexes) + $scope.has2DIndex = has2dIndex($scope.keyName, $scope.indexes) + $scope.showTools = false + $scope.hasIndex = $scope.hasIndexDown or $scope.hasIndexUp or $scope.has2DIndex + $scope.getGeoJsonUrl = () -> + r = $routeParams + return "#" + "/#{r.server}/#{r.database}/#{r.collection}/idx/#{r.idx}/geojson/#{$scope.keyName}" ] return directive diff --git a/client/coffee/vultureApp.coffee b/client/coffee/vultureApp.coffee index 98bdc15..6f4ae21 100644 --- a/client/coffee/vultureApp.coffee +++ b/client/coffee/vultureApp.coffee @@ -3,6 +3,7 @@ root = this; angular.module('vulture', [ 'ngRoute' + 'leaflet-directive' 'vulture.filters' 'vulture.services' 'vulture.directives' @@ -14,6 +15,8 @@ config(['$routeProvider', ($routeProvider) -> $routeProvider.when('/:server/databases', {templateUrl: 'partials/databases.html', controller: 'databasesCtrl'}) $routeProvider.when('/:server/:database/collections', {templateUrl: 'partials/collections.html', controller: 'collectionsCtrl'}) $routeProvider.when('/:server/:database/:collection/idx/:idx', {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/geojson/:key', {templateUrl: 'partials/geojsonmap.html', controller: 'geojsonmapCtrl'}) $routeProvider.otherwise({redirectTo: '/servers'}) ]) diff --git a/client/index.html b/client/index.html index f8c666c..6914de9 100644 --- a/client/index.html +++ b/client/index.html @@ -10,6 +10,7 @@ + @@ -37,10 +38,13 @@
+ + + @@ -49,6 +53,7 @@ + diff --git a/client/partials/directives/key.html b/client/partials/directives/key.html index 4f6f506..ae23d66 100644 --- a/client/partials/directives/key.html +++ b/client/partials/directives/key.html @@ -1,10 +1,15 @@ - - {{value}} - - - - - + + {{keyName}} + + + + + + + + + + \ No newline at end of file diff --git a/client/partials/document.html b/client/partials/document.html index cc58ff3..9adbc55 100644 --- a/client/partials/document.html +++ b/client/partials/document.html @@ -1,10 +1,10 @@