Using master pg library
This commit is contained in:
parent
d9a95a997c
commit
e0854aa001
8 changed files with 43 additions and 76 deletions
|
@ -1,4 +1,4 @@
|
||||||
CREATE EXTENSION pg_trgm;
|
--CREATE EXTENSION pg_trgm;
|
||||||
|
|
||||||
CREATE TABLE books (
|
CREATE TABLE books (
|
||||||
id varchar(16) primary key,
|
id varchar(16) primary key,
|
||||||
|
@ -21,6 +21,7 @@ CREATE TABLE books (
|
||||||
-- Books column indexes
|
-- Books column indexes
|
||||||
CREATE INDEX CONCURRENTLY books_lang_idx ON books (lang);
|
CREATE INDEX CONCURRENTLY books_lang_idx ON books (lang);
|
||||||
CREATE INDEX CONCURRENTLY books_isbn_idx ON books (isbn);
|
CREATE INDEX CONCURRENTLY books_isbn_idx ON books (isbn);
|
||||||
|
CREATE INDEX CONCURRENTLY books_active_idx ON books (active);
|
||||||
|
|
||||||
-- Books trigram indexes
|
-- Books trigram indexes
|
||||||
CREATE INDEX CONCURRENTLY books_title_idx ON books USING GIN (title gin_trgm_ops);
|
CREATE INDEX CONCURRENTLY books_title_idx ON books USING GIN (title gin_trgm_ops);
|
||||||
|
|
|
@ -38,13 +38,6 @@ func coverHandler(h handler) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !book.Active {
|
|
||||||
if !h.sess.IsModerator() {
|
|
||||||
notFound(h)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file := coverFile
|
file := coverFile
|
||||||
if vars["size"] == "small" {
|
if vars["size"] == "small" {
|
||||||
file = coverSmallFile
|
file = coverSmallFile
|
||||||
|
|
|
@ -18,8 +18,8 @@ type Book struct {
|
||||||
Lang string
|
Lang string
|
||||||
Isbn string
|
Isbn string
|
||||||
FileSize int
|
FileSize int
|
||||||
Cover bool
|
Cover bool `sql:",notnull"`
|
||||||
Active bool
|
Active bool `sql:",notnull"`
|
||||||
UploadDate time.Time
|
UploadDate time.Time
|
||||||
Tsv string
|
Tsv string
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ func (db *pgDB) AddBook(book Book) error {
|
||||||
book.UploadDate = time.Now()
|
book.UploadDate = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.sql.Create(&book)
|
return db.sql.Insert(&book)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBooks matching query
|
// GetBooks matching query
|
||||||
|
@ -39,8 +39,6 @@ func (db *pgDB) GetBooks(query string, length int, start int) (books []Book, num
|
||||||
return db.getBooks(true, query, length, start)
|
return db.getBooks(true, query, length, start)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: func (db *pgDB) GetBooksIter() Iter {
|
|
||||||
|
|
||||||
// GetNewBooks returns a list of books in the incoming queue and the number of books
|
// GetNewBooks returns a list of books in the incoming queue and the number of books
|
||||||
// in the queue
|
// in the queue
|
||||||
func (db *pgDB) GetNewBooks(query string, length int, start int) (books []Book, num int, err error) {
|
func (db *pgDB) GetNewBooks(query string, length int, start int) (books []Book, num int, err error) {
|
||||||
|
@ -48,45 +46,42 @@ func (db *pgDB) GetNewBooks(query string, length int, start int) (books []Book,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *pgDB) getBooks(active bool, query string, length int, start int) (books []Book, num int, err error) {
|
func (db *pgDB) getBooks(active bool, query string, length int, start int) (books []Book, num int, err error) {
|
||||||
column := []string{}
|
rank := []string{}
|
||||||
columnParams := []interface{}{}
|
rankParams := []interface{}{}
|
||||||
searchCondition := "active = "
|
searchCondition := ""
|
||||||
if active {
|
if active {
|
||||||
searchCondition = "true"
|
searchCondition += "active is true"
|
||||||
} else {
|
} else {
|
||||||
searchCondition = "false"
|
searchCondition += "active is not true"
|
||||||
}
|
}
|
||||||
searchParams := []interface{}{}
|
searchParams := []interface{}{}
|
||||||
|
|
||||||
textQuery, columnQuerys, trigramQuerys := buildQuery(query)
|
textQuery, columnQuerys, trigramQuerys := buildQuery(query)
|
||||||
for _, c := range columnQuerys {
|
for _, c := range columnQuerys {
|
||||||
searchCondition = searchCondition + " AND " + c.column + " = ?"
|
searchCondition += " AND " + c.column + " = ?"
|
||||||
searchParams = append(searchParams, c.value)
|
searchParams = append(searchParams, c.value)
|
||||||
}
|
}
|
||||||
for _, c := range trigramQuerys {
|
for _, c := range trigramQuerys {
|
||||||
column = append(column, "word_similarity(?, "+c.column+")")
|
rank = append(rank, "word_similarity(?, "+c.column+")")
|
||||||
columnParams = append(columnParams, c.value)
|
rankParams = append(rankParams, c.value)
|
||||||
searchCondition = searchCondition + " AND " + c.column + " %> ?"
|
searchCondition += " AND " + c.column + " %> ?"
|
||||||
searchParams = append(searchParams, c.value)
|
searchParams = append(searchParams, c.value)
|
||||||
}
|
}
|
||||||
if textQuery != "" {
|
if textQuery != "" {
|
||||||
column = append(column, "ts_rank(tsv, to_tsquery(?))")
|
rank = append(rank, "ts_rank(tsv, to_tsquery(?))")
|
||||||
columnParams = append(columnParams, textQuery)
|
rankParams = append(rankParams, textQuery)
|
||||||
searchCondition = searchCondition + " AND to_tsquery(?) @@ tsv"
|
searchCondition += " AND to_tsquery(?) @@ tsv"
|
||||||
searchParams = append(searchParams, textQuery)
|
searchParams = append(searchParams, textQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
columnStr := "*"
|
|
||||||
order := "upload_date DESC"
|
order := "upload_date DESC"
|
||||||
if len(column) > 0 {
|
if len(rank) > 0 {
|
||||||
columnStr = "*, " + strings.Join(column, "+") + " AS rank"
|
order = strings.Join(rank, "+") + " DESC, upload_date DESC"
|
||||||
order = "rank DESC, upload_date DESC"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
num, err = db.sql.Model(&books).
|
num, err = db.sql.Model(&books).
|
||||||
ColumnExpr(columnStr, columnParams...).
|
|
||||||
Where(searchCondition, searchParams...).
|
Where(searchCondition, searchParams...).
|
||||||
Order(order).
|
OrderExpr(order, rankParams...).
|
||||||
Offset(start).
|
Offset(start).
|
||||||
Limit(length).
|
Limit(length).
|
||||||
SelectAndCountEstimate(100)
|
SelectAndCountEstimate(100)
|
||||||
|
@ -169,7 +164,6 @@ type columnq struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildQuery(query string) (textQuery string, columnQuerys []columnq, trigramQuerys []columnq) {
|
func buildQuery(query string) (textQuery string, columnQuerys []columnq, trigramQuerys []columnq) {
|
||||||
// FIXME: does *Querys need initialization??
|
|
||||||
words := strings.Split(query, " ")
|
words := strings.Split(query, " ")
|
||||||
for _, w := range words {
|
for _, w := range words {
|
||||||
if w == "" {
|
if w == "" {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/pg.v4"
|
"github.com/go-pg/pg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DB interface {
|
type DB interface {
|
||||||
|
@ -45,7 +45,12 @@ type Options struct {
|
||||||
|
|
||||||
// Init the database connection
|
// Init the database connection
|
||||||
func Init(options Options) (DB, error) {
|
func Init(options Options) (DB, error) {
|
||||||
|
network := "tcp"
|
||||||
|
if options.Addr[0] == '/' {
|
||||||
|
network = "unix"
|
||||||
|
}
|
||||||
sql := pg.Connect(&pg.Options{
|
sql := pg.Connect(&pg.Options{
|
||||||
|
Network: network,
|
||||||
Addr: options.Addr,
|
Addr: options.Addr,
|
||||||
User: options.User,
|
User: options.User,
|
||||||
Password: options.Password,
|
Password: options.Password,
|
||||||
|
|
|
@ -18,7 +18,7 @@ func (db *pgDB) AddNews(text string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *pgDB) addRawNews(text string, date time.Time) error {
|
func (db *pgDB) addRawNews(text string, date time.Time) error {
|
||||||
return db.sql.Create(&New{
|
return db.sql.Insert(&New{
|
||||||
Text: text,
|
Text: text,
|
||||||
Date: date,
|
Date: date,
|
||||||
})
|
})
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (db *pgDB) addRawUser(name string, hpass []byte, salt []byte, role string)
|
||||||
Salt: salt,
|
Salt: salt,
|
||||||
Role: role,
|
Role: role,
|
||||||
}
|
}
|
||||||
return db.sql.Create(&u)
|
return db.sql.Insert(&u)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *pgDB) GetRole(name string) (string, error) {
|
func (db *pgDB) GetRole(name string) (string, error) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/meskio/epubgo"
|
"github.com/meskio/epubgo"
|
||||||
"gitlab.com/trantor/trantor/lib/database"
|
"gitlab.com/trantor/trantor/lib/database"
|
||||||
|
"gitlab.com/trantor/trantor/lib/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
type chapter struct {
|
type chapter struct {
|
||||||
|
@ -105,7 +106,7 @@ func getChapters(e *epubgo.Epub, file string, id string, base string) []chapter
|
||||||
|
|
||||||
func listChapters(nav *epubgo.NavigationIterator, depth int) []chapter {
|
func listChapters(nav *epubgo.NavigationIterator, depth int) []chapter {
|
||||||
var chapters []chapter
|
var chapters []chapter
|
||||||
var err error = nil
|
var err error
|
||||||
for err == nil {
|
for err == nil {
|
||||||
var c chapter
|
var c chapter
|
||||||
c.Label = nav.Title()
|
c.Label = nav.Title()
|
||||||
|
@ -128,7 +129,7 @@ func listChapters(nav *epubgo.NavigationIterator, depth int) []chapter {
|
||||||
|
|
||||||
func readStartHandler(h handler) {
|
func readStartHandler(h handler) {
|
||||||
id := mux.Vars(h.r)["id"]
|
id := mux.Vars(h.r)["id"]
|
||||||
e, _ := openReadEpub(h)
|
e := openReadEpub(id, h.store)
|
||||||
if e == nil {
|
if e == nil {
|
||||||
log.Warn("Open epub returns an empty file")
|
log.Warn("Open epub returns an empty file")
|
||||||
notFound(h)
|
notFound(h)
|
||||||
|
@ -148,7 +149,7 @@ func readStartHandler(h handler) {
|
||||||
func readHandler(h handler) {
|
func readHandler(h handler) {
|
||||||
id := mux.Vars(h.r)["id"]
|
id := mux.Vars(h.r)["id"]
|
||||||
file := mux.Vars(h.r)["file"]
|
file := mux.Vars(h.r)["file"]
|
||||||
e, book := openReadEpub(h)
|
e := openReadEpub(id, h.store)
|
||||||
if e == nil {
|
if e == nil {
|
||||||
notFound(h)
|
notFound(h)
|
||||||
return
|
return
|
||||||
|
@ -158,6 +159,11 @@ func readHandler(h handler) {
|
||||||
var data readData
|
var data readData
|
||||||
data.S = GetStatus(h)
|
data.S = GetStatus(h)
|
||||||
|
|
||||||
|
book, err := h.db.GetBookID(id)
|
||||||
|
if err != nil {
|
||||||
|
notFound(h)
|
||||||
|
return
|
||||||
|
}
|
||||||
author := ""
|
author := ""
|
||||||
if len(book.Authors) > 0 {
|
if len(book.Authors) > 0 {
|
||||||
author = " by " + book.Authors[0]
|
author = " by " + book.Authors[0]
|
||||||
|
@ -177,38 +183,23 @@ func readHandler(h handler) {
|
||||||
h.template.load(h, "read", data)
|
h.template.load(h, "read", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func openReadEpub(h handler) (*epubgo.Epub, database.Book) {
|
func openReadEpub(id string, store storage.Store) *epubgo.Epub {
|
||||||
var book database.Book
|
f, err := store.Get(id, epubFile)
|
||||||
id := mux.Vars(h.r)["id"]
|
|
||||||
if id == "" {
|
|
||||||
return nil, book
|
|
||||||
}
|
|
||||||
book, err := h.db.GetBookID(id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, book
|
return nil
|
||||||
}
|
|
||||||
if !book.Active {
|
|
||||||
if !h.sess.IsModerator() {
|
|
||||||
return nil, book
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := h.store.Get(book.ID, epubFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, book
|
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
info, err := f.Stat()
|
info, err := f.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, book
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := epubgo.Load(f, info.Size())
|
e, err := epubgo.Load(f, info.Size())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, book
|
return nil
|
||||||
}
|
}
|
||||||
return e, book
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func contentHandler(h handler) {
|
func contentHandler(h handler) {
|
||||||
|
@ -228,16 +219,6 @@ func contentHandler(h handler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func openEpubFile(h handler, id string, file string) error {
|
func openEpubFile(h handler, id string, file string) error {
|
||||||
book, err := h.db.GetBookID(id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !book.Active {
|
|
||||||
if !h.sess.IsModerator() {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := h.store.Get(id, epubFile)
|
f, err := h.store.Get(id, epubFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -84,13 +84,6 @@ func downloadHandler(h handler) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !book.Active {
|
|
||||||
if !h.sess.IsModerator() {
|
|
||||||
notFound(h)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := h.store.Get(book.ID, epubFile)
|
f, err := h.store.Get(book.ID, epubFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
notFound(h)
|
notFound(h)
|
||||||
|
|
Reference in a new issue