From e56b43692822e07c953909b864ec6a3269e696d8 Mon Sep 17 00:00:00 2001 From: Meutel Date: Sun, 30 Jul 2017 20:14:20 +0200 Subject: [PATCH] Database, queries books --- .gitignore | 1 + bouquins/bouquins.go | 20 +++-- bouquins/db.go | 181 +++++++++++++++++++++++++++++++++++++++++++ main.go | 10 +++ templates/index.html | 28 +++---- 5 files changed, 222 insertions(+), 18 deletions(-) create mode 100644 bouquins/db.go diff --git a/.gitignore b/.gitignore index 22e9a30..4a3d7e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.swp *~ go-bouquins +calibre.db diff --git a/bouquins/bouquins.go b/bouquins/bouquins.go index 3759bcc..05237a5 100644 --- a/bouquins/bouquins.go +++ b/bouquins/bouquins.go @@ -1,20 +1,22 @@ package bouquins import ( + "database/sql" "html/template" "log" "net/http" ) const ( - TPL_BOOKS = "books.html" - TPL_AUTHORS = "authors.html" + TPL_BOOKS = "book.html" + TPL_AUTHORS = "author.html" TPL_SERIES = "series.html" TPL_INDEX = "index.html" ) type Bouquins struct { *template.Template + *sql.DB } /* @@ -129,8 +131,6 @@ func NewIndexModel(title string, count int64) *IndexModel { } } -// ROUTES // - func (app *Bouquins) render(res http.ResponseWriter, tpl string, model interface{}) { err := app.Template.ExecuteTemplate(res, tpl, model) if err != nil { @@ -138,8 +138,18 @@ func (app *Bouquins) render(res http.ResponseWriter, tpl string, model interface } } +// ROUTES // + func (app *Bouquins) IndexPage(res http.ResponseWriter, req *http.Request) { - model := NewIndexModel("", 123) + count, err := app.BookCount() + if err != nil { + log.Print(err) + } + model := NewIndexModel("", count) + model.Books, err = app.BooksAdv(0, 0) + if err != nil { + log.Print(err) + } app.render(res, TPL_INDEX, model) } func (app *Bouquins) BooksPage(res http.ResponseWriter, req *http.Request) { diff --git a/bouquins/db.go b/bouquins/db.go new file mode 100644 index 0000000..0328d13 --- /dev/null +++ b/bouquins/db.go @@ -0,0 +1,181 @@ +package bouquins + +import ( + "database/sql" +) + +const ( + STMT_PAGE = " LIMIT ? OFFSET ?" + STMT_BOOKS0 = `SELECT books.id AS id,title,series_index,name as series_name,series.id AS series_id + FROM books LEFT OUTER JOIN books_series_link ON books.id = books_series_link.book + LEFT OUTER JOIN series ON series.id = books_series_link.series ` + STMT_BOOKS_TAGS0 = `SELECT name, books_tags_link.book as book FROM tags, books_tags_link + WHERE tags.id = books_tags_link.tag AND books_tags_link.book IN ( SELECT id FROM books ` + STMT_BOOKS_AUTHORS0 = `SELECT authors.id, authors.name, books_authors_link.book as book + FROM authors, books_authors_link WHERE books_authors_link.author = authors.id + AND books_authors_link.book IN ( SELECT id FROM books ` + STMT_WHERE = " WHERE " + STMT_SEARCH_TERM = " books.sort like ? " + STMT_BOOL_AND = " AND " + STMT_BOOL_OR = " OR " + STMT_SEARCH_ORDER = " ORDER BY books.sort" + + STMT_BOOKS_COUNT = "SELECT count(id) FROM books" + STMT_BOOK = `SELECT books.id AS id,title, series_index, series.name AS series_name, series.id AS series_id, + strftime('%s', timestamp), strftime('%Y', pubdate), isbn,lccn,path,uuid,has_cover, + languages.lang_code, publishers.name AS pubname FROM books + LEFT OUTER JOIN books_languages_link ON books_languages_link.book = books.id + LEFT OUTER JOIN languages ON languages.id = books_languages_link.lang_code + LEFT OUTER JOIN data ON data.book = books.id + LEFT OUTER JOIN books_series_link ON books.id = books_series_link.book + LEFT OUTER JOIN series ON series.id = books_series_link.series + LEFT OUTER JOIN books_publishers_link ON books.id = books_publishers_link.book + LEFT OUTER JOIN publishers ON publishers.id = books_publishers_link.publisher + WHERE books.id = ?` + STMT_BOOK_TAGS = "SELECT name FROM tags, books_tags_link WHERE tags.id = books_tags_link.tag AND books_tags_link.book = ?" + STMT_BOOK_AUTHORS = `SELECT authors.id, authors.name, books_authors_link.book as book + FROM authors, books_authors_link WHERE books_authors_link.author = authors.id + AND books_authors_link.book = ?` + STMT_BOOK_DATA = "SELECT data.name, data.format, data.uncompressed_size FROM data WHERE data.book = ?" + STMT_BOOKS_ID_ASC = STMT_BOOKS0 + " ORDER BY id" + STMT_PAGE + STMT_BOOKS_ID_DESC = STMT_BOOKS0 + "ORDER BY id DESC" + STMT_PAGE + STMT_BOOKS_TITLE_ASC = STMT_BOOKS0 + "ORDER BY books.sort" + STMT_PAGE + STMT_BOOKS_TITLE_DESC = STMT_BOOKS0 + "ORDER BY books.sort DESC" + STMT_PAGE + STMT_BOOKS_TAGS_ID_ASC = STMT_BOOKS_TAGS0 + "ORDER BY id" + STMT_PAGE + ")" + STMT_BOOKS_TAGS_ID_DESC = STMT_BOOKS_TAGS0 + "ORDER BY id DESC" + STMT_PAGE + ")" + STMT_BOOKS_TAGS_TITLE_ASC = STMT_BOOKS_TAGS0 + "ORDER BY books.sort" + STMT_PAGE + ")" + STMT_BOOKS_TAGS_TITLE_DESC = STMT_BOOKS_TAGS0 + "ORDER BY books.sort DESC" + STMT_PAGE + ")" + STMT_BOOKS_AUTHORS_ID_ASC = STMT_BOOKS_AUTHORS0 + "ORDER BY id" + STMT_PAGE + ")" + STMT_BOOKS_AUTHORS_ID_DESC = STMT_BOOKS_AUTHORS0 + "ORDER BY id DESC" + STMT_PAGE + ")" + STMT_BOOKS_AUTHORS_TITLE_ASC = STMT_BOOKS_AUTHORS0 + "ORDER BY books.sort" + STMT_PAGE + ")" + STMT_BOOKS_AUTHORS_TITLE_DESC = STMT_BOOKS_AUTHORS0 + "ORDER BY books.sort DESC" + STMT_PAGE + ")" + + DEF_LIM = 10 +) + +// MERGE SUB QUERIES // +func assignAuthorsTagsBooks(books []*BookAdv, authors map[int64][]*Author, tags map[int64][]string) { + for _, b := range books { + b.Authors = authors[b.Id] + b.Tags = tags[b.Id] + } +} + +// SUB QUERIES // + +func (app *Bouquins) queryBooks(limit, offset int) ([]*BookAdv, error) { + books := make([]*BookAdv, 0, limit) + stmt, err := app.DB.Prepare(STMT_BOOKS_ID_ASC) + if err != nil { + return nil, err + } + rows, err := stmt.Query(limit, offset) + if err != nil { + return nil, err + } + for rows.Next() { + book := new(BookAdv) + var series_name sql.NullString + var series_id sql.NullInt64 + if err := rows.Scan(&book.Id, &book.Title, &book.SeriesIndex, &series_name, &series_id); err != nil { + return nil, err + } + if series_name.Valid && series_id.Valid { + book.Series = &Series{ + series_id.Int64, + series_name.String, + } + } + books = append(books, book) + } + if err := rows.Err(); err != nil { + return nil, err + } + return books, nil +} + +func (app *Bouquins) queryBooksAuthors(limit, offset int) (map[int64][]*Author, error) { + authors := make(map[int64][]*Author) + stmt, err := app.DB.Prepare(STMT_BOOKS_AUTHORS_ID_ASC) + if err != nil { + return nil, err + } + rows, err := stmt.Query(limit, offset) + if err != nil { + return nil, err + } + for rows.Next() { + author := new(Author) + var book int64 + if err := rows.Scan(&author.Id, &author.Name, &book); err != nil { + return nil, err + } + if authors[book] == nil { + authors[book] = append(make([]*Author, 0), author) + } else { + authors[book] = append(authors[book], author) + } + } + if err := rows.Err(); err != nil { + return nil, err + } + return authors, nil +} + +func (app *Bouquins) queryBooksTags(limit, offset int) (map[int64][]string, error) { + tags := make(map[int64][]string) + stmt, err := app.DB.Prepare(STMT_BOOKS_TAGS_ID_ASC) + if err != nil { + return nil, err + } + rows, err := stmt.Query(limit, offset) + if err != nil { + return nil, err + } + for rows.Next() { + var tag string + var book int64 + if err := rows.Scan(&tag, &book); err != nil { + return nil, err + } + bookTags := tags[book] + if bookTags == nil { + bookTags = make([]string, 1) + tags[book] = bookTags + } + tags[book] = append(bookTags, tag) + } + if err := rows.Err(); err != nil { + return nil, err + } + return tags, nil +} + +// DB LOADS // + +func (app *Bouquins) BookCount() (int64, error) { + var count int64 + row := app.DB.QueryRow(STMT_BOOKS_COUNT) + err := row.Scan(&count) + return count, err +} + +func (app *Bouquins) BooksAdv(limit int, offset int) ([]*BookAdv, error) { + if limit == 0 { + limit = DEF_LIM + } + books, err := app.queryBooks(limit, offset) + if err != nil { + return nil, err + } + authors, err := app.queryBooksAuthors(limit, offset) + if err != nil { + return nil, err + } + tags, err := app.queryBooksTags(limit, offset) + if err != nil { + return nil, err + } + assignAuthorsTagsBooks(books, authors, tags) + return books, nil +} diff --git a/main.go b/main.go index 4af3c7f..52d9e20 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,8 @@ package main import ( + "database/sql" + _ "github.com/mattn/go-sqlite3" "html/template" "log" "net/http" @@ -18,13 +20,20 @@ const ( FONTS = "/fonts/" ) +var db *sql.DB + func init() { tpl, err := template.ParseGlob("templates/*.html") if err != nil { log.Fatalln(err) } + db, err = sql.Open("sqlite3", "calibre.db") + if err != nil { + log.Fatalln(err) + } app := &bouquins.Bouquins{ tpl, + db, } assets() router(app) @@ -44,5 +53,6 @@ func router(app *bouquins.Bouquins) { } func main() { + defer db.Close() http.ListenAndServe(":9000", nil) } diff --git a/templates/index.html b/templates/index.html index cf54576..2c807a2 100644 --- a/templates/index.html +++ b/templates/index.html @@ -36,7 +36,7 @@ {{ range .Authors }} - {{ .Name }}  + {{ .Name }}  {{ end }} {{ end }} @@ -55,13 +55,13 @@ - {{ .Author.Name }} + {{ .Author.Name }} {{ .Author.Count }} {{ end }} - {{ if .Books }} + {{ if ne (len .Books) 0 }} - + {{ range .Books }} + + {{ .Title }} + {{ end }}
@@ -71,23 +71,25 @@ Auteur(s) Serie
- {{ .Book.Title }} - + {{ .Name }} + {{ end }} - + {{ if .Series }} + + {{ .Series.Name }} + {{ .Book.SeriesIndex }} + {{ end }}
{{ end }} {{ if (ne (len .Books) 0) or (ne (len .Authors) 0) or (ne (len .Series) 0) }}