blob: d0a1dbec7f2ed2615c36ff938d6a68cfc7ca7b1c [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 lucy
/*
#define C_LUCY_HITS
#include "Lucy/Search/Collector.h"
#include "Lucy/Search/Collector/SortCollector.h"
#include "Lucy/Search/Hits.h"
#include "Lucy/Search/IndexSearcher.h"
#include "Lucy/Search/Query.h"
#include "Lucy/Search/Compiler.h"
#include "Lucy/Search/Searcher.h"
#include "Lucy/Search/QueryParser.h"
#include "Lucy/Search/ANDQuery.h"
#include "Lucy/Search/ORQuery.h"
#include "Lucy/Search/ANDMatcher.h"
#include "Lucy/Search/MatchDoc.h"
#include "Lucy/Search/ORMatcher.h"
#include "Lucy/Search/SeriesMatcher.h"
#include "Lucy/Search/SortRule.h"
#include "Lucy/Search/SortSpec.h"
#include "Lucy/Search/TopDocs.h"
#include "Lucy/Document/HitDoc.h"
#include "Lucy/Index/IndexReader.h"
#include "LucyX/Search/MockMatcher.h"
#include "Clownfish/Blob.h"
#include "Clownfish/Hash.h"
#include "Clownfish/HashIterator.h"
#include "Clownfish/Vector.h"
static inline void
float32_set(float *floats, size_t i, float value) {
floats[i] = value;
}
*/
import "C"
import "unsafe"
import "git-wip-us.apache.org/repos/asf/lucy-clownfish.git/runtime/go/clownfish"
type HitsIMP struct {
clownfish.ObjIMP
err error
}
type MatcherIMP struct {
clownfish.ObjIMP
err error
}
func OpenIndexSearcher(index interface{}) (obj IndexSearcher, err error) {
indexC := (*C.cfish_Obj)(clownfish.GoToClownfish(index, unsafe.Pointer(C.CFISH_OBJ), false))
defer C.cfish_decref(unsafe.Pointer(indexC))
err = clownfish.TrapErr(func() {
cfObj := C.lucy_IxSearcher_new(indexC)
obj = WRAPIndexSearcher(unsafe.Pointer(cfObj))
})
return obj, err
}
// Read data into the supplied doc.
func (s *SearcherIMP) ReadDoc(docID int32, doc interface{}) error {
self := (*C.lucy_Searcher)(clownfish.Unwrap(s, "s"))
class := C.cfish_Obj_get_class((*C.cfish_Obj)(unsafe.Pointer(self)))
if class == C.LUCY_INDEXSEARCHER {
ixReader := C.LUCY_IxSearcher_Get_Reader((*C.lucy_IndexSearcher)(unsafe.Pointer(self)))
cfStr := (*C.cfish_String)(clownfish.GoToClownfish("Lucy::Index::DocReader", unsafe.Pointer(C.CFISH_STRING), false))
defer C.cfish_decref(unsafe.Pointer(cfStr))
docReader := C.LUCY_IxReader_Fetch(ixReader, cfStr)
if docReader == nil {
return clownfish.NewErr("No DocReader available")
}
docReaderGo := clownfish.WRAPAny(unsafe.Pointer(C.cfish_incref(unsafe.Pointer(docReader)))).(DocReader)
return docReaderGo.ReadDoc(docID, doc)
} else {
return clownfish.NewErr("Support for ReadDoc not implemented")
}
}
func (s *SearcherIMP) FetchDoc(docID int32) (doc HitDoc, err error) {
err = clownfish.TrapErr(func() {
self := (*C.lucy_Searcher)(clownfish.Unwrap(s, "s"))
docC := C.LUCY_Searcher_Fetch_Doc(self, C.int32_t(docID))
doc = WRAPHitDoc(unsafe.Pointer(docC))
})
return doc, err
}
func (s *SearcherIMP) fetchDocVec(docID int32) (dv DocVector, err error) {
err = clownfish.TrapErr(func() {
self := (*C.lucy_Searcher)(clownfish.Unwrap(s, "s"))
dvC := C.LUCY_Searcher_Fetch_Doc_Vec(self, C.int32_t(docID))
dv = WRAPDocVector(unsafe.Pointer(dvC))
})
return dv, err
}
func (s *SearcherIMP) Close() error {
return clownfish.TrapErr(func() {
self := (*C.lucy_Searcher)(clownfish.Unwrap(s, "s"))
C.LUCY_Searcher_Close(self)
})
}
func (s *SearcherIMP) Hits(query interface{}, offset uint32, numWanted uint32,
sortSpec SortSpec) (hits Hits, err error) {
self := (*C.lucy_Searcher)(clownfish.Unwrap(s, "s"))
sortSpecC := (*C.lucy_SortSpec)(clownfish.UnwrapNullable(sortSpec))
queryC := (*C.cfish_Obj)(clownfish.GoToClownfish(query, unsafe.Pointer(C.CFISH_OBJ), false))
defer C.cfish_decref(unsafe.Pointer(queryC))
err = clownfish.TrapErr(func() {
hitsC := C.LUCY_Searcher_Hits(self, queryC,
C.uint32_t(offset), C.uint32_t(numWanted), sortSpecC)
hits = WRAPHits(unsafe.Pointer(hitsC))
})
return hits, err
}
func (s *SearcherIMP) topDocs(query Query, numWanted uint32,
sortSpec SortSpec) (topDocs TopDocs, err error) {
err = clownfish.TrapErr(func() {
self := (*C.lucy_Searcher)(clownfish.Unwrap(s, "s"))
sortSpecC := (*C.lucy_SortSpec)(clownfish.UnwrapNullable(sortSpec))
queryC := (*C.lucy_Query)(clownfish.Unwrap(query, "query"))
topDocsC := C.LUCY_Searcher_Top_Docs(self, queryC,
C.uint32_t(numWanted), sortSpecC)
topDocs = WRAPTopDocs(unsafe.Pointer(topDocsC))
})
return topDocs, err
}
func NewQueryParser(schema Schema, fields []string) QueryParser {
return NewORParser(schema, fields)
}
func NewORParser(schema Schema, fields []string) QueryParser {
return doNewQueryParser(schema, "OR", fields)
}
func NewANDParser(schema Schema, fields []string) QueryParser {
return doNewQueryParser(schema, "AND", fields)
}
func doNewQueryParser(schema Schema, defaultBoolop string, fields []string) QueryParser {
schemaCF := (*C.lucy_Schema)(clownfish.Unwrap(schema, "schema"))
defaultBoolopCF := (*C.cfish_String)(clownfish.GoToClownfish(defaultBoolop, unsafe.Pointer(C.CFISH_STRING), true))
defer C.cfish_decref(unsafe.Pointer(defaultBoolopCF))
fieldsCF := stringSliceToVec(fields)
defer C.cfish_decref(unsafe.Pointer(fieldsCF))
retvalCF := C.lucy_QParser_new(schemaCF, nil, defaultBoolopCF, fieldsCF)
return clownfish.WRAPAny(unsafe.Pointer(retvalCF)).(QueryParser)
}
func (qp *QueryParserIMP) MakePhraseQuery(field string, terms []interface{}) PhraseQuery {
return NewPhraseQuery(field, terms)
}
func (qp *QueryParserIMP) MakeANDQuery(children []Query) ANDQuery {
return NewANDQuery(children)
}
func (qp *QueryParserIMP) MakeORQuery(children []Query) ORQuery {
return NewORQuery(children)
}
func (q *QueryParserIMP) getFields() []string {
self := (*C.lucy_QueryParser)(clownfish.Unwrap(q, "q"))
retvalCF := C.LUCY_QParser_Get_Fields(self)
return vecToStringSlice(retvalCF)
}
type setScorer interface {
SetScore(float32)
}
func (h *HitsIMP) Next(hit interface{}) bool {
self := (*C.lucy_Hits)(clownfish.Unwrap(h, "h"))
ivars := C.lucy_Hits_IVARS(self)
matchDoc := (*C.lucy_MatchDoc)(unsafe.Pointer(
C.CFISH_Vec_Fetch(ivars.match_docs, C.size_t(ivars.offset))))
ivars.offset += 1
if matchDoc == nil {
// Bail if there aren't any more *captured* hits. (There may be
// more total hits.)
return false
} else {
// Lazily fetch HitDoc, set score.
searcher := clownfish.WRAPAny(unsafe.Pointer(C.cfish_incref(
unsafe.Pointer(ivars.searcher)))).(Searcher)
docID := int32(C.LUCY_MatchDoc_Get_Doc_ID(matchDoc))
err := searcher.ReadDoc(docID, hit)
if err != nil {
h.err = err
return false
}
if ss, ok := hit.(setScorer); ok {
ss.SetScore(float32(C.LUCY_MatchDoc_Get_Score(matchDoc)))
}
return true
}
}
func (obj *HitsIMP) Error() error {
return obj.err
}
func NewFieldSortRule(field string, reverse bool) SortRule {
fieldC := clownfish.GoToClownfish(field, unsafe.Pointer(C.CFISH_STRING), false)
cfObj := C.lucy_SortRule_new(C.lucy_SortRule_FIELD, (*C.cfish_String)(fieldC), C.bool(reverse))
return WRAPSortRule(unsafe.Pointer(cfObj))
}
func NewDocIDSortRule(reverse bool) SortRule {
cfObj := C.lucy_SortRule_new(C.lucy_SortRule_DOC_ID, nil, C.bool(reverse))
return WRAPSortRule(unsafe.Pointer(cfObj))
}
func NewScoreSortRule(reverse bool) SortRule {
cfObj := C.lucy_SortRule_new(C.lucy_SortRule_SCORE, nil, C.bool(reverse))
return WRAPSortRule(unsafe.Pointer(cfObj))
}
func NewSortSpec(rules []SortRule) SortSpec {
vec := clownfish.NewVector(len(rules))
for _, rule := range rules {
vec.Push(rule)
}
cfObj := C.lucy_SortSpec_new((*C.cfish_Vector)(clownfish.Unwrap(vec, "rules")))
return WRAPSortSpec(unsafe.Pointer(cfObj))
}
func (spec *SortSpecIMP) GetRules() []SortRule {
self := (*C.lucy_SortSpec)(clownfish.Unwrap(spec, "spec"))
vec := C.LUCY_SortSpec_Get_Rules(self)
length := int(C.CFISH_Vec_Get_Size(vec))
slice := make([]SortRule, length)
for i := 0; i < length; i++ {
elem := C.cfish_incref(unsafe.Pointer(C.CFISH_Vec_Fetch(vec, C.size_t(i))))
slice[i] = WRAPSortRule(unsafe.Pointer(elem))
}
return slice
}
func NewTopDocs(matchDocs []MatchDoc, totalHits uint32) TopDocs {
vec := clownfish.NewVector(len(matchDocs))
for _, matchDoc := range matchDocs {
vec.Push(matchDoc)
}
cfObj := C.lucy_TopDocs_new(((*C.cfish_Vector)(clownfish.Unwrap(vec, "matchDocs"))),
C.uint32_t(totalHits))
return WRAPTopDocs(unsafe.Pointer(cfObj))
}
func (td *TopDocsIMP) SetMatchDocs(matchDocs []MatchDoc) {
self := (*C.lucy_TopDocs)(clownfish.Unwrap(td, "td"))
vec := clownfish.NewVector(len(matchDocs))
for _, matchDoc := range matchDocs {
vec.Push(matchDoc)
}
C.LUCY_TopDocs_Set_Match_Docs(self, (*C.cfish_Vector)(clownfish.Unwrap(vec, "matchDocs")))
}
func (td *TopDocsIMP) GetMatchDocs() []MatchDoc {
self := (*C.lucy_TopDocs)(clownfish.Unwrap(td, "td"))
vec := C.LUCY_TopDocs_Get_Match_Docs(self)
length := int(C.CFISH_Vec_Get_Size(vec))
slice := make([]MatchDoc, length)
for i := 0; i < length; i++ {
elem := C.cfish_incref(unsafe.Pointer(C.CFISH_Vec_Fetch(vec, C.size_t(i))))
slice[i] = WRAPMatchDoc(unsafe.Pointer(elem))
}
return slice
}
func (q *QueryIMP) MakeCompiler(searcher Searcher, boost float32,
subordinate bool) (retval Compiler, err error) {
err = clownfish.TrapErr(func() {
self := (*C.lucy_Query)(clownfish.Unwrap(q, "q"))
searcherCF := (*C.lucy_Searcher)(clownfish.Unwrap(searcher, "searcher"))
retvalCF := C.LUCY_Query_Make_Compiler(self, searcherCF, C.float(boost), C.bool(subordinate))
if retvalCF != nil {
retval = clownfish.WRAPAny(unsafe.Pointer(retvalCF)).(Compiler)
}
})
return retval, err
}
func (c *CompilerIMP) MakeMatcher(reader SegReader, needScore bool) (retval Matcher, err error) {
err = clownfish.TrapErr(func() {
self := (*C.lucy_Compiler)(clownfish.Unwrap(c, "c"))
readerCF := (*C.lucy_SegReader)(clownfish.Unwrap(reader, "reader"))
retvalCF := C.LUCY_Compiler_Make_Matcher(self, readerCF, C.bool(needScore))
if retvalCF != nil {
retval = clownfish.WRAPAny(unsafe.Pointer(retvalCF)).(Matcher)
}
})
if err != nil || retval == nil {
return nil, err
}
return retval, err
}
func NewANDQuery(children []Query) ANDQuery {
vec := clownfish.NewVector(len(children))
for _, child := range children {
vec.Push(child)
}
childrenC := (*C.cfish_Vector)(unsafe.Pointer(vec.TOPTR()))
cfObj := C.lucy_ANDQuery_new(childrenC)
return WRAPANDQuery(unsafe.Pointer(cfObj))
}
func NewORQuery(children []Query) ORQuery {
vec := clownfish.NewVector(len(children))
for _, child := range children {
vec.Push(child)
}
childrenC := (*C.cfish_Vector)(unsafe.Pointer(vec.TOPTR()))
cfObj := C.lucy_ORQuery_new(childrenC)
return WRAPORQuery(unsafe.Pointer(cfObj))
}
func (m *MatcherIMP) Next() int32 {
var retval int32
m.err = clownfish.TrapErr(func() {
self := (*C.lucy_Matcher)(clownfish.Unwrap(m, "m"))
retval = int32(C.LUCY_Matcher_Next(self))
})
if m.err != nil {
return 0
}
return retval
}
func (m *MatcherIMP) Error() error {
return m.err
}
func NewANDMatcher(children []Matcher, sim Similarity) ANDMatcher {
simC := (*C.lucy_Similarity)(clownfish.UnwrapNullable(sim))
vec := clownfish.NewVector(len(children))
for _, child := range children {
vec.Push(child)
}
childrenC := (*C.cfish_Vector)(unsafe.Pointer(vec.TOPTR()))
cfObj := C.lucy_ANDMatcher_new(childrenC, simC)
return WRAPANDMatcher(unsafe.Pointer(cfObj))
}
func NewORMatcher(children []Matcher) ORMatcher {
vec := clownfish.NewVector(len(children))
for _, child := range children {
vec.Push(child)
}
childrenC := (*C.cfish_Vector)(unsafe.Pointer(vec.TOPTR()))
cfObj := C.lucy_ORMatcher_new(childrenC)
return WRAPORMatcher(unsafe.Pointer(cfObj))
}
func NewORScorer(children []Matcher, sim Similarity) ORScorer {
simC := (*C.lucy_Similarity)(clownfish.UnwrapNullable(sim))
vec := clownfish.NewVector(len(children))
for _, child := range children {
vec.Push(child)
}
childrenC := (*C.cfish_Vector)(unsafe.Pointer(vec.TOPTR()))
cfObj := C.lucy_ORScorer_new(childrenC, simC)
return WRAPORScorer(unsafe.Pointer(cfObj))
}
func NewSeriesMatcher(matchers []Matcher, offsets []int32) SeriesMatcher {
vec := clownfish.NewVector(len(matchers))
for _, child := range matchers {
vec.Push(child)
}
i32arr := NewI32Array(offsets)
cfObj := C.lucy_SeriesMatcher_new(((*C.cfish_Vector)(clownfish.Unwrap(vec, "matchers"))),
((*C.lucy_I32Array)(clownfish.Unwrap(i32arr, "offsets"))))
return WRAPSeriesMatcher(unsafe.Pointer(cfObj))
}
func newMockMatcher(docIDs []int32, scores []float32) MockMatcher {
docIDsconv := NewI32Array(docIDs)
docIDsCF := (*C.lucy_I32Array)(unsafe.Pointer(docIDsconv.TOPTR()))
var blob *C.cfish_Blob = nil
if scores != nil {
size := len(scores) * 4
floats := (*C.float)(C.malloc(C.size_t(size)))
for i := 0; i < len(scores); i++ {
C.float32_set(floats, C.size_t(i), C.float(scores[i]))
}
blob = C.cfish_Blob_new_steal(unsafe.Pointer(floats), C.size_t(size))
defer C.cfish_decref(unsafe.Pointer(blob))
}
matcher := C.lucy_MockMatcher_new(docIDsCF, blob)
return WRAPMockMatcher(unsafe.Pointer(matcher))
}
func (sc *SortCollectorIMP) PopMatchDocs() []MatchDoc {
self := (*C.lucy_SortCollector)(clownfish.Unwrap(sc, "sc"))
matchDocsC := C.LUCY_SortColl_Pop_Match_Docs(self)
defer C.cfish_decref(unsafe.Pointer(matchDocsC))
length := int(C.CFISH_Vec_Get_Size(matchDocsC))
slice := make([]MatchDoc, length)
for i := 0; i < length; i++ {
elem := C.cfish_incref(unsafe.Pointer(C.CFISH_Vec_Fetch(matchDocsC, C.size_t(i))))
slice[i] = WRAPMatchDoc(unsafe.Pointer(elem))
}
return slice
}