package bouquins import ( "database/sql" "log" ) 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 BOOKS QueryType = 0 BOOKS_TAGS QueryType = 1 BOOKS_AUTHORS QueryType = 2 ) var QUERIES = map[Query]string{ Query{BOOKS, true, true}: STMT_BOOKS_TITLE_DESC, Query{BOOKS, true, false}: STMT_BOOKS_TITLE_ASC, Query{BOOKS, false, true}: STMT_BOOKS_ID_DESC, Query{BOOKS, false, false}: STMT_BOOKS_ID_ASC, Query{BOOKS_TAGS, true, true}: STMT_BOOKS_TAGS_TITLE_DESC, Query{BOOKS_TAGS, true, false}: STMT_BOOKS_TAGS_TITLE_ASC, Query{BOOKS_TAGS, false, true}: STMT_BOOKS_TAGS_ID_DESC, Query{BOOKS_TAGS, false, false}: STMT_BOOKS_TAGS_ID_ASC, Query{BOOKS_AUTHORS, true, true}: STMT_BOOKS_AUTHORS_TITLE_DESC, Query{BOOKS_AUTHORS, true, false}: STMT_BOOKS_AUTHORS_TITLE_ASC, Query{BOOKS_AUTHORS, false, true}: STMT_BOOKS_AUTHORS_ID_DESC, Query{BOOKS_AUTHORS, false, false}: STMT_BOOKS_AUTHORS_ID_ASC, } type QueryType uint type Query struct { Type QueryType Title bool Desc bool } // PREPARED STATEMENTS // func (app *Bouquins) ps(qt QueryType, sort, order string) (*sql.Stmt, error) { //TODO cache query := QUERIES[Query{qt, sort == "title", order == "desc"}] log.Println(query) return app.DB.Prepare(query) } // 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, sort, order string) ([]*BookAdv, error) { books := make([]*BookAdv, 0, limit) stmt, err := app.ps(BOOKS, sort, order) 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, sort, order string) (map[int64][]*Author, error) { authors := make(map[int64][]*Author) stmt, err := app.ps(BOOKS_AUTHORS, sort, order) 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, sort, order string) (map[int64][]string, error) { tags := make(map[int64][]string) stmt, err := app.ps(BOOKS_TAGS, sort, order) 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, 0) 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) SeriesAdv(limit, offset int, sort, order string) ([]*SeriesAdv, error) { panic("not implemented") } func (app *Bouquins) AuthorsAdv(limit, offset int, sort, order string) ([]*AuthorAdv, error) { panic("not implemented") } func (app *Bouquins) BooksAdv(limit, offset int, sort, order string) ([]*BookAdv, error) { if limit == 0 { limit = DEF_LIM } books, err := app.queryBooks(limit, offset, sort, order) if err != nil { return nil, err } authors, err := app.queryBooksAuthors(limit, offset, sort, order) if err != nil { return nil, err } tags, err := app.queryBooksTags(limit, offset, sort, order) if err != nil { return nil, err } assignAuthorsTagsBooks(books, authors, tags) return books, nil }