From cc7219e69aaab7aa215ed6c39106d89a7df7ee83 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Mon, 9 Nov 2015 17:42:08 -0800 Subject: [PATCH 01/12] Tune and test PolyAnalyzer Go bindings. --- go/build.go | 5 +++++ go/lucy/analysis.go | 22 ++++++++++++++++++++++ go/lucy/analysis_test.go | 4 ++++ 3 files changed, 31 insertions(+) diff --git a/go/build.go b/go/build.go index 6060f5b03..05231fc5d 100644 --- a/go/build.go +++ b/go/build.go @@ -145,6 +145,11 @@ func specClasses(parcel *cfc.Parcel) { analyzerBinding.SpecMethod("Split", "Split(string) []string") analyzerBinding.Register() + polyAnalyzerBinding := cfc.NewGoClass(parcel, "Lucy::Analysis::PolyAnalyzer") + polyAnalyzerBinding.SpecMethod("Get_Analyzers", "GetAnalyzers() []Analyzer") + polyAnalyzerBinding.SetSuppressCtor(true) + polyAnalyzerBinding.Register() + indexerBinding := cfc.NewGoClass(parcel, "Lucy::Index::Indexer") indexerBinding.SpecMethod("", "Close() error") indexerBinding.SpecMethod("Add_Doc", "AddDoc(doc interface{}) error") diff --git a/go/lucy/analysis.go b/go/lucy/analysis.go index 61465b495..68a379c3f 100644 --- a/go/lucy/analysis.go +++ b/go/lucy/analysis.go @@ -20,6 +20,7 @@ package lucy #include #include "Lucy/Analysis/Analyzer.h" +#include "Lucy/Analysis/PolyAnalyzer.h" #include "Lucy/Analysis/Token.h" #include "Clownfish/Vector.h" */ @@ -59,3 +60,24 @@ func (a *AnalyzerIMP) Split(text string) []string { defer C.cfish_decref(unsafe.Pointer(retvalCF)) return vecToStringSlice(retvalCF) } + +func NewPolyAnalyzer(children []Analyzer) PolyAnalyzer { + vec := clownfish.NewVector(len(children)) + for i := 0; i < len(children); i++ { + vec.Push(children[i]) + } + retvalCF := C.lucy_PolyAnalyzer_new(nil, (*C.cfish_Vector)(clownfish.Unwrap(vec, "vec"))) + return WRAPPolyAnalyzer(unsafe.Pointer(retvalCF)) +} + +func (p *PolyAnalyzerIMP) GetAnalyzers() []Analyzer { + self := (*C.lucy_PolyAnalyzer)(clownfish.Unwrap(p, "p")) + retvalCF := C.LUCY_PolyAnalyzer_Get_Analyzers(self) + size := C.CFISH_Vec_Get_Size(retvalCF) + retval := make([]Analyzer, int(size)) + for i := 0; i < int(size); i++ { + child := unsafe.Pointer(C.CFISH_Vec_Fetch(retvalCF, C.size_t(i))) + retval[i] = clownfish.WRAPAny(unsafe.Pointer(C.cfish_incref(child))).(Analyzer) + } + return retval +} diff --git a/go/lucy/analysis_test.go b/go/lucy/analysis_test.go index 9470fbbc7..d9d9f2ff9 100644 --- a/go/lucy/analysis_test.go +++ b/go/lucy/analysis_test.go @@ -105,6 +105,10 @@ func TestCoreAnalyzers(t *testing.T) { runAnalyzerTests(t, NewSnowballStemmer("en")) runAnalyzerTests(t, NewSnowballStopFilter("en", nil)) runAnalyzerTests(t, NewStandardTokenizer()) + + children := []Analyzer{NewStandardTokenizer(), NewNormalizer("NFKC", true, false)} + polyAnalyzer := NewPolyAnalyzer(children) + runAnalyzerTests(t, polyAnalyzer) } func TestRegexTokenizerSplit(t *testing.T) { From e101acade8d9d2f1235b71e23db661e44359cf3d Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Mon, 9 Nov 2015 20:16:04 -0800 Subject: [PATCH 02/12] Tune and test Go bindings for Simple. --- core/Lucy/Simple.c | 5 ++ core/Lucy/Simple.cfh | 3 ++ go/build.go | 9 ++++ go/lucy/simple.go | 120 +++++++++++++++++++++++++++++++++++++++++ go/lucy/simple_test.go | 62 +++++++++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 go/lucy/simple.go create mode 100644 go/lucy/simple_test.go diff --git a/core/Lucy/Simple.c b/core/Lucy/Simple.c index 22719841a..1287bcc0e 100644 --- a/core/Lucy/Simple.c +++ b/core/Lucy/Simple.c @@ -170,6 +170,11 @@ Simple_Get_Indexer_IMP(Simple *self) { return ivars->indexer; } +Hits* +Simple_Get_Hits_IMP(Simple *self) { + return Simple_IVARS(self)->hits; +} + void Simple_Finish_Indexing_IMP(Simple *self) { SimpleIVARS *const ivars = Simple_IVARS(self); diff --git a/core/Lucy/Simple.cfh b/core/Lucy/Simple.cfh index 3680ce520..d3cdcfa43 100644 --- a/core/Lucy/Simple.cfh +++ b/core/Lucy/Simple.cfh @@ -90,6 +90,9 @@ public class Lucy::Simple { Indexer* Get_Indexer(Simple *self); + nullable Hits* + Get_Hits(Simple *self); + void Finish_Indexing(Simple *self); diff --git a/go/build.go b/go/build.go index 05231fc5d..59810168f 100644 --- a/go/build.go +++ b/go/build.go @@ -136,6 +136,15 @@ func runCFC() { } func specClasses(parcel *cfc.Parcel) { + simpleBinding := cfc.NewGoClass(parcel, "Lucy::Simple") + simpleBinding.SpecMethod("Add_Doc", "AddDoc(doc interface{}) error") + simpleBinding.SpecMethod("Search", "Search(string, int, int) (int, error)") + simpleBinding.SpecMethod("Next", "Next(hit interface{}) bool") + simpleBinding.SpecMethod("", "Error() error") + simpleBinding.SetSuppressCtor(true) + simpleBinding.SetSuppressStruct(true) + simpleBinding.Register() + tokenBinding := cfc.NewGoClass(parcel, "Lucy::Analysis::Token") tokenBinding.SpecMethod("", "SetText(string)") tokenBinding.SpecMethod("", "GetText() string") diff --git a/go/lucy/simple.go b/go/lucy/simple.go new file mode 100644 index 000000000..b4e6ff420 --- /dev/null +++ b/go/lucy/simple.go @@ -0,0 +1,120 @@ +/* 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 + +/* + +#include "Lucy/Simple.h" + +*/ +import "C" +import "unsafe" +import "reflect" +import "fmt" + +import "git-wip-us.apache.org/repos/asf/lucy-clownfish.git/runtime/go/clownfish" + +type SimpleIMP struct { + clownfish.ObjIMP + err error +} + +func OpenSimple(index interface{}, language string) (s Simple, err error) { + indexC := (*C.cfish_Obj)(clownfish.GoToClownfish(index, unsafe.Pointer(C.CFISH_OBJ), false)) + defer C.cfish_decref(unsafe.Pointer(indexC)) + languageC := (*C.cfish_String)(clownfish.GoToClownfish(language, unsafe.Pointer(C.CFISH_STRING), false)) + defer C.cfish_decref(unsafe.Pointer(languageC)) + err = clownfish.TrapErr(func() { + cfObj := C.lucy_Simple_new(indexC, languageC) + s = WRAPSimple(unsafe.Pointer(cfObj)) + }) + return s, err +} + +func (s *SimpleIMP) AddDoc(doc interface{}) error { + self := (*C.lucy_Simple)(clownfish.Unwrap(s, "s")) + indexer := s.getIndexer() + var docToIndex Doc + stockDoc := indexer.getStockDoc() + stockDocC := (*C.lucy_Doc)(clownfish.Unwrap(stockDoc, "stockDoc")) + docFields := fetchDocFields(stockDocC) + for field := range docFields { + delete(docFields, field) + } + + switch d := doc.(type) { + case map[string]interface{}: + for k, v := range d { + docFields[k] = v + } + docToIndex = stockDoc + case Doc: + docToIndex = d + default: + docToIndex = stockDoc + // Get reflection value and type for the supplied struct. + var docValue reflect.Value + var success bool + if reflect.ValueOf(doc).Kind() == reflect.Ptr { + temp := reflect.ValueOf(doc).Elem() + if temp.Kind() == reflect.Struct { + docValue = temp + success = true + } + } + if !success { + mess := fmt.Sprintf("Unexpected type for doc: %T", doc) + return clownfish.NewErr(mess) + } + + // Copy field values into stockDoc. + docType := docValue.Type() + for i := 0; i < docValue.NumField(); i++ { + field := docType.Field(i).Name + value := docValue.Field(i).String() + docFields[field] = value + } + } + + return clownfish.TrapErr(func(){ + docC := (*C.lucy_Doc)(clownfish.Unwrap(docToIndex, "docToIndex")) + C.LUCY_Simple_Add_Doc(self, docC) + }) +} + +func (s *SimpleIMP) Search(query string, offset, numWanted int) (totalHits int, err error) { + err = clownfish.TrapErr(func() { + self := (*C.lucy_Simple)(clownfish.Unwrap(s, "s")) + qStringC := (*C.cfish_String)(clownfish.GoToClownfish(query, unsafe.Pointer(C.CFISH_STRING), false)) + defer C.cfish_decref(unsafe.Pointer(qStringC)) + totalHits = int(C.LUCY_Simple_Search(self, qStringC, C.uint32_t(offset), C.uint32_t(numWanted))) + }) + return totalHits, err +} + +func (s *SimpleIMP) Next(hit interface{}) bool { + var retval bool + if hits := s.getHits(); hits != nil { + retval = hits.Next(hit) + s.err = hits.Error() + } + return retval +} + +func (s *SimpleIMP) Error() error { + return s.err +} diff --git a/go/lucy/simple_test.go b/go/lucy/simple_test.go new file mode 100644 index 000000000..cf2081e80 --- /dev/null +++ b/go/lucy/simple_test.go @@ -0,0 +1,62 @@ +/* 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 + +import "testing" + +func TestSimpleBasics(t *testing.T) { + var err error + simple, err := OpenSimple(NewRAMFolder(""), "en") + if simple == nil || err != nil { + t.Errorf("OpenSimple: %v", err) + } + + docStruct := &testDoc{Content: "foo"} + docMap := map[string]interface{}{"Content": "foo"} + docDoc := NewDoc(0) + docDoc.Store("Content", "foo") + + err = simple.AddDoc(docStruct) + if err != nil { + t.Errorf("AddDoc with struct: %v", err) + } + err = simple.AddDoc(docMap) + if err != nil { + t.Errorf("AddDoc with map: %v", err) + } + err = simple.AddDoc(docDoc) + if err != nil { + t.Errorf("AddDoc with Doc: %v", err) + } + + count, err := simple.Search("foo", 0, 10) + if count != 3 || err != nil { + t.Errorf("Search: %d, %v", count, err) + } + docStruct.Content = "" + if !simple.Next(docStruct) || docStruct.Content != "foo" { + t.Errorf("Next with struct: %v", simple.Error()) + } + delete(docMap, "Content") + if !simple.Next(docMap) || docMap["Content"].(string) != "foo" { + t.Errorf("Next with map: %v", simple.Error()) + } + docDoc.Store("Content", "") + if !simple.Next(docDoc) { + t.Errorf("Next with Doc: %v", simple.Error()) + } +} From e35becd7dd804ceb103e19710f0aa85fd864e150 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Tue, 10 Nov 2015 19:28:38 -0800 Subject: [PATCH 03/12] Consolidate Searcher Go bindings. --- go/lucy/search.go | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/go/lucy/search.go b/go/lucy/search.go index a8bab6f55..44f5054f7 100644 --- a/go/lucy/search.go +++ b/go/lucy/search.go @@ -69,15 +69,6 @@ func OpenIndexSearcher(index interface{}) (obj IndexSearcher, err error) { return obj, err } -func (obj *IndexSearcherIMP) Close() error { - return doClose(obj) -} - -func (obj *IndexSearcherIMP) Hits(query interface{}, offset uint32, numWanted uint32, - sortSpec SortSpec) (hits Hits, err error) { - return doHits(obj, query, offset, numWanted, sortSpec) -} - // Read data into the supplied doc. func (s *SearcherIMP) ReadDoc(docID int32, doc interface{}) error { self := (*C.lucy_Searcher)(clownfish.Unwrap(s, "s")) @@ -97,14 +88,14 @@ func (s *SearcherIMP) ReadDoc(docID int32, doc interface{}) error { } } -func doClose(obj Searcher) error { - self := ((*C.lucy_Searcher)(unsafe.Pointer(obj.TOPTR()))) +func (s *SearcherIMP) Close() error { return clownfish.TrapErr(func() { + self := (*C.lucy_Searcher)(clownfish.Unwrap(s, "s")) C.LUCY_Searcher_Close(self) }) } -func doHits(s Searcher, query interface{}, offset uint32, numWanted uint32, +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)) @@ -118,24 +109,6 @@ func doHits(s Searcher, query interface{}, offset uint32, numWanted uint32, return hits, err } -func (obj *SearcherIMP) Close() error { - return doClose(obj) -} - -func (obj *SearcherIMP) Hits(query interface{}, offset uint32, numWanted uint32, - sortSpec SortSpec) (hits Hits, err error) { - return doHits(obj, query, offset, numWanted, sortSpec) -} - -func (obj *PolySearcherIMP) Close() error { - return doClose(obj) -} - -func (obj *PolySearcherIMP) Hits(query interface{}, offset uint32, numWanted uint32, - sortSpec SortSpec) (hits Hits, err error) { - return doHits(obj, query, offset, numWanted, sortSpec) -} - type setScorer interface { SetScore(float32) } From d483dd076c36f3e8c29d80de5ad6e401e5531ca0 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Tue, 10 Nov 2015 20:04:44 -0800 Subject: [PATCH 04/12] More custom Go for Searcher. Hand-roll bindings for FetchDoc, fetchDocVec and topDocs to allow for returning a tuple including an `error`. --- go/build.go | 3 +++ go/lucy/index_test.go | 2 +- go/lucy/search.go | 31 +++++++++++++++++++++++++++++++ go/lucy/search_test.go | 16 ++++++++++++---- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/go/build.go b/go/build.go index 59810168f..2b94cc5cb 100644 --- a/go/build.go +++ b/go/build.go @@ -209,7 +209,10 @@ func specClasses(parcel *cfc.Parcel) { searcherBinding := cfc.NewGoClass(parcel, "Lucy::Search::Searcher") searcherBinding.SpecMethod("Hits", "Hits(query interface{}, offset uint32, numWanted uint32, sortSpec SortSpec) (Hits, error)") + searcherBinding.SpecMethod("Top_Docs", "topDocs(Query, uint32, SortSpec) (TopDocs, error)") searcherBinding.SpecMethod("Close", "Close() error") + searcherBinding.SpecMethod("Fetch_Doc", "FetchDoc(int32) (HitDoc, error)") + searcherBinding.SpecMethod("Fetch_Doc_Vec", "fetchDocVec(int32) (DocVector, error)") searcherBinding.SpecMethod("", "ReadDoc(int32, interface{}) error") searcherBinding.Register() diff --git a/go/lucy/index_test.go b/go/lucy/index_test.go index 4faab4e8f..5fb4d8b29 100644 --- a/go/lucy/index_test.go +++ b/go/lucy/index_test.go @@ -334,7 +334,7 @@ func TestDocVectorMisc(t *testing.T) { indexer.AddDoc(&testDoc{Content: "foo bar baz"}) indexer.Commit() searcher, _ := OpenIndexSearcher(folder) - dv := searcher.fetchDocVec(1) + dv, _ := searcher.fetchDocVec(1) fieldBuf := dv.fieldBuf("content"); if fieldBuf == nil { t.Errorf("fieldBuf returned nil") diff --git a/go/lucy/search.go b/go/lucy/search.go index 44f5054f7..147af6305 100644 --- a/go/lucy/search.go +++ b/go/lucy/search.go @@ -88,6 +88,24 @@ func (s *SearcherIMP) ReadDoc(docID int32, doc interface{}) error { } } +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")) @@ -109,6 +127,19 @@ func (s *SearcherIMP) Hits(query interface{}, offset uint32, numWanted uint32, 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 +} + type setScorer interface { SetScore(float32) } diff --git a/go/lucy/search_test.go b/go/lucy/search_test.go index fcd3c3355..50d7faefa 100644 --- a/go/lucy/search_test.go +++ b/go/lucy/search_test.go @@ -418,7 +418,7 @@ type simpleTestDoc struct { func TestHitsBasics(t *testing.T) { index := createTestIndex("a", "b") searcher, _ := OpenIndexSearcher(index) - topDocs := searcher.topDocs(NewTermQuery("content", "a"), 10, nil) + topDocs, _ := searcher.topDocs(NewTermQuery("content", "a"), 10, nil) hits := NewHits(searcher, topDocs, 0) if got := hits.TotalHits(); got != topDocs.getTotalHits() { t.Errorf("TotalHits is off: %d", got) @@ -604,8 +604,13 @@ func TestIndexSearcherMisc(t *testing.T) { if _, ok := searcher.GetReader().(PolyReader); !ok { t.Error("GetReader") } - if _, ok := searcher.fetchDocVec(4).(DocVector); !ok { - t.Error("DocVector") + doc, err := searcher.FetchDoc(4) + if _, ok := doc.(Doc); !ok || err != nil { + t.Error("FetchDoc: %v", err) + } + docVec, err := searcher.fetchDocVec(4) + if _, ok := docVec.(DocVector); !ok || err != nil { + t.Error("FetchDocVec: %v", err) } } @@ -640,7 +645,10 @@ func TestIndexSearcherHits(t *testing.T) { func TestIndexSearcherTopDocs(t *testing.T) { index := createTestIndex("a", "b") searcher, _ := OpenIndexSearcher(index) - topDocs := searcher.topDocs(NewTermQuery("content", "b"), 10, nil) + topDocs, err := searcher.topDocs(NewTermQuery("content", "b"), 10, nil) + if err != nil { + t.Errorf("topDocs: %v", err) + } matchDocs := topDocs.GetMatchDocs() if docID := matchDocs[0].getDocID(); docID != 2 { t.Errorf("TopDocs expected 2, got %d", docID) From 3d636a19d41e9f049aaa444db097de5e31895e9a Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Thu, 12 Nov 2015 19:11:48 -0800 Subject: [PATCH 05/12] Add error retval for SortCache Go bindings. Some methods need to trap exceptions and return error objects. --- go/build.go | 6 ++++++ go/lucy/index.go | 31 +++++++++++++++++++++++++++++++ go/lucy/index_test.go | 15 +++++++++------ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/go/build.go b/go/build.go index 2b94cc5cb..ccd606b91 100644 --- a/go/build.go +++ b/go/build.go @@ -202,6 +202,12 @@ func specClasses(parcel *cfc.Parcel) { segBinding.SpecMethod("Write_File", "WriteFile(Folder) error") segBinding.Register() + sortCacheBinding := cfc.NewGoClass(parcel, "Lucy::Index::SortCache") + sortCacheBinding.SpecMethod("Value", "Value(int32) (interface{}, error)") + sortCacheBinding.SpecMethod("Ordinal", "Ordinal(int32) (int32, error)") + sortCacheBinding.SpecMethod("Find", "Find(interface{}) (int32, error)") + sortCacheBinding.Register() + schemaBinding := cfc.NewGoClass(parcel, "Lucy::Plan::Schema") schemaBinding.SpecMethod("All_Fields", "AllFields() []string") schemaBinding.Register() diff --git a/go/lucy/index.go b/go/lucy/index.go index f186f7dfa..230e050f0 100644 --- a/go/lucy/index.go +++ b/go/lucy/index.go @@ -23,6 +23,7 @@ package lucy #include "Lucy/Index/TermVector.h" #include "Lucy/Index/Segment.h" #include "Lucy/Index/Snapshot.h" +#include "Lucy/Index/SortCache.h" #include "Lucy/Document/Doc.h" #include "Lucy/Plan/Schema.h" #include "Clownfish/Hash.h" @@ -384,3 +385,33 @@ func (s *SegmentIMP) ReadFile(folder Folder) error { C.LUCY_Seg_Read_File(self, folderC) }) } + +func (s *SortCacheIMP) Value(ord int32) (retval interface{}, err error) { + err = clownfish.TrapErr(func() { + self := (*C.lucy_SortCache)(clownfish.Unwrap(s, "s")) + retvalCF := C.LUCY_SortCache_Value(self, C.int32_t(ord)) + defer C.cfish_decref(unsafe.Pointer(retvalCF)) + retval = clownfish.ToGo(unsafe.Pointer(retvalCF)) + }) + return retval, err +} + +func (s *SortCacheIMP) Ordinal(docId int32) (retval int32, err error) { + err = clownfish.TrapErr(func() { + self := (*C.lucy_SortCache)(clownfish.Unwrap(s, "s")) + retvalCF := C.LUCY_SortCache_Ordinal(self, C.int32_t(docId)) + retval = int32(retvalCF) + }) + return retval, err +} + +func (s *SortCacheIMP) Find(term interface{}) (retval int32, err error) { + err = clownfish.TrapErr(func() { + self := (*C.lucy_SortCache)(clownfish.Unwrap(s, "s")) + termCF := (*C.cfish_Obj)(clownfish.GoToClownfish(term, unsafe.Pointer(C.CFISH_OBJ), true)) + defer C.cfish_decref(unsafe.Pointer(termCF)) + retvalCF := C.LUCY_SortCache_Find(self, termCF) + retval = int32(retvalCF) + }) + return retval, err +} diff --git a/go/lucy/index_test.go b/go/lucy/index_test.go index 5fb4d8b29..e7c93a90d 100644 --- a/go/lucy/index_test.go +++ b/go/lucy/index_test.go @@ -386,6 +386,8 @@ func TestSnapshotMisc(t *testing.T) { } func TestSortCacheMisc(t *testing.T) { + var err error + schema := NewSchema() spec := NewFullTextType(NewStandardTokenizer()) spec.SetSortable(true) @@ -410,18 +412,19 @@ func TestSortCacheMisc(t *testing.T) { t.Errorf("GetOrdWidth: %d", width) } - if lowest, ok := sortCache.Value(0).(string); !ok || lowest != "bar" { - t.Errorf("Ord") + lowest, err := sortCache.Value(0) + if _, ok := lowest.(string); err != nil || !ok || lowest != "bar" { + t.Errorf("Value: %v", err) } - if ord := sortCache.Ordinal(1); ord != 2 { // "foo" is ordinal 2 - t.Errorf("Ordinal: %d", ord) + if ord, err := sortCache.Ordinal(1); err != nil || ord != 2 { // "foo" is ordinal 2 + t.Errorf("Ordinal: %d, %v", ord, err) } if nullOrd := sortCache.GetNullOrd(); nullOrd != 3 { t.Errorf("GetNullOrd: %d", nullOrd) } - if ord := sortCache.Find("foo"); ord != 2 { - t.Errorf("Find: %d", ord) + if ord, err := sortCache.Find("foo"); err != nil || ord != 2 { + t.Errorf("Find: %d, %v", ord, err) } if sortCache.getNativeOrds() { From 6d0a55f7387df4714386b7ffce9ba554c0c384f4 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Thu, 12 Nov 2015 19:38:23 -0800 Subject: [PATCH 06/12] Add error to Go binding for MakeMatcher. --- go/build.go | 4 ++++ go/lucy/search.go | 16 ++++++++++++++++ go/lucy/search_test.go | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/go/build.go b/go/build.go index ccd606b91..fc73f6498 100644 --- a/go/build.go +++ b/go/build.go @@ -228,6 +228,10 @@ func specClasses(parcel *cfc.Parcel) { hitsBinding.SetSuppressStruct(true) hitsBinding.Register() + compilerBinding := cfc.NewGoClass(parcel, "Lucy::Search::Compiler") + compilerBinding.SpecMethod("Make_Matcher", "MakeMatcher(SegReader, bool) (Matcher, error)") + compilerBinding.Register() + andQueryBinding := cfc.NewGoClass(parcel, "Lucy::Search::ANDQuery") andQueryBinding.SetSuppressCtor(true) andQueryBinding.Register() diff --git a/go/lucy/search.go b/go/lucy/search.go index 147af6305..c5390444d 100644 --- a/go/lucy/search.go +++ b/go/lucy/search.go @@ -25,6 +25,7 @@ package lucy #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/ANDQuery.h" #include "Lucy/Search/ORQuery.h" @@ -244,6 +245,21 @@ func (td *TopDocsIMP) GetMatchDocs() []MatchDoc { return slice } +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 { diff --git a/go/lucy/search_test.go b/go/lucy/search_test.go index 50d7faefa..a92708c60 100644 --- a/go/lucy/search_test.go +++ b/go/lucy/search_test.go @@ -95,6 +95,11 @@ func TestTermCompilerMisc(t *testing.T) { checkQuerySerialize(t, compiler) checkQueryEquals(t, compiler) checkQueryToStringHasFoo(t, compiler) + segReaders := searcher.GetReader().SegReaders() + matcher, err := compiler.MakeMatcher(segReaders[0].(SegReader), false) + if matcher == nil || err != nil { + t.Errorf("MakeMatcher: %v", err) + } } func TestTermCompilerWeighting(t *testing.T) { From dac0e2bd5d4f93f84842d393a59d3f84f215bf88 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Mon, 16 Nov 2015 14:30:20 -0800 Subject: [PATCH 07/12] Add error to Go binding for MakeCompiler. --- go/build.go | 4 ++++ go/lucy/search.go | 13 +++++++++++++ go/lucy/search_test.go | 6 +++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/go/build.go b/go/build.go index fc73f6498..6dff0740f 100644 --- a/go/build.go +++ b/go/build.go @@ -228,6 +228,10 @@ func specClasses(parcel *cfc.Parcel) { hitsBinding.SetSuppressStruct(true) hitsBinding.Register() + queryBinding := cfc.NewGoClass(parcel, "Lucy::Search::Query") + queryBinding.SpecMethod("Make_Compiler", "MakeCompiler(Searcher, float32, bool) (Compiler, error)") + queryBinding.Register() + compilerBinding := cfc.NewGoClass(parcel, "Lucy::Search::Compiler") compilerBinding.SpecMethod("Make_Matcher", "MakeMatcher(SegReader, bool) (Matcher, error)") compilerBinding.Register() diff --git a/go/lucy/search.go b/go/lucy/search.go index c5390444d..2db169b4e 100644 --- a/go/lucy/search.go +++ b/go/lucy/search.go @@ -245,6 +245,19 @@ func (td *TopDocsIMP) GetMatchDocs() []MatchDoc { 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")) diff --git a/go/lucy/search_test.go b/go/lucy/search_test.go index a92708c60..1127f7227 100644 --- a/go/lucy/search_test.go +++ b/go/lucy/search_test.go @@ -55,9 +55,9 @@ func checkQueryEquals(t *testing.T, query Query) { func checkQueryMakeCompiler(t *testing.T, query Query) { index := createTestIndex("foo", "bar", "baz") searcher, _ := OpenIndexSearcher(index) - compiler := query.MakeCompiler(searcher, 1.0, false) - if got, ok := compiler.(Compiler); !ok { - t.Error("MakeCompiler failed: got '%v'", got) + compiler, err := query.MakeCompiler(searcher, 1.0, false) + if _, ok := compiler.(Compiler); !ok || err != nil { + t.Error("MakeCompiler for %v failed: %v", query, err) } } From dd4328b3a0c528e6d168c1c241bc07253c2e2c4b Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Mon, 16 Nov 2015 15:35:54 -0800 Subject: [PATCH 08/12] Catch errors in Go binding for DH_Next(). Use the same approach we took with Hits_Next(): trap errors and capture them to a member variable, and expect the user to check that no errors occurred after iteration finishes. --- go/build.go | 3 +++ go/lucy/store.go | 21 +++++++++++++++++++++ go/lucy/store_test.go | 3 +++ 3 files changed, 27 insertions(+) diff --git a/go/build.go b/go/build.go index 6dff0740f..6c5525064 100644 --- a/go/build.go +++ b/go/build.go @@ -350,6 +350,9 @@ func specClasses(parcel *cfc.Parcel) { dhBinding := cfc.NewGoClass(parcel, "Lucy::Store::DirHandle") dhBinding.SpecMethod("Close", "Close() error") + dhBinding.SpecMethod("Next", "next() bool") + dhBinding.SpecMethod("", "Error() error") + dhBinding.SetSuppressStruct(true) dhBinding.Register() lockBinding := cfc.NewGoClass(parcel, "Lucy::Store::Lock") diff --git a/go/lucy/store.go b/go/lucy/store.go index 6fabd160b..7af82297e 100644 --- a/go/lucy/store.go +++ b/go/lucy/store.go @@ -39,6 +39,11 @@ import "fmt" import "git-wip-us.apache.org/repos/asf/lucy-clownfish.git/runtime/go/clownfish" +type DirHandleIMP struct { + clownfish.ObjIMP + err error +} + func (e *LockErrIMP) Error() string { self := ((*C.lucy_LockErr)(unsafe.Pointer(e.TOPTR()))) return clownfish.CFStringToGo(unsafe.Pointer(C.LUCY_LockErr_Get_Mess(self))) @@ -644,6 +649,22 @@ func OpenRAMFileHandle(path string, flags uint32, ramFile RAMFile) (fh RAMFileHa return fh, err } +func (dh *DirHandleIMP) Error() error { + return dh.err +} + +func (dh *DirHandleIMP) next() bool { + var retval bool + dh.err = clownfish.TrapErr(func() { + self := (*C.lucy_DirHandle)(clownfish.Unwrap(dh, "dh")) + retval = bool(C.LUCY_DH_Next(self)) + }) + if dh.err != nil { + return false + } + return retval +} + func (dh *DirHandleIMP) Close() error { return clownfish.TrapErr(func() { self := (*C.lucy_DirHandle)(clownfish.Unwrap(dh, "dh")) diff --git a/go/lucy/store_test.go b/go/lucy/store_test.go index 69bb0d421..c4225256d 100644 --- a/go/lucy/store_test.go +++ b/go/lucy/store_test.go @@ -598,6 +598,9 @@ func runDirHandleCommonTests(t *testing.T, folder Folder, makeDH func() DirHandl t.Errorf("Unexpected entry: '%s'", entry) } } + if got := dh.Error(); got != nil { + t.Errorf("Error() should return nil after iteration: %v", got) + } if count != 2 { t.Errorf("Didn't get to all entries, found only %d", count) } From d5063a519200d52c1abeec263bd008f176fa94bd Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Mon, 16 Nov 2015 16:11:22 -0800 Subject: [PATCH 09/12] Tune and test Go bindings for Stepper. --- go/build.go | 8 +++++ go/lucy/util.go | 69 ++++++++++++++++++++++++++++++++++++++++++++ go/lucy/util_test.go | 53 ++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 go/lucy/util.go create mode 100644 go/lucy/util_test.go diff --git a/go/build.go b/go/build.go index 6c5525064..3b6fc683b 100644 --- a/go/build.go +++ b/go/build.go @@ -365,6 +365,14 @@ func specClasses(parcel *cfc.Parcel) { cfWriterBinding := cfc.NewGoClass(parcel, "Lucy::Store::CompoundFileWriter") cfWriterBinding.SpecMethod("Consolidate", "Consolidate() error") cfWriterBinding.Register() + + stepperBinding := cfc.NewGoClass(parcel, "Lucy::Util::Stepper") + stepperBinding.SpecMethod("Write_Key_Frame", "WriteKeyFrame(OutStream, interface{}) error") + stepperBinding.SpecMethod("Write_Delta", "WriteDelta(OutStream, interface{}) error") + stepperBinding.SpecMethod("Read_Key_Frame", "ReadKeyFrame(InStream) error") + stepperBinding.SpecMethod("Read_Delta", "ReadDelta(InStream) error") + stepperBinding.SpecMethod("Read_Record", "readRecord(InStream) error") + stepperBinding.Register() } func build() { diff --git a/go/lucy/util.go b/go/lucy/util.go new file mode 100644 index 000000000..a57de8926 --- /dev/null +++ b/go/lucy/util.go @@ -0,0 +1,69 @@ +/* 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 + +/* +#include "Lucy/Util/Stepper.h" +*/ +import "C" +import "unsafe" + +import "git-wip-us.apache.org/repos/asf/lucy-clownfish.git/runtime/go/clownfish" + +func (s *StepperIMP) WriteKeyFrame(outstream OutStream, value interface{}) error { + return clownfish.TrapErr(func() { + self := (*C.lucy_Stepper)(clownfish.Unwrap(s, "s")) + outstreamCF := (*C.lucy_OutStream)(clownfish.Unwrap(outstream, "outstream")) + valueCF := (*C.cfish_Obj)(clownfish.GoToClownfish(value, unsafe.Pointer(C.CFISH_OBJ), false)) + defer C.cfish_decref(unsafe.Pointer(valueCF)) + C.LUCY_Stepper_Write_Key_Frame(self, outstreamCF, valueCF) + }) +} + +func (s *StepperIMP) WriteDelta(outstream OutStream, value interface{}) error { + return clownfish.TrapErr(func() { + self := (*C.lucy_Stepper)(clownfish.Unwrap(s, "s")) + outstreamCF := (*C.lucy_OutStream)(clownfish.Unwrap(outstream, "outstream")) + valueCF := (*C.cfish_Obj)(clownfish.GoToClownfish(value, unsafe.Pointer(C.CFISH_OBJ), false)) + defer C.cfish_decref(unsafe.Pointer(valueCF)) + C.LUCY_Stepper_Write_Delta(self, outstreamCF, valueCF) + }) +} + +func (s *StepperIMP) ReadKeyFrame(instream InStream) error { + return clownfish.TrapErr(func() { + self := (*C.lucy_Stepper)(clownfish.Unwrap(s, "s")) + instreamCF := (*C.lucy_InStream)(clownfish.Unwrap(instream, "instream")) + C.LUCY_Stepper_Read_Key_Frame(self, instreamCF) + }) +} + +func (s *StepperIMP) ReadDelta(instream InStream) error { + return clownfish.TrapErr(func() { + self := (*C.lucy_Stepper)(clownfish.Unwrap(s, "s")) + instreamCF := (*C.lucy_InStream)(clownfish.Unwrap(instream, "instream")) + C.LUCY_Stepper_Read_Delta(self, instreamCF) + }) +} + +func (s *StepperIMP) readRecord(instream InStream) error { + return clownfish.TrapErr(func() { + self := (*C.lucy_Stepper)(clownfish.Unwrap(s, "s")) + instreamCF := (*C.lucy_InStream)(clownfish.Unwrap(instream, "instream")) + C.LUCY_Stepper_Read_Record(self, instreamCF) + }) +} diff --git a/go/lucy/util_test.go b/go/lucy/util_test.go new file mode 100644 index 000000000..38a450360 --- /dev/null +++ b/go/lucy/util_test.go @@ -0,0 +1,53 @@ +/* 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 + +import "testing" +import "git-wip-us.apache.org/repos/asf/lucy-clownfish.git/runtime/go/clownfish" + +func TestStepperMisc(t *testing.T) { + var err error + stepper := NewTextTermStepper() + folder := NewRAMFolder("") + out, _ := folder.OpenOut("foo.dat") + err = stepper.WriteKeyFrame(out, "foo") + if err != nil { + t.Errorf("WriteKeyFrame: %v", err) + } + + bb := clownfish.NewByteBuf(0) + //bb.Cat([]byte("food")) + err = stepper.WriteDelta(out, bb) + if err != nil { + t.Errorf("WriteDelta: %v", err) + } + out.Close() + stepper.Reset() + in, _ := folder.OpenIn("foo.dat") + err = stepper.ReadKeyFrame(in) + if err != nil { + t.Errorf("ReadKeyFrame: %v", err) + } + err = stepper.ReadDelta(in) + if err != nil { + t.Errorf("ReadDelta: %v", err) + } + err = stepper.ReadDelta(in) + if err == nil { + t.Errorf("Expected error when reading past EOF") + } +} From 91dd3894b16cc3f5140c2ac398d193a59f4b19a0 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Mon, 16 Nov 2015 17:15:05 -0800 Subject: [PATCH 10/12] Tune and test Go bindings for Doc. --- go/build.go | 6 ++++ go/lucy/document.go | 21 ++++++++++++ go/lucy/document_test.go | 74 ++++++++++++++++++++++++++++++++++++++++ go/lucy/lucy.go | 2 +- 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 go/lucy/document_test.go diff --git a/go/build.go b/go/build.go index 3b6fc683b..8db210945 100644 --- a/go/build.go +++ b/go/build.go @@ -159,6 +159,12 @@ func specClasses(parcel *cfc.Parcel) { polyAnalyzerBinding.SetSuppressCtor(true) polyAnalyzerBinding.Register() + docBinding := cfc.NewGoClass(parcel, "Lucy::Document::Doc") + docBinding.SpecMethod("", "GetFields() map[string]interface{}") + docBinding.SpecMethod("", "SetFields(map[string]interface{})") + docBinding.SpecMethod("Field_Names", "FieldNames() []string") + docBinding.Register() + indexerBinding := cfc.NewGoClass(parcel, "Lucy::Index::Indexer") indexerBinding.SpecMethod("", "Close() error") indexerBinding.SpecMethod("Add_Doc", "AddDoc(doc interface{}) error") diff --git a/go/lucy/document.go b/go/lucy/document.go index 1221810d6..1d8c289e0 100644 --- a/go/lucy/document.go +++ b/go/lucy/document.go @@ -48,3 +48,24 @@ func fetchDocFields(d *C.lucy_Doc) map[string]interface{} { } return fieldsGo } + +func (d *DocIMP) GetFields() map[string]interface{} { + self := (*C.lucy_Doc)(clownfish.Unwrap(d, "d")) + return fetchDocFields(self) +} + +func (d *DocIMP) SetFields(fields map[string]interface{}) { + self := (*C.lucy_Doc)(clownfish.Unwrap(d, "d")) + ivars := C.lucy_Doc_IVARS(self) + oldID := uintptr(unsafe.Pointer(ivars.fields)) + newFieldsID := registry.store(fields) + ivars.fields = unsafe.Pointer(newFieldsID) + registry.delete(oldID) +} + +func (d *DocIMP) FieldNames() []string { + self := (*C.lucy_Doc)(unsafe.Pointer(d.TOPTR())) + fieldsCF := C.LUCY_Doc_Field_Names(self) + defer C.cfish_decref(unsafe.Pointer(fieldsCF)) + return vecToStringSlice(fieldsCF) +} diff --git a/go/lucy/document_test.go b/go/lucy/document_test.go new file mode 100644 index 000000000..2cf631375 --- /dev/null +++ b/go/lucy/document_test.go @@ -0,0 +1,74 @@ +/* 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 + +import "testing" +import "reflect" +import "git-wip-us.apache.org/repos/asf/lucy-clownfish.git/runtime/go/clownfish" + +func TestDocMisc(t *testing.T) { + doc := NewDoc(1) + if got := doc.GetDocID(); got != 1 { + t.Errorf("GetDocID: %d", got) + } + doc.SetDocID(42) + if got := doc.GetDocID(); got != 42 { + t.Errorf("Set/GetDocID: %d", got) + } + fields := map[string]interface{}{"content": "foo"} + doc.SetFields(fields) + if got, ok := doc.Extract("content").(string); !ok || got != "foo" { + t.Errorf("Extract: %v", got) + } + doc.Store("content", "bar") + retrievedFields := doc.GetFields() + if got, ok := retrievedFields["content"].(string); !ok || got != "bar" { + t.Errorf("Store/GetFields: %v", got) + } + if got := doc.GetSize(); got != 1 { + t.Errorf("GetSize: %d", got) + } + if got := doc.FieldNames(); !reflect.DeepEqual(got, []string{"content"}) { + t.Errorf("FieldNames: %v", got) + } + checkDocSerialize(t, doc) + checkdocDumpLoad(t, doc) +} + +func checkDocSerialize(t *testing.T, doc Doc) { + folder := NewRAMFolder("") + outStream, _ := folder.OpenOut("foo") + doc.serialize(outStream) + outStream.Close() + inStream, _ := folder.OpenIn("foo") + dupe := clownfish.GetClass(doc).MakeObj().(Doc).deserialize(inStream) + if !doc.Equals(dupe) { + t.Errorf("Unsuccessful serialization round trip -- expected '%v', got '%v'", + doc.ToString(), dupe.ToString()) + } +} + +func checkdocDumpLoad(t *testing.T, doc Doc) { + t.Skip("Dump/Load are TODO") + return + dupe := clownfish.GetClass(doc).MakeObj().(Doc) + dupe = dupe.load(doc.dump()).(Doc) + if !doc.Equals(dupe) { + t.Errorf("Unsuccessful dump/load round trip -- expected '%v', got '%v'", + doc.ToString(), dupe.ToString()) + } +} diff --git a/go/lucy/lucy.go b/go/lucy/lucy.go index 585a1923e..03307bb6e 100644 --- a/go/lucy/lucy.go +++ b/go/lucy/lucy.go @@ -273,7 +273,7 @@ func GOLUCY_Doc_init(d *C.lucy_Doc, fields unsafe.Pointer, docID C.int32_t) *C.l //export GOLUCY_Doc_Set_Fields func GOLUCY_Doc_Set_Fields(d *C.lucy_Doc, fields unsafe.Pointer) { - panic(clownfish.NewErr("Set_Fields unsupported in Go bindings")) + panic(clownfish.NewErr("Set_Fields unsupported from C-space in Go")) } //export GOLUCY_Doc_Get_Size From 66120820174ffd6bc2459179fe642d094cc3ad57 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Mon, 16 Nov 2015 18:16:28 -0800 Subject: [PATCH 11/12] Test Go bindings for HitDoc. --- go/lucy/document_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/go/lucy/document_test.go b/go/lucy/document_test.go index 2cf631375..4a82baaed 100644 --- a/go/lucy/document_test.go +++ b/go/lucy/document_test.go @@ -49,6 +49,19 @@ func TestDocMisc(t *testing.T) { checkdocDumpLoad(t, doc) } +func TestHitDocMisc(t *testing.T) { + hitDoc := NewHitDoc(42, 1.5) + if got := hitDoc.GetScore(); got != 1.5 { + t.Errorf("GetScore: %f", got) + } + hitDoc.SetScore(2.0) + if got := hitDoc.GetScore(); got != 2.0 { + t.Errorf("Set/GetScore: %f", got) + } + checkDocSerialize(t, hitDoc) + checkdocDumpLoad(t, hitDoc) +} + func checkDocSerialize(t *testing.T, doc Doc) { folder := NewRAMFolder("") outStream, _ := folder.OpenOut("foo") From 8208210b3f5877b2056fecd4fa19f4de94ccdd6e Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Mon, 16 Nov 2015 18:33:40 -0800 Subject: [PATCH 12/12] Trap errors in Go bindings for Matcher_Next. Use the same design pattern as Hits_Next: trap errors in a Go struct member and expect the user to check after iteration finishes. --- go/build.go | 6 ++++++ go/lucy/search.go | 21 +++++++++++++++++++++ go/lucy/search_test.go | 3 +++ 3 files changed, 30 insertions(+) diff --git a/go/build.go b/go/build.go index 8db210945..e1fef50da 100644 --- a/go/build.go +++ b/go/build.go @@ -250,6 +250,12 @@ func specClasses(parcel *cfc.Parcel) { orQueryBinding.SetSuppressCtor(true) orQueryBinding.Register() + matcherBinding := cfc.NewGoClass(parcel, "Lucy::Search::Matcher") + matcherBinding.SpecMethod("Next", "Next() int32") + matcherBinding.SpecMethod("", "Error() error") + matcherBinding.SetSuppressStruct(true) + matcherBinding.Register() + andMatcherBinding := cfc.NewGoClass(parcel, "Lucy::Search::ANDMatcher") andMatcherBinding.SetSuppressCtor(true) andMatcherBinding.Register() diff --git a/go/lucy/search.go b/go/lucy/search.go index 2db169b4e..e202b9df5 100644 --- a/go/lucy/search.go +++ b/go/lucy/search.go @@ -60,6 +60,11 @@ type HitsIMP struct { 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)) @@ -293,6 +298,22 @@ func NewORQuery(children []Query) ORQuery { 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)) diff --git a/go/lucy/search_test.go b/go/lucy/search_test.go index 1127f7227..3d4fc5037 100644 --- a/go/lucy/search_test.go +++ b/go/lucy/search_test.go @@ -273,6 +273,9 @@ func checkMatcher(t *testing.T, matcher Matcher, supportsScore bool) { if got := matcher.Next(); got != 0 { t.Error("Next (iteration finished): %d", got) } + if got := matcher.Error(); got != nil { + t.Error("Error after iteration finished: %v", got) + } } func TestMockMatcherBasics(t *testing.T) {