package database import ( "strings" "time" ) // Book metadata type Book struct { ID string Title string Authors []string `sql:"authors" pg:",array"` Contributor string Publisher string Description string Tags []string `sql:"tags" pg:",array"` Date string Lang string Isbn string FileSize int Cover bool Active bool UploadDate time.Time Tsv string } // TODO: missing history // AddBook to the database func (db *pgDB) AddBook(book Book) error { emptyTime := time.Time{} if book.UploadDate == emptyTime { book.UploadDate = time.Now() } return db.sql.Create(&book) } // GetBooks matching query func (db *pgDB) GetBooks(query string, length int, start int) (books []Book, num int, err error) { 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 // in the queue func (db *pgDB) GetNewBooks(query string, length int, start int) (books []Book, num int, err error) { return db.getBooks(false, query, length, start) } func (db *pgDB) getBooks(active bool, query string, length int, start int) (books []Book, num int, err error) { sqlQuery := db.sql.Model(&books) searchCondition := "active = " if active { searchCondition = "true" } else { searchCondition = "false" } params := []interface{}{} textQuery, columnQuerys := buildQuery(query) for _, c := range columnQuerys { searchCondition = searchCondition + " AND " + c.column + " ILIKE ?" params = append(params, c.value) } if textQuery != "" { searchCondition = searchCondition + " AND to_tsquery(?) @@ tsv" params = append(params, textQuery) } sqlQuery = sqlQuery.Where(searchCondition, params...) if textQuery != "" { sqlQuery = sqlQuery.Order("ts_rank(tsv, to_tsquery(?)) DESC, upload_date DESC", textQuery) } else { sqlQuery = sqlQuery.Order("upload_date DESC") } num, err = sqlQuery. Offset(start). Limit(length). SelectAndCountEstimate(100) return books, num, err } // GetBookID returns a the book with the specified id func (db *pgDB) GetBookID(id string) (Book, error) { var book Book err := db.sql.Model(&book). Where("id = ?", id). Select() return book, err } // DeleteBook removes the book with id from the database func (db *pgDB) DeleteBook(id string) error { _, err := db.sql.Model(&Book{}). Where("id = ?", id). Delete() return err } // UpdateBook metadata func (db *pgDB) UpdateBook(id string, data map[string]interface{}) error { setCondition := "" params := []interface{}{} for col, val := range data { colValid := false for _, name := range []string{"title", "authors", "contributor", "publisher", "description", "tags", "date", "lang", "isbn"} { if col == name { colValid = true break } } if !colValid { continue } if len(setCondition) != 0 { setCondition += ", " } setCondition += col + " = ?" params = append(params, val) } _, err := db.sql.Model(&Book{}). Set(setCondition, params...). Where("id = ?", id). Update() return err } // ActiveBook activates the book func (db *pgDB) ActiveBook(id string) error { uploadDate := time.Now() _, err := db.sql.Model(&Book{}). Set("active = true, upload_date = ? ", uploadDate). Where("id = ?", id). Update() return err } // IsBookActive checks if the book is active func (db *pgDB) IsBookActive(id string) bool { var active []bool err := db.sql.Model(&Book{}). Column("active"). Where("id = ?", id). Select(&active) if err != nil || len(active) != 1 { return false } return active[0] } type columnq struct { column string value string } func buildQuery(query string) (string, []columnq) { textQuery := "" columnQuerys := []columnq{} words := strings.Split(query, " ") for _, w := range words { if w == "" { continue } tag := strings.SplitN(w, ":", 2) if len(tag) > 1 && tag[1] != "" { value := strings.Replace(tag[1], "%", "\\%", 0) value = strings.Replace(value, "_", "\\_", 0) expr := "%" + value + "%" switch tag[0] { case "lang": columnQuerys = append(columnQuerys, columnq{"lang", value}) case "author": columnQuerys = append(columnQuerys, columnq{"array_to_string(authors, ' ')", expr}) case "title": columnQuerys = append(columnQuerys, columnq{"title", expr}) case "contributor": columnQuerys = append(columnQuerys, columnq{"contributor", expr}) case "publisher": columnQuerys = append(columnQuerys, columnq{"publisher", expr}) case "subject": expr = strings.ToLower(expr) columnQuerys = append(columnQuerys, columnq{"array_to_string(tags, ' ')", expr}) case "tag": expr = strings.ToLower(expr) columnQuerys = append(columnQuerys, columnq{"array_to_string(tag, ' ')", expr}) case "isbn": columnQuerys = append(columnQuerys, columnq{"isbn", expr}) case "description": columnQuerys = append(columnQuerys, columnq{"description", expr}) } } else { if len(textQuery) != 0 { lastChar := textQuery[len(textQuery)-1:] if w != "&" && w != "|" && lastChar != "&" && lastChar != "|" { textQuery += " | " } else { textQuery += " " } } textQuery += w } } return textQuery, columnQuerys }