-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbible.go
122 lines (100 loc) · 2.41 KB
/
bible.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package vulgata
import (
"archive/tar"
"compress/gzip"
"encoding/json"
"fmt"
"github.com/derekparker/trie"
"io"
"os"
"path"
"runtime"
"strings"
)
// Bible contains the old testament and new testament
type Bible struct {
OldTestament Testament
NewTestament Testament
searchTree *trie.Trie
}
// SearchNode represents a search result for a given verse inquiry
type SearchNode struct {
Book *Book
Chapter *Chapter
Verse *Verse
}
// String creates a string representation for the SearchNode, ex. Genesis 1:1
func (s *SearchNode) String() string {
return fmt.Sprintf("%s %d:%d",
s.Book.Title,
s.Chapter.ChapterNumber,
s.Verse.VerseNumber)
}
const bibleTar = "bible.tar.gz"
const oldTestamentFilename = "old_testament.json"
const newTestamentFilename = "new_testament.json"
// NewBible creates a new bible instance
func NewBible() *Bible {
_, currFile, _, _ := runtime.Caller(0)
filename := fmt.Sprintf("%s/%s", path.Dir(currFile), bibleTar)
bible := &Bible{
searchTree: trie.New(),
}
f, _ := os.Open(filename)
defer f.Close()
gzf, _ := gzip.NewReader(f)
defer gzf.Close()
tr := tar.NewReader(gzf)
// Build testaments, books, chapters, verses
for {
h, err := tr.Next()
if err == io.EOF {
break
}
switch h.Name {
case oldTestamentFilename:
bible.OldTestament = Testament{Books: decode(tr)}
case newTestamentFilename:
bible.NewTestament = Testament{Books: decode(tr)}
}
}
// Build search tree
var books []Book
books = append(books, bible.OldTestament.Books...)
books = append(books, bible.NewTestament.Books...)
for i := range books {
bk := books[i]
for j := range bk.Chapters {
ch := bk.Chapters[j]
for k := range ch.Verses {
vr := strings.ToLower(ch.Verses[k].Text)
bible.searchTree.Add(vr, SearchNode{
Book: &bk,
Chapter: &ch,
Verse: &ch.Verses[k],
})
}
}
}
return bible
}
func decode(r io.Reader) []Book {
var books []Book
json.NewDecoder(r).Decode(&books)
return books
}
// Search finds top matching verses based on the given query.
// The number of search results are restricted by maxResults
func (b *Bible) Search(query string, maxResults int) []SearchNode {
t := b.searchTree
keys := t.FuzzySearch(strings.ToLower(query))
var verses []SearchNode
for k := range keys {
res, _ := t.Find(keys[k])
verses = append(verses, res.Meta().(SearchNode))
if len(verses) >= maxResults {
break
}
}
return verses
}