diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8dde3d8..6422bfc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,7 @@ on: push: tags: - - 'go/v*.*.*' + - 'v*.*.*' pull_request: workflow_dispatch: diff --git a/Makefile b/Makefile index 3dfc5bb..0194605 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,8 @@ all: @set -e; build-verify: - CGO_LDFLAGS='-static' GOOS="windows" GOARCH="amd64" CGO_ENABLED="1" CC="x86_64-w64-mingw32-gcc" go build -v main.go - CGO_LDFLAGS='-static' GOOS="linux" GOARCH="amd64" CGO_ENABLED="1" CC="x86_64-linux-musl-gcc" go build -v main.go + CGO_LDFLAGS='-static' GOOS="windows" GOARCH="amd64" CGO_ENABLED="1" CC="x86_64-w64-mingw32-gcc" go build -v example.go + CGO_LDFLAGS='-static' GOOS="linux" GOARCH="amd64" CGO_ENABLED="1" CC="x86_64-linux-musl-gcc" go build -v example.go CGO_ENABLED="1" gomobile bind -v -target=android -androidapi 26 -o lib.aar github.com/anyproto/tantivy-go/tantivy/gomobile CGO_ENABLED="1" gomobile bind -v -target=ios -o Lib.xcframework github.com/anyproto/tantivy-go/tantivy/gomobile diff --git a/tantivy/document.go b/tantivy/document.go index 7949ea8..5574f6b 100644 --- a/tantivy/document.go +++ b/tantivy/document.go @@ -9,11 +9,25 @@ import ( type Document struct{ ptr *C.Document } +// NewDocument creates a new instance of Document. +// +// Returns: +// - *Document: a pointer to a newly created Document instance. func NewDocument() *Document { ptr := C.document_create() return &Document{ptr: ptr} } +// AddField adds a field with the specified name and value to the document using the given index. +// Returns an error if adding the field fails. +// +// Parameters: +// - fieldName: the name of the field to add +// - fieldValue: the value of the field to add +// - index: the index to use for adding the field +// +// Returns: +// - error: an error if adding the field fails, or nil if the operation is successful func (d *Document) AddField(fieldName, fieldValue string, index *Index) error { cFieldName := C.CString(fieldName) defer C.string_free(cFieldName) @@ -25,6 +39,16 @@ func (d *Document) AddField(fieldName, fieldValue string, index *Index) error { return tryExtractError(errBuffer) } +// ToJson converts the document to its JSON representation based on the provided schema. +// Optionally, specific fields can be included in the JSON output. +// +// Parameters: +// - schema: the schema to use for converting the document to JSON +// - includeFields: optional variadic parameter specifying the fields to include in the JSON output +// +// Returns: +// - string: the JSON representation of the document +// - error: an error if the conversion fails, or nil if the operation is successful func (d *Document) ToJson(schema *Schema, includeFields ...string) (string, error) { var errBuffer *C.char @@ -46,6 +70,17 @@ func (d *Document) ToJson(schema *Schema, includeFields ...string) (string, erro return C.GoString(cStr), nil } +// ToModel converts a document to a model of type T using the provided schema and a conversion function. +// +// Parameters: +// - doc: the document to convert +// - schema: the schema to use for converting the document to JSON +// - includeFields: optional fields to include in the JSON output +// - f: a function that takes a JSON string and converts it to a model of type T +// +// Returns: +// - T: the model of type T resulting from the conversion +// - error: an error if the conversion fails, or nil if the operation is successful func ToModel[T any](doc *Document, schema *Schema, includeFields []string, f func(json string) (T, error)) (T, error) { json, err := doc.ToJson(schema, includeFields...) if err != nil { diff --git a/tantivy/index.go b/tantivy/index.go index 5c75621..72e5e1c 100644 --- a/tantivy/index.go +++ b/tantivy/index.go @@ -10,6 +10,15 @@ import ( type Index struct{ ptr *C.Index } +// NewIndexWithSchema creates a new instance of Index with the provided schema. +// +// Parameters: +// - path: The path to the index as a string. +// - schema: A pointer to the Schema to be used. +// +// Returns: +// - *Index: A pointer to a newly created Index instance. +// - error: An error if the index creation fails. func NewIndexWithSchema(path string, schema *Schema) (*Index, error) { cPath := C.CString(path) defer C.string_free(cPath) @@ -22,6 +31,13 @@ func NewIndexWithSchema(path string, schema *Schema) (*Index, error) { return &Index{ptr: ptr}, nil } +// AddAndConsumeDocuments adds and consumes the provided documents to the index. +// +// Parameters: +// - docs: A variadic parameter of pointers to Document to be added and consumed. +// +// Returns: +// - error: An error if adding and consuming the documents fails. func (i *Index) AddAndConsumeDocuments(docs ...*Document) error { if len(docs) == 0 { return nil @@ -35,6 +51,14 @@ func (i *Index) AddAndConsumeDocuments(docs ...*Document) error { return tryExtractError(errBuffer) } +// DeleteDocuments deletes documents from the index based on the specified field and IDs. +// +// Parameters: +// - field: The field name to match against the document IDs. +// - deleteIds: A variadic parameter of document IDs to be deleted. +// +// Returns: +// - error: An error if deleting the documents fails. func (i *Index) DeleteDocuments(field string, deleteIds ...string) error { if len(deleteIds) == 0 { return nil @@ -55,6 +79,11 @@ func (i *Index) DeleteDocuments(field string, deleteIds ...string) error { return tryExtractError(errBuffer) } +// NumDocs returns the number of documents in the index. +// +// Returns: +// - uint64: The number of documents. +// - error: An error if retrieving the document count fails. func (i *Index) NumDocs() (uint64, error) { var errBuffer *C.char numDocs := C.index_num_docs(i.ptr, &errBuffer) @@ -65,6 +94,17 @@ func (i *Index) NumDocs() (uint64, error) { return uint64(numDocs), nil } +// Search performs a search query on the index and returns the search results. +// +// Parameters: +// - query (string): The search query string. +// - docsLimit (uintptr): The maximum number of documents to return. +// - withHighlights (bool): Whether to include highlights in the results. +// - fieldNames (...string): The names of the fields to be included in the search. +// +// Returns: +// - *SearchResult: A pointer to the SearchResult containing the search results. +// - error: An error if the search fails. func (i *Index) Search(query string, docsLimit uintptr, withHighlights bool, fieldNames ...string) (*SearchResult, error) { if len(fieldNames) == 0 { return nil, fmt.Errorf("fieldNames must not be empty") @@ -101,6 +141,16 @@ func (i *Index) Free() { C.index_free(i.ptr) } +// RegisterTextAnalyzerNgram registers a text analyzer using N-grams with the index. +// +// Parameters: +// - tokenizerName (string): The name of the tokenizer to be used. +// - minGram (uintptr): The minimum length of the n-grams. +// - maxGram (uintptr): The maximum length of the n-grams. +// - prefixOnly (bool): Whether to generate only prefix n-grams. +// +// Returns: +// - error: An error if the registration fails. func (i *Index) RegisterTextAnalyzerNgram(tokenizerName string, minGram, maxGram uintptr, prefixOnly bool) error { cTokenizerName := C.CString(tokenizerName) defer C.string_free(cTokenizerName) @@ -110,6 +160,16 @@ func (i *Index) RegisterTextAnalyzerNgram(tokenizerName string, minGram, maxGram return tryExtractError(errBuffer) } +// RegisterTextAnalyzerEdgeNgram registers a text analyzer using edge n-grams with the index. +// +// Parameters: +// - tokenizerName (string): The name of the tokenizer to be used. +// - minGram (uintptr): The minimum length of the edge n-grams. +// - maxGram (uintptr): The maximum length of the edge n-grams. +// - limit (uintptr): The maximum number of edge n-grams to generate. +// +// Returns: +// - error: An error if the registration fails. func (i *Index) RegisterTextAnalyzerEdgeNgram(tokenizerName string, minGram, maxGram uintptr, limit uintptr) error { cTokenizerName := C.CString(tokenizerName) defer C.string_free(cTokenizerName) @@ -118,6 +178,15 @@ func (i *Index) RegisterTextAnalyzerEdgeNgram(tokenizerName string, minGram, max return tryExtractError(errBuffer) } +// RegisterTextAnalyzerSimple registers a simple text analyzer with the index. +// +// Parameters: +// - tokenizerName (string): The name of the tokenizer to be used. +// - textLimit (uintptr): The limit on the length of the text to be analyzed. +// - lang (string): The language code for the text analyzer. +// +// Returns: +// - error: An error if the registration fails. func (i *Index) RegisterTextAnalyzerSimple(tokenizerName string, textLimit uintptr, lang string) error { cTokenizerName := C.CString(tokenizerName) defer C.string_free(cTokenizerName) @@ -129,6 +198,13 @@ func (i *Index) RegisterTextAnalyzerSimple(tokenizerName string, textLimit uintp return tryExtractError(errBuffer) } +// RegisterTextAnalyzerRaw registers a raw text analyzer with the index. +// +// Parameters: +// - tokenizerName (string): The name of the raw tokenizer to be used. +// +// Returns: +// - error: An error if the registration fails. func (i *Index) RegisterTextAnalyzerRaw(tokenizerName string) error { cTokenizerName := C.CString(tokenizerName) defer C.string_free(cTokenizerName) @@ -138,6 +214,16 @@ func (i *Index) RegisterTextAnalyzerRaw(tokenizerName string) error { return tryExtractError(errBuffer) } +// GetSearchResults extracts search results from a SearchResult and converts them into a slice of models. +// +// Parameters: +// - searchResult (*SearchResult): The search results to process. +// - schema (*Schema): The schema to use for converting documents to models. +// - f (func(json string) (T, error)): A function to convert JSON strings to models. +// - includeFields (...string): Optional list of fields to include in the result. +// +// Returns: +// - ([]T, error): A slice of models obtained from the search results, and an error if something goes wrong. func GetSearchResults[T any]( searchResult *SearchResult, schema *Schema, diff --git a/tantivy/schemabuilder.go b/tantivy/schemabuilder.go index b8b77c6..77d174e 100644 --- a/tantivy/schemabuilder.go +++ b/tantivy/schemabuilder.go @@ -12,8 +12,11 @@ type ( ) const ( + // IndexRecordOptionBasic specifies that only basic indexing information should be used. IndexRecordOptionBasic = iota + // IndexRecordOptionWithFreqs specifies that indexing should include term frequencies. IndexRecordOptionWithFreqs + // IndexRecordOptionWithFreqsAndPositions specifies that indexing should include term frequencies and term positions. IndexRecordOptionWithFreqsAndPositions ) @@ -40,6 +43,8 @@ const ( Turkish = "tr" ) +// NewSchemaBuilder creates a new SchemaBuilder instance. +// Returns a pointer to the SchemaBuilder and an error if creation fails. func NewSchemaBuilder() (*SchemaBuilder, error) { ptr := C.schema_builder_new() if ptr == nil { @@ -48,6 +53,17 @@ func NewSchemaBuilder() (*SchemaBuilder, error) { return &SchemaBuilder{ptr: ptr}, nil } +// AddTextField adds a text field to the schema being built. +// +// Parameters: +// - name: The name of the field. +// - stored: Whether the field should be stored in the index. +// - isText: Whether the field should be treated as tantivy text or string for full-text search. +// - isFast: Whether the field should be indexed as tantivy quick field. +// - indexRecordOption: The indexing option to be used (e.g., basic, with frequencies, with frequencies and positions). +// - tokenizer: The name of the tokenizer to be used for the field. +// +// Returns an error if the field could not be added. func (b *SchemaBuilder) AddTextField( name string, stored bool, @@ -74,6 +90,8 @@ func (b *SchemaBuilder) AddTextField( return tryExtractError(errBuffer) } +// BuildSchema finalizes the schema building process and returns the resulting Schema. +// Returns a pointer to the Schema and an error if the schema could not be built. func (b *SchemaBuilder) BuildSchema() (*Schema, error) { var errBuffer *C.char ptr := C.schema_builder_build(b.ptr, &errBuffer) diff --git a/tantivy/searchresult.go b/tantivy/searchresult.go index 2d5b2c6..040ded6 100644 --- a/tantivy/searchresult.go +++ b/tantivy/searchresult.go @@ -9,6 +9,14 @@ import ( type SearchResult struct{ ptr *C.SearchResult } +// Get retrieves a document from the search result at the specified index. +// +// Parameters: +// - index: The index of the document to retrieve. +// +// Returns: +// - A pointer to the Document if successful, or nil if not found. +// - An error if there was an issue retrieving the document. func (r *SearchResult) Get(index uint64) (*Document, error) { var errBuffer *C.char ptr := C.search_result_get_doc(r.ptr, C.uintptr_t(index), &errBuffer) @@ -19,6 +27,11 @@ func (r *SearchResult) Get(index uint64) (*Document, error) { return &Document{ptr: ptr}, nil } +// GetSize returns the number of documents in the search result. +// +// Returns: +// - The size of the search result if successful. +// - An error if there was an issue getting the size. func (r *SearchResult) GetSize() (uint64, error) { var errBuffer *C.char diff --git a/tantivy/tantivy.go b/tantivy/tantivy.go index 620bcdf..798c706 100644 --- a/tantivy/tantivy.go +++ b/tantivy/tantivy.go @@ -27,6 +27,14 @@ const TokenizerRaw = "raw" var doOnce sync.Once +// LibInit initializes the library with an optional directive. +// +// Parameters: +// - directive: A variadic parameter that allows specifying an initialization directive. +// If no directive is provided, the default value "info" is used. +// +// Returns: +// - An error if the initialization fails. func LibInit(directive ...string) error { var initVal string var err error