/bleveplugin

Primary LanguageGoApache License 2.0Apache-2.0

bleve plugin

GoDoc

Japanese language analysis plugins for the bleve v2 indexing/search library.

Usage example

blog: 全文検索エンジン Bleve で日本語形態素解析をおこなう

see. example/analyzer/main.go

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/blevesearch/bleve/v2"
	"github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
	"github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
	"github.com/blevesearch/bleve/v2/analysis/token/lowercase"
	"github.com/ikawaha/bleveplugin/analysis/lang/ja"
)

func main() {
	if err := run(os.Args); err != nil {
		log.Println(err)
		os.Exit(1)
	}
	os.Exit(0)
}

func run(_ []string) error {
	// document mapping
	keywordFieldMapping := bleve.NewTextFieldMapping()
	keywordFieldMapping.Analyzer = keyword.Name
	jaTextFieldMapping := bleve.NewTextFieldMapping()
	jaTextFieldMapping.Analyzer = "ja"
	dm := bleve.NewDocumentMapping()
	dm.AddFieldMappingsAt("type", keywordFieldMapping)
	dm.AddFieldMappingsAt("id", jaTextFieldMapping)
	dm.AddFieldMappingsAt("author", jaTextFieldMapping)
	dm.AddFieldMappingsAt("text", jaTextFieldMapping)

	// index mapping
	im := bleve.NewIndexMapping()
	im.TypeField = "type"
	im.AddDocumentMapping("book", dm)
	if err := im.AddCustomTokenizer("ja_tokenizer", map[string]any{
		"type":      ja.Name,
		"dict":      ja.DictIPA,
		"base_form": true,
		"stop_tags": true,
	}); err != nil {
		return fmt.Errorf("failed to create ja tokenizer: %w", err)
	}
	if err := im.AddCustomAnalyzer("ja", map[string]any{
		"type":      custom.Name,
		"tokenizer": "ja_tokenizer",
		"token_filters": []string{
			ja.StopWordsName,
			lowercase.Name,
		},
	}); err != nil {
		return fmt.Errorf("failed to create ja analyzer: %w", err)
	}

	// index
	index, err := bleve.NewMemOnly(im)
	defer index.Close()

	// documents
	docs := []string{
		`{"type": "book", "id": "1:赤い蝋燭と人魚", "author": "小川未明", "text": "人魚は南の方の海にばかり棲んでいるのではありません"}`,
		`{"type": "book", "id": "2:吾輩は猫である", "author": "夏目漱石", "text":   "吾輩は猫である。名前はまだない"}`,
		`{"type": "book", "id": "3:狐と踊れ", "author": "神林長平", "text": "踊っているのでなければ踊らされているのだろうさ"}`,
		`{"type": "book", "id": "4:ダンスダンスダンス", "author": "村上春樹", "text": "音楽の鳴っている間はとにかく踊り続けるんだ。おいらの言っていることはわかるかい?"}`,
	}
	// indexing
	for _, doc := range docs {
		var data map[string]any
		if err := json.Unmarshal([]byte(doc), &data); err != nil {
			log.Printf("SKIP: failed to unmarshal doc: %v, %s", err, doc)
			continue
		}
		if err := index.Index(data["id"].(string), data); err != nil {
			return fmt.Errorf("error indexing document: %w", err)
		}
		// printing ...
		fmt.Printf("indexed document with id:%s, %s\n", data["id"], doc)
	}
	dc, err := index.DocCount()
	if err != nil {
		return fmt.Errorf("failed to count documents: %w", err)
	}
	fmt.Printf("doc count: %d\n --------\n", dc)

	// query
	q := "踊る人形"
	query := bleve.NewMatchQuery(q)
	query.SetField("text")
	req := bleve.NewSearchRequest(query)
	fmt.Printf("query: search field %q, value %q\n", query.Field(), q)

	// search
	result, err := index.Search(req)
	if err != nil {
		log.Fatalf("error executing search: %v", err)
	}
	// search result
	for _, v := range result.Hits {
		fmt.Println(v.ID)
	}
	return nil
}

OUTPUT:

indexed document with id:1:赤い蝋燭と人魚, {"type": "book", "id": "1:赤い蝋燭と人魚", "author": "小川未明", "text": "人魚は南の方の海にばかり棲んでいるのではありません"}
indexed document with id:2:吾輩は猫である, {"type": "book", "id": "2:吾輩は猫である", "author": "夏目漱石", "text":   "吾輩は猫である。名前はまだない"}
indexed document with id:3:狐と踊れ, {"type": "book", "id": "3:狐と踊れ", "author": "神林長平", "text": "踊っているのでなければ踊らされているのだろうさ"}
indexed document with id:4:ダンスダンスダンス, {"type": "book", "id": "4:ダンスダンスダンス", "author": "村上春樹", "text": "音楽の鳴っている間はとにかく踊り続けるんだ。おいらの言っていることはわかるかい?"}
doc count: 4
 --------
query: search field "text", value "踊る人形"
3:狐と踊れ
4:ダンスダンスダンス