0
0
Fork 0

added additional http handlers to cover most functionality

This commit is contained in:
Marty Schoch 2014-08-25 09:08:27 -04:00
parent 34afb0929e
commit d164b017b2
11 changed files with 526 additions and 3 deletions

49
http/doc_count.go Normal file
View File

@ -0,0 +1,49 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package http
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
type DocCountHandler struct {
defaultIndexName string
}
func NewDocCountHandler(defaultIndexName string) *DocCountHandler {
return &DocCountHandler{
defaultIndexName: defaultIndexName,
}
}
func (h *DocCountHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// find the index to operate on
indexName := mux.Vars(req)["indexName"]
if indexName == "" {
indexName = h.defaultIndexName
}
index := IndexByName(indexName)
if index == nil {
showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404)
return
}
docCount := index.DocCount()
rv := struct {
Status string `json:"status"`
Count uint64
}{
Status: "ok",
Count: docCount,
}
mustEncode(w, rv)
}

60
http/doc_delete.go Normal file
View File

@ -0,0 +1,60 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package http
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
type DocDeleteHandler struct {
defaultIndexName string
}
func NewDocDeleteHandler(defaultIndexName string) *DocDeleteHandler {
return &DocDeleteHandler{
defaultIndexName: defaultIndexName,
}
}
func (h *DocDeleteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// find the index to operate on
indexName := mux.Vars(req)["indexName"]
if indexName == "" {
indexName = h.defaultIndexName
}
index := IndexByName(indexName)
if index == nil {
showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404)
return
}
// find the doc id
docId := mux.Vars(req)["docId"]
if docId == "" {
showError(w, req, "document id cannot be empty", 400)
return
}
err := index.Delete(docId)
if err != nil {
showError(w, req, fmt.Sprintf("error deleting document '%s': %v", docId, err), 500)
return
}
rv := struct {
Status string `json:"status"`
}{
Status: "ok",
}
mustEncode(w, rv)
}

100
http/doc_get.go Normal file
View File

@ -0,0 +1,100 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package http
import (
"fmt"
"net/http"
"time"
"github.com/couchbaselabs/bleve/document"
"github.com/gorilla/mux"
)
type DocGetHandler struct {
defaultIndexName string
}
func NewDocGetHandler(defaultIndexName string) *DocGetHandler {
return &DocGetHandler{
defaultIndexName: defaultIndexName,
}
}
func (h *DocGetHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// find the index to operate on
indexName := mux.Vars(req)["indexName"]
if indexName == "" {
indexName = h.defaultIndexName
}
index := IndexByName(indexName)
if index == nil {
showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404)
return
}
// find the doc id
docId := mux.Vars(req)["docId"]
if docId == "" {
showError(w, req, "document id cannot be empty", 400)
return
}
doc, err := index.Document(docId)
if err != nil {
showError(w, req, fmt.Sprintf("error deleting document '%s': %v", docId, err), 500)
return
}
if doc == nil {
showError(w, req, fmt.Sprintf("no such document '%s'", docId), 404)
return
}
rv := struct {
ID string `json:"id"`
Fields map[string]interface{} `json:"fields"`
}{
ID: docId,
Fields: map[string]interface{}{},
}
for _, field := range doc.Fields {
var newval interface{}
switch field := field.(type) {
case *document.TextField:
newval = string(field.Value())
case *document.NumericField:
n, err := field.Number()
if err == nil {
newval = n
}
case *document.DateTimeField:
d, err := field.DateTime()
if err == nil {
newval = d.Format(time.RFC3339Nano)
}
}
existing, existed := rv.Fields[field.Name()]
if existed {
switch existing := existing.(type) {
case []interface{}:
rv.Fields[field.Name()] = append(existing, newval)
case interface{}:
arr := make([]interface{}, 2)
arr[0] = existing
arr[1] = newval
rv.Fields[field.Name()] = arr
}
} else {
rv.Fields[field.Name()] = newval
}
}
mustEncode(w, rv)
}

68
http/doc_index.go Normal file
View File

@ -0,0 +1,68 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package http
import (
"fmt"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
)
type DocIndexHandler struct {
defaultIndexName string
}
func NewDocIndexHandler(defaultIndexName string) *DocIndexHandler {
return &DocIndexHandler{
defaultIndexName: defaultIndexName,
}
}
func (h *DocIndexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// find the index to operate on
indexName := mux.Vars(req)["indexName"]
if indexName == "" {
indexName = h.defaultIndexName
}
index := IndexByName(indexName)
if index == nil {
showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404)
return
}
// find the doc id
docId := mux.Vars(req)["docId"]
if docId == "" {
showError(w, req, "document id cannot be empty", 400)
return
}
// read the request body
requestBody, err := ioutil.ReadAll(req.Body)
if err != nil {
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
return
}
err = index.Index(docId, requestBody)
if err != nil {
showError(w, req, fmt.Sprintf("error indexing document '%s': %v", docId, err), 500)
return
}
rv := struct {
Status string `json:"status"`
}{
Status: "ok",
}
mustEncode(w, rv)
}

75
http/index_create.go Normal file
View File

@ -0,0 +1,75 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package http
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"github.com/couchbaselabs/bleve"
"github.com/gorilla/mux"
)
type CreateIndexHandler struct {
basePath string
}
func NewCreateIndexHander(basePath string) *CreateIndexHandler {
return &CreateIndexHandler{
basePath: basePath,
}
}
func (h *CreateIndexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// find the name of the index to create
indexName := mux.Vars(req)["indexName"]
if indexName == "" {
showError(w, req, fmt.Sprintf("index name is required", indexName), 400)
return
}
indexMapping := bleve.NewIndexMapping()
// read the request body
requestBody, err := ioutil.ReadAll(req.Body)
if err != nil {
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
return
}
// interpret request body as index mapping
if len(requestBody) > 0 {
err := json.Unmarshal(requestBody, &indexMapping)
if err != nil {
showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400)
return
}
}
newIndex, err := bleve.New(h.indexPath(indexName), indexMapping)
if err != nil {
showError(w, req, fmt.Sprintf("error creating index: %v", err), 500)
return
}
RegisterIndexName(indexName, newIndex)
rv := struct {
Status string `json:"status"`
}{
Status: "ok",
}
mustEncode(w, rv)
}
func (h *CreateIndexHandler) indexPath(name string) string {
return h.basePath + string(os.PathSeparator) + name
}

63
http/index_delete.go Normal file
View File

@ -0,0 +1,63 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package http
import (
"fmt"
"net/http"
"os"
"github.com/gorilla/mux"
)
type DeleteIndexHandler struct {
basePath string
}
func NewDeleteIndexHandler(basePath string) *DeleteIndexHandler {
return &DeleteIndexHandler{
basePath: basePath,
}
}
func (h *DeleteIndexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// find the name of the index to delete
indexName := mux.Vars(req)["indexName"]
if indexName == "" {
showError(w, req, fmt.Sprintf("index name is required", indexName), 400)
return
}
indexToDelete := UnregisterIndexByName(indexName)
if indexToDelete == nil {
showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404)
return
}
// close the index
indexToDelete.Close()
// now delete it
err := os.RemoveAll(h.indexPath(indexName))
if err != nil {
showError(w, req, fmt.Sprintf("error deletoing index: %v", err), 500)
return
}
rv := struct {
Status string `json:"status"`
}{
Status: "ok",
}
mustEncode(w, rv)
}
func (h *DeleteIndexHandler) indexPath(name string) string {
return h.basePath + string(os.PathSeparator) + name
}

50
http/index_get.go Normal file
View File

@ -0,0 +1,50 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package http
import (
"fmt"
"net/http"
"github.com/couchbaselabs/bleve"
"github.com/gorilla/mux"
)
type GetIndexHandler struct{}
func NewGetIndexHandler() *GetIndexHandler {
return &GetIndexHandler{}
}
func (h *GetIndexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// find the name of the index to create
indexName := mux.Vars(req)["indexName"]
if indexName == "" {
showError(w, req, fmt.Sprintf("index name is required", indexName), 400)
return
}
index := IndexByName(indexName)
if index == nil {
showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404)
return
}
rv := struct {
Status string `json:"status"`
Name string `json:"name"`
Mapping *bleve.IndexMapping `json:"mapping"`
}{
Status: "ok",
Name: indexName,
Mapping: index.Mapping(),
}
mustEncode(w, rv)
}

31
http/index_list.go Normal file
View File

@ -0,0 +1,31 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package http
import (
"net/http"
)
type ListIndexesHandler struct{}
func NewListIndexesHander() *ListIndexesHandler {
return &ListIndexesHandler{}
}
func (h *ListIndexesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
indexNames := IndexNames()
rv := struct {
Status string `json:"status"`
Indexes []string `json:"indexes"`
}{
Status: "ok",
Indexes: indexNames,
}
mustEncode(w, rv)
}

View File

@ -27,9 +27,36 @@ func RegisterIndexName(name string, index bleve.Index) {
indexNameMapping[name] = index
}
func UnregisterIndexByName(name string) bleve.Index {
indexNameMappingLock.Lock()
defer indexNameMappingLock.Unlock()
if indexNameMapping == nil {
return nil
}
rv := indexNameMapping[name]
if rv != nil {
delete(indexNameMapping, name)
}
return rv
}
func IndexByName(name string) bleve.Index {
indexNameMappingLock.RLock()
defer indexNameMappingLock.RUnlock()
return indexNameMapping[name]
}
func IndexNames() []string {
indexNameMappingLock.RLock()
defer indexNameMappingLock.RUnlock()
rv := make([]string, len(indexNameMapping))
count := 0
for k, _ := range indexNameMapping {
rv[count] = k
count++
}
return rv
}

View File

@ -18,8 +18,8 @@ import (
type DocumentMapping struct {
Enabled bool `json:"enabled"`
Dynamic bool `json:"dynamic"`
Properties map[string]*DocumentMapping `json:"properties"`
Fields []*FieldMapping `json:"fields"`
Properties map[string]*DocumentMapping `json:"properties,omitempty"`
Fields []*FieldMapping `json:"fields,omitempty"`
DefaultAnalyzer string `json:"default_analyzer"`
}

View File

@ -32,7 +32,7 @@ const DefaultDateTimeParser = "dateTimeOptional"
const DefaultByteArrayConverter = "json"
type IndexMapping struct {
TypeMapping map[string]*DocumentMapping `json:"types"`
TypeMapping map[string]*DocumentMapping `json:"types,omitempty"`
DefaultMapping *DocumentMapping `json:"default_mapping"`
TypeField string `json:"type_field"`
DefaultType string `json:"default_type"`