func (docstore *DocStoreExt) docHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) collection := vars["collection"] if collection == "" { panic("missing collection query arg") } sid := vars["_id"] if sid == "" { panic("missing _id query arg") } var _id *id.ID var err error srw := httputil.NewSnappyResponseWriter(w, r) defer srw.Close() switch r.Method { case "GET": js := []byte{} if _id, err = docstore.fetchDoc(collection, sid, &js); err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") srw.Write(addID(js, sid)) case "POST": doc := map[string]interface{}{} if _id, err = docstore.fetchDoc(collection, sid, &doc); err != nil { panic(err) } var update map[string]interface{} decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&update); err != nil { panic(err) } docstore.logger.Debug("Update", "_id", sid, "update_query", update) newDoc, err := updateDoc(doc, update) blob, err := json.Marshal(newDoc) if err != nil { panic(err) } hash := fmt.Sprintf("%x", blake2b.Sum256(blob)) docstore.blobStore.Put(hash, blob) bash := make([]byte, len(hash)+1) // FIXME(tsileo) fetch previous flag and set the same bash[0] = FlagIndexed copy(bash[1:], []byte(hash)[:]) if _, err := docstore.kvStore.Put(fmt.Sprintf(KeyFmt, collection, _id.String()), string(bash), -1); err != nil { panic(err) } httputil.WriteJSON(srw, newDoc) } w.Header().Set("BlobStash-DocStore-Doc-Id", sid) w.Header().Set("BlobStash-DocStore-Doc-Hash", _id.Hash()) w.Header().Set("BlobStash-DocStore-Doc-CreatedAt", strconv.Itoa(_id.Ts())) } }
func (docstore *DocStoreExt) collectionsHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": collections, err := docstore.Collections() if err != nil { panic(err) } srw := httputil.NewSnappyResponseWriter(w, r) httputil.WriteJSON(srw, map[string]interface{}{ "collections": collections, }) srw.Close() default: w.WriteHeader(http.StatusMethodNotAllowed) } } }
func (docstore *DocStoreExt) searchHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) collection := vars["collection"] if collection == "" { panic("missing collection query arg") } js, err := docstore.Search(collection, r.URL.Query().Get("q")) if err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") srw := httputil.NewSnappyResponseWriter(w, r) srw.Write(js) srw.Close() } }
func (docstore *DocStoreExt) docsHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() vars := mux.Vars(r) collection := vars["collection"] if collection == "" { panic("missing collection query arg") } // explainMode := false // if r.Header.Get("BlobStash-DocStore-Explain-Mode") != "" || q.Get("explain") != "" { // explainMode = true // } switch r.Method { case "GET": // Parse the cursor cursor := q.Get("cursor") // Parse the query (JSON-encoded) query := map[string]interface{}{} jsQuery := q.Get("query") if jsQuery != "" { if err := json.Unmarshal([]byte(jsQuery), &query); err != nil { panic(err) } } // Parse the limit limit := 50 if q.Get("limit") != "" { ilimit, err := strconv.Atoi(q.Get("limit")) if err != nil { http.Error(w, "bad limit", 500) } limit = ilimit } js, stats, err := docstore.query(collection, query, cursor, limit) if err != nil { panic(err) } // Set some meta headers to help the client build subsequent query // (iterator/cursor handling) var hasMore bool // Guess if they're are still results on client-side, // by checking if NReturned < limit, we can deduce there's no more results. // The cursor should be the start of the next query if stats.NReturned == limit { hasMore = true } w.Header().Set("BlobStahs-DocStore-Iter-Has-More", strconv.FormatBool(hasMore)) w.Header().Set("BlobStash-DocStore-Iter-Cursor", nextKey(stats.LastID)) // Set headers for the query stats w.Header().Set("BlobStash-DocStore-Query-Returned", strconv.Itoa(stats.NReturned)) w.Header().Set("BlobStash-DocStore-Query-Examined", strconv.Itoa(stats.TotalDocsExamined)) w.Header().Set("BlobStash-DocStore-Query-Exec-Time", strconv.Itoa(stats.ExecutionTimeMillis)) // Write the JSON response (encoded if requested) w.Header().Set("Content-Type", "application/json") srw := httputil.NewSnappyResponseWriter(w, r) srw.Write(js) srw.Close() case "POST": // Read the whole body blob, err := ioutil.ReadAll(r.Body) if err != nil { panic(err) } // Ensure it's JSON encoded doc := map[string]interface{}{} if err := json.Unmarshal(blob, &doc); err != nil { panic(err) } // FIXME(tsileo) re-enable full-text index // docFlag := FlagNoIndex // // Should the doc be full-text indexed? // indexHeader := r.Header.Get("BlobStash-DocStore-IndexFullText") // if indexHeader != "" { // docFlag = FlagIndexed // } _id, err := docstore.Insert(collection, &doc) if err != nil { panic(err) } // if indexHeader != "" { // if err := docstore.index.Index(_id.String(), doc); err != nil { // panic(err) // } // } w.Header().Set("BlobStash-DocStore-Doc-Id", _id.String()) w.Header().Set("BlobStash-DocStore-Doc-Hash", _id.Hash()) w.Header().Set("BlobStash-DocStore-Doc-CreatedAt", strconv.Itoa(_id.Ts())) w.WriteHeader(http.StatusNoContent) default: w.WriteHeader(http.StatusMethodNotAllowed) } } }