├── README.md ├── Makefile ├── models ├── event.go └── event_test.go └── conn ├── bleve.go └── bleve_test.go /README.md: -------------------------------------------------------------------------------- 1 | # Studying Bleve Search 2 | 3 | My studies about [Bleve Search](http://www.blevesearch.com/) 4 | 5 | Code explained on [my blog](http://nassor.me) 6 | 7 | ## Commands 8 | 9 | ```bash 10 | $ make # execute all tests 11 | $ make deps # get all dependencies 12 | ``` 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: tests 2 | 3 | tests: 4 | @ go test -v ./conn 5 | @ go test -v ./models 6 | 7 | deps: 8 | go get github.com/blevesearch/bleve/... # bleve package 9 | go get github.com/mattn/go-sqlite3 # sqlite3 package 10 | go get github.com/jinzhu/gorm # orm package 11 | -------------------------------------------------------------------------------- /models/event.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/blevesearch/bleve" 7 | ) 8 | 9 | // Event is an event! wow! ;D 10 | type Event struct { 11 | ID int 12 | Name string 13 | Description string 14 | Local string 15 | Website string 16 | Start time.Time 17 | End time.Time 18 | } 19 | 20 | // Index is used to add the event in the bleve index. 21 | func (e *Event) Index(index bleve.Index) error { 22 | err := index.Index(string(e.ID), e) 23 | return err 24 | } 25 | -------------------------------------------------------------------------------- /conn/bleve.go: -------------------------------------------------------------------------------- 1 | package conn 2 | 3 | import "github.com/blevesearch/bleve" 4 | 5 | var bleveIdx bleve.Index 6 | 7 | // Bleve connect or create the index persistence 8 | func Bleve(indexPath string) (bleve.Index, error) { 9 | 10 | // with bleveIdx isn't set... 11 | if bleveIdx == nil { 12 | var err error 13 | // try to open de persistence file... 14 | bleveIdx, err = bleve.Open(indexPath) 15 | // if doesn't exists or something goes wrong... 16 | if err != nil { 17 | // create a new mapping file and create a new index 18 | mapping := bleve.NewIndexMapping() 19 | bleveIdx, err = bleve.New(indexPath, mapping) 20 | if err != nil { 21 | return nil, err 22 | } 23 | } 24 | } 25 | 26 | // return the index 27 | return bleveIdx, nil 28 | } 29 | -------------------------------------------------------------------------------- /conn/bleve_test.go: -------------------------------------------------------------------------------- 1 | package conn 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | const ( 9 | checkMark = "\u2713" 10 | ballotX = "\u2717" 11 | testIdx = "test.index" 12 | ) 13 | 14 | func TestCreateIndex(t *testing.T) { 15 | should := "Should be able to create an index if don't exists" 16 | idx, err := Bleve(testIdx) 17 | if err != nil || idx == nil { 18 | t.Error(should, ballotX, err) 19 | } else { 20 | t.Log(should, checkMark) 21 | } 22 | 23 | idxDestroy() 24 | } 25 | 26 | func TestOpenIndex(t *testing.T) { 27 | should := "Should be able to open an index if don't exists" 28 | 29 | Bleve("test.index") 30 | idx, err := Bleve(testIdx) 31 | if err != nil || idx == nil { 32 | t.Error(should, ballotX, err) 33 | } else { 34 | t.Log(should, checkMark) 35 | } 36 | 37 | idxDestroy() 38 | } 39 | 40 | func idxDestroy() { 41 | os.RemoveAll(testIdx) 42 | } 43 | -------------------------------------------------------------------------------- /models/event_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | "time" 7 | 8 | "github.com/blevesearch/bleve" 9 | "github.com/jinzhu/gorm" 10 | _ "github.com/mattn/go-sqlite3" 11 | "github.com/nassor/studies-blevesearch/conn" 12 | ) 13 | 14 | const ( 15 | checkMark = "\u2713" 16 | ballotX = "\u2717" 17 | testIdx = "test.bleve" 18 | dbFile = "test.sqlite3.db" 19 | ) 20 | 21 | func TestIndexing(t *testing.T) { 22 | _, eventList := dbCreate() 23 | idx := idxCreate() 24 | 25 | err := eventList[0].Index(idx) 26 | if err != nil { 27 | t.Error("Wasn't possible create the index", err, ballotX) 28 | } else { 29 | t.Log("Should create an event index", checkMark) 30 | } 31 | 32 | idxDestroy() 33 | dbDestroy() 34 | } 35 | 36 | func TestFindByAnything(t *testing.T) { 37 | db, eventList := dbCreate() 38 | idx := idxCreate() 39 | indexEvents(idx, eventList) 40 | 41 | // We are looking to an Event with some string which match with dotGo 42 | query := bleve.NewMatchQuery("dotGo") 43 | searchRequest := bleve.NewSearchRequest(query) 44 | searchResult, err := idx.Search(searchRequest) 45 | if err != nil { 46 | t.Error("Something wrong happen with the search", err, ballotX) 47 | } else { 48 | t.Log("Should search the query", checkMark) 49 | } 50 | 51 | if searchResult.Total != 1 { 52 | t.Error("Only 1 result are expected, got ", searchResult.Total, ballotX) 53 | } else { 54 | t.Log("Should return only one result", checkMark) 55 | } 56 | 57 | event := &Event{} 58 | db.First(&event, &searchResult.Hits[0].ID) 59 | if event.Name != "dotGo 2015" { 60 | t.Error("Expected \"dotGo 2015\", Receive: ", event.Name) 61 | } else { 62 | t.Log("Should return an event with the name equal a", event.Name, checkMark) 63 | } 64 | 65 | idxDestroy() 66 | dbDestroy() 67 | } 68 | 69 | // indexEvents add the eventList to the index 70 | func indexEvents(idx bleve.Index, eventList []Event) { 71 | for _, event := range eventList { 72 | event.Index(idx) 73 | } 74 | } 75 | 76 | // fill the database with some data 77 | func fillDatabase(db *gorm.DB) []Event { 78 | eventList := []Event{ 79 | {1, "dotGo 2015", "The European Go conference", "Paris", "http://www.dotgo.eu/", time.Date(2015, 11, 19, 9, 0, 0, 0, time.UTC), time.Date(2015, 11, 19, 18, 30, 0, 0, time.UTC)}, 80 | 81 | {2, "GopherCon INDIA 2016", "The Go Conference in India", "Bengaluru", "http://www.gophercon.in/", time.Date(2016, 2, 19, 0, 0, 0, 0, time.UTC), time.Date(2016, 2, 20, 23, 59, 0, 0, time.UTC)}, 82 | 83 | {3, "GopherCon 2016", "GopherCon, It is the largest event in the world dedicated solely to the Go programming language. It's attended by the best and the brightest of the Go team and community.", "Denver", "http://gophercon.com/", time.Date(2016, 7, 11, 0, 0, 0, 0, time.UTC), time.Date(2016, 7, 13, 23, 59, 0, 0, time.UTC)}, 84 | } 85 | 86 | // inserting the events 87 | for _, event := range eventList { 88 | db.Create(&event) 89 | } 90 | 91 | return eventList 92 | } 93 | 94 | func idxCreate() bleve.Index { 95 | idx, _ := conn.Bleve(testIdx) 96 | return idx 97 | } 98 | 99 | // create a SQLite3 database file, create an events table and fill with some data. 100 | func dbCreate() (gorm.DB, []Event) { 101 | db, _ := gorm.Open("sqlite3", dbFile) 102 | db.DropTableIfExists(&Event{}) 103 | db.CreateTable(&Event{}) 104 | eventList := fillDatabase(&db) 105 | return db, eventList 106 | } 107 | 108 | func idxDestroy() { 109 | os.RemoveAll(testIdx) 110 | } 111 | 112 | func dbDestroy() { 113 | os.RemoveAll(dbFile) 114 | } 115 | --------------------------------------------------------------------------------