major changes
This commit is contained in:
parent
a3270f399d
commit
2b56832843
83 changed files with 7760 additions and 1244 deletions
328
index.go
Normal file
328
index.go
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
// Copyright (C) 2017 Marius Schellenberger
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
|
||||
"github.com/blevesearch/bleve"
|
||||
"github.com/blevesearch/bleve/document"
|
||||
)
|
||||
|
||||
const (
|
||||
maxResult = 1e6
|
||||
maxSearchResult = 101
|
||||
noResults = "<b>No search results</b>"
|
||||
indexName = "Index"
|
||||
welcome = `# Welcome to GoWiki
|
||||
This is the [Index](/wiki/Index) page.
|
||||
You can customize it how you like.`
|
||||
)
|
||||
|
||||
type Article struct {
|
||||
Title string `json:"title"`
|
||||
LinkTitle string `json:"-"`
|
||||
Search string `json:"search"`
|
||||
Index template.HTML `json:"index"`
|
||||
Text template.HTML `json:"text"`
|
||||
MD string `json:"md"`
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
Public bool `json:"public"`
|
||||
}
|
||||
|
||||
func notExists(title string) error {
|
||||
return indexErr("article '" + title + "' does not esist")
|
||||
}
|
||||
|
||||
func indexErr(i interface{}) error {
|
||||
return fmt.Errorf("index: %s", i)
|
||||
}
|
||||
|
||||
type Index struct {
|
||||
index bleve.Index
|
||||
cache *IndexCache
|
||||
}
|
||||
|
||||
func NewIndex(path string) (i *Index, err error) {
|
||||
var bi bleve.Index
|
||||
if _, err = os.Stat(path); os.IsNotExist(err) {
|
||||
mapping := bleve.NewIndexMapping()
|
||||
bi, err = bleve.New(path, mapping)
|
||||
if err != nil {
|
||||
err = indexErr(err)
|
||||
return
|
||||
}
|
||||
i = &Index{index: bi}
|
||||
_, err = i.Index(indexName, welcome, defUser, true)
|
||||
if err != nil {
|
||||
err = indexErr(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
bi, err = bleve.Open(path)
|
||||
if err != nil {
|
||||
err = indexErr(err)
|
||||
return
|
||||
}
|
||||
i = &Index{index: bi}
|
||||
}
|
||||
i.cache = NewIndexCache()
|
||||
return
|
||||
}
|
||||
|
||||
func NewDumpIndex(path string) (i *Index, err error) {
|
||||
bi, err := bleve.OpenUsing(path, map[string]interface{}{"read_only": true})
|
||||
if err != nil {
|
||||
err = indexErr(err)
|
||||
return
|
||||
}
|
||||
i = &Index{index: bi}
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Index) Close() error {
|
||||
return i.index.Close()
|
||||
}
|
||||
|
||||
func (i *Index) Restore(dumpArt *DumpArticle) error {
|
||||
art := render(dumpArt.MD).Article()
|
||||
art.Title = dumpArt.Title
|
||||
art.Created = dumpArt.Created
|
||||
art.Updated = dumpArt.Updated
|
||||
art.Public = dumpArt.Public
|
||||
return i.index.Index(dumpArt.LinkTitle, art)
|
||||
}
|
||||
|
||||
func (i *Index) Index(title, text, user string, public bool) (string, error) {
|
||||
if user == "" {
|
||||
return "", indexErr("user cannot be empty")
|
||||
}
|
||||
newTitle, title := makeLinkTitle(title)
|
||||
if i.exists(false, newTitle) {
|
||||
return "", indexErr("article '" + title + "' already exists")
|
||||
}
|
||||
art := render(text).Article()
|
||||
art.Title = title
|
||||
art.Created = user + " " + now()
|
||||
art.Public = public
|
||||
err := i.index.Index(newTitle, art)
|
||||
i.cache.Add(newTitle, art)
|
||||
return newTitle, err
|
||||
}
|
||||
|
||||
func (i *Index) exists(public bool, title string) bool {
|
||||
_, err := i.Get(public, title)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (i *Index) Delete(title string) error {
|
||||
if title == indexName {
|
||||
return indexErr("page '" + indexName + "' cannot be deleted")
|
||||
}
|
||||
if !i.exists(false, title) {
|
||||
return notExists(title)
|
||||
}
|
||||
err := i.index.Delete(title)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.cache.Delete(title)
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *Index) Update(title, text, user string, public bool) error {
|
||||
if user == "" {
|
||||
return indexErr("user cannot be empty")
|
||||
}
|
||||
if title == indexName && !public {
|
||||
return indexErr("page '" + indexName + "' must stay public")
|
||||
}
|
||||
if !i.exists(false, title) {
|
||||
return notExists(title)
|
||||
}
|
||||
art, err := i.Get(false, title)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rend := render(text).Article()
|
||||
art.Updated = user + " " + now()
|
||||
art.Search = rend.Search
|
||||
art.Index = rend.Index
|
||||
art.Text = rend.Text
|
||||
art.MD = rend.MD
|
||||
art.Public = public
|
||||
err = i.index.Index(title, art)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.cache.Add(title, &art)
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *Index) Get(public bool, title string) (art Article, err error) {
|
||||
a := i.cache.Get(title)
|
||||
switch {
|
||||
case a != nil:
|
||||
art = *a
|
||||
default:
|
||||
var doc *document.Document
|
||||
doc, err = i.index.Document(title)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if doc == nil || doc.Fields == nil {
|
||||
err = notExists(title)
|
||||
return
|
||||
}
|
||||
for _, field := range doc.Fields {
|
||||
switch f := field.(type) {
|
||||
case *document.TextField:
|
||||
switch field.Name() {
|
||||
case "title":
|
||||
art.Title = string(f.Value())
|
||||
case "index":
|
||||
art.Index = template.HTML(string(f.Value()))
|
||||
case "text":
|
||||
art.Text = template.HTML(string(f.Value()))
|
||||
case "md":
|
||||
art.MD = string(f.Value())
|
||||
case "created":
|
||||
art.Created = string(f.Value())
|
||||
case "updated":
|
||||
art.Updated = string(f.Value())
|
||||
}
|
||||
case *document.BooleanField:
|
||||
if field.Name() == "public" {
|
||||
p, e := f.Boolean()
|
||||
if e != nil {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
art.Public = p
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
art.LinkTitle = title
|
||||
if public && !art.Public {
|
||||
err = notExists(title)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Index) Search(public bool, search string) (html string, err error) {
|
||||
if search == "" {
|
||||
html = noResults
|
||||
return
|
||||
}
|
||||
var (
|
||||
res *bleve.SearchResult
|
||||
req *bleve.SearchRequest
|
||||
)
|
||||
if public {
|
||||
req = bleve.NewSearchRequest(bleve.NewConjunctionQuery([]bleve.Query{
|
||||
bleve.NewBoolFieldQuery(true).SetField("public"),
|
||||
bleve.NewQueryStringQuery("title:" + search + " search:" + search),
|
||||
}))
|
||||
} else {
|
||||
req = bleve.NewSearchRequest(bleve.NewQueryStringQuery("title:" + search + " search:" + search))
|
||||
}
|
||||
req.Highlight = bleve.NewHighlightWithStyle("html")
|
||||
req.Size = maxSearchResult
|
||||
res, err = i.index.Search(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if res.Total == 0 {
|
||||
html = noResults
|
||||
return
|
||||
} else if res.Total == maxSearchResult {
|
||||
err = indexErr("too many search results")
|
||||
return
|
||||
}
|
||||
for _, hit := range res.Hits {
|
||||
if hit == nil {
|
||||
continue
|
||||
}
|
||||
art, err := i.Get(false, hit.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
html += `<a href="/wiki/` + hit.ID + `"><b>` + art.Title + "</b></a>"
|
||||
for fragField, frags := range hit.Fragments {
|
||||
if fragField == "title" || fragField == "search" {
|
||||
html += `<pre style="white-space: pre-wrap">`
|
||||
for _, f := range frags {
|
||||
html += f
|
||||
}
|
||||
html += "</pre>"
|
||||
break
|
||||
}
|
||||
}
|
||||
html += "<br>"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Title struct {
|
||||
Title string
|
||||
LinkTitle string
|
||||
}
|
||||
|
||||
func (i *Index) GetAll(public bool) (t []Title, err error) {
|
||||
var (
|
||||
res *bleve.SearchResult
|
||||
req *bleve.SearchRequest
|
||||
)
|
||||
if public {
|
||||
req = bleve.NewSearchRequest(bleve.NewBoolFieldQuery(true).SetField("public"))
|
||||
} else {
|
||||
req = bleve.NewSearchRequest(bleve.NewMatchAllQuery())
|
||||
}
|
||||
req.Size = maxResult
|
||||
res, err = i.index.Search(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range res.Hits {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
if v.ID == indexName {
|
||||
continue
|
||||
}
|
||||
doc, err := i.index.Document(v.ID)
|
||||
if err != nil || doc == nil || doc.Fields == nil {
|
||||
continue
|
||||
}
|
||||
for _, field := range doc.Fields {
|
||||
if field.Name() == "title" {
|
||||
f, ok := field.(*document.TextField)
|
||||
if ok {
|
||||
t = append(t, Title{string(f.Value()), v.ID})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Index) Dump() (titles []string, err error) {
|
||||
req := bleve.NewSearchRequest(bleve.NewMatchAllQuery())
|
||||
req.Size = maxResult
|
||||
res, err := i.index.Search(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range res.Hits {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
titles = append(titles, v.ID)
|
||||
}
|
||||
return
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue