4ae9eb895c
also added a corresponding http handler
200 lines
4.4 KiB
Go
200 lines
4.4 KiB
Go
// 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 bleve
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/couchbaselabs/bleve/document"
|
|
"github.com/couchbaselabs/bleve/index"
|
|
"github.com/couchbaselabs/bleve/index/store"
|
|
"github.com/couchbaselabs/bleve/index/store/leveldb"
|
|
"github.com/couchbaselabs/bleve/index/upside_down"
|
|
"github.com/couchbaselabs/bleve/search"
|
|
)
|
|
|
|
type indexImpl struct {
|
|
s store.KVStore
|
|
i index.Index
|
|
m *IndexMapping
|
|
}
|
|
|
|
func newIndex(path string, mapping *IndexMapping) (*indexImpl, error) {
|
|
store, err := leveldb.Open(path, Config.CreateIfMissing)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
idx := upside_down.NewUpsideDownCouch(store)
|
|
err = idx.Open()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &indexImpl{
|
|
s: store,
|
|
i: idx,
|
|
m: mapping,
|
|
}, nil
|
|
}
|
|
|
|
// Index the provided data.
|
|
func (i *indexImpl) Index(data interface{}) error {
|
|
id, ok := i.determineID(data)
|
|
if ok {
|
|
return i.IndexID(id, data)
|
|
}
|
|
|
|
return ERROR_NO_ID
|
|
}
|
|
|
|
func (i *indexImpl) IndexID(id string, data interface{}) error {
|
|
doc := document.NewDocument(id)
|
|
err := i.m.MapDocument(doc, data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = i.i.Update(doc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *indexImpl) IndexJSON(data []byte) error {
|
|
var obj interface{}
|
|
err := json.Unmarshal(data, &obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return i.Index(obj)
|
|
}
|
|
|
|
func (i *indexImpl) IndexJSONID(id string, data []byte) error {
|
|
var obj interface{}
|
|
err := json.Unmarshal(data, &obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return i.IndexID(id, obj)
|
|
}
|
|
|
|
func (i *indexImpl) Delete(data interface{}) error {
|
|
id, ok := i.determineID(data)
|
|
if ok {
|
|
return i.DeleteID(id)
|
|
}
|
|
|
|
return ERROR_NO_ID
|
|
}
|
|
|
|
func (i *indexImpl) DeleteID(id string) error {
|
|
err := i.i.Delete(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *indexImpl) Document(id string) (*document.Document, error) {
|
|
return i.i.Document(id)
|
|
}
|
|
|
|
func (i *indexImpl) DocCount() uint64 {
|
|
return i.i.DocCount()
|
|
}
|
|
|
|
func (i *indexImpl) Search(req *SearchRequest) (*SearchResult, error) {
|
|
collector := search.NewTopScorerSkipCollector(req.Size, req.From)
|
|
searcher, err := req.Query.Searcher(i, req.Explain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = collector.Collect(searcher)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hits := collector.Results()
|
|
|
|
if req.Highlight != nil {
|
|
// get the right highlighter
|
|
highlighter := Config.Highlight.Highlighters[*Config.DefaultHighlighter]
|
|
if req.Highlight.Style != nil {
|
|
highlighter = Config.Highlight.Highlighters[*req.Highlight.Style]
|
|
if highlighter == nil {
|
|
return nil, fmt.Errorf("no highlighter named `%s` registered", *req.Highlight.Style)
|
|
}
|
|
}
|
|
|
|
for _, hit := range hits {
|
|
doc, err := i.Document(hit.ID)
|
|
if err == nil {
|
|
highlightFields := req.Highlight.Fields
|
|
if highlightFields == nil {
|
|
// add all fields with matches
|
|
highlightFields = make([]string, 0, len(hit.Locations))
|
|
for k, _ := range hit.Locations {
|
|
highlightFields = append(highlightFields, k)
|
|
}
|
|
}
|
|
|
|
for _, hf := range highlightFields {
|
|
highlighter.BestFragmentsInField(hit, doc, hf, 3)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return &SearchResult{
|
|
Request: req,
|
|
Hits: hits,
|
|
Total: collector.Total(),
|
|
MaxScore: collector.MaxScore(),
|
|
Took: collector.Took(),
|
|
}, nil
|
|
}
|
|
|
|
func (i *indexImpl) Dump() {
|
|
i.i.Dump()
|
|
}
|
|
|
|
func (i *indexImpl) Fields() ([]string, error) {
|
|
return i.i.Fields()
|
|
}
|
|
|
|
func (i *indexImpl) DumpFields() {
|
|
i.i.DumpFields()
|
|
}
|
|
|
|
func (i *indexImpl) DumpDoc(id string) ([]interface{}, error) {
|
|
return i.i.DumpDoc(id)
|
|
}
|
|
|
|
func (i *indexImpl) Close() {
|
|
i.i.Close()
|
|
}
|
|
|
|
func (i *indexImpl) determineID(data interface{}) (string, bool) {
|
|
// first see if the object implements Identifier
|
|
identifier, ok := data.(Identifier)
|
|
if ok {
|
|
return identifier.ID(), true
|
|
}
|
|
|
|
// now see if we can find an ID using the mapping
|
|
if i.m.IdField != nil {
|
|
id, ok := mustString(lookupPropertyPath(data, *i.m.IdField))
|
|
if ok {
|
|
return id, true
|
|
}
|
|
}
|
|
|
|
return "", false
|
|
}
|