package database import ( log "github.com/cihub/seelog" "strings" "time" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" ) const ( books_coll = "books" ) type Book struct { Id string Title string Author []string Contributor string Publisher string Description string Subject []string Date string Lang []string Isbn string Type string Format string Source string Relation string Coverage string Rights string Meta string FileSize int Cover bool Active bool BadQuality int `bad_quality` BadQualityReporters []string `bad_quality_reporters` } type history struct { Date time.Time Changes bson.M } func indexBooks(coll *mgo.Collection) { indexes := []mgo.Index{ { Key: []string{"id"}, Unique: true, Background: true, }, { Key: []string{"active", "-_id"}, Background: true, }, { Key: []string{"active", "-bad_quality", "-_id"}, Background: true, }, { Key: []string{"$text:title", "$text:author", "$text:contributor", "$text:publisher", "$text:subject", "$text:description"}, Weights: map[string]int{"title": 20, "author": 20, "contributor": 15, "publisher": 15, "subject": 10, "description": 5}, LanguageOverride: "_lang", Background: true, }, } for _, k := range []string{"lang", "title", "author", "subject"} { idx := mgo.Index{ Key: []string{"active", k, "-_id"}, Background: true, } indexes = append(indexes, idx) } for _, idx := range indexes { err := coll.EnsureIndex(idx) if err != nil { log.Error("Error indexing books: ", err) } } } func addBook(coll *mgo.Collection, book map[string]interface{}) error { book["_lang"] = metadataLang(book) return coll.Insert(book) } func getBooks(coll *mgo.Collection, query string, length int, start int) (books []Book, num int, err error) { return _getBooks(coll, buildQuery(query), length, start) } func getNewBooks(coll *mgo.Collection, length int, start int) (books []Book, num int, err error) { return _getBooks(coll, bson.M{"$nor": []bson.M{{"active": true}}}, length, start) } func getBooksIter(coll *mgo.Collection) Iter { return coll.Find(bson.M{}).Iter() } func _getBooks(coll *mgo.Collection, query bson.M, length int, start int) (books []Book, num int, err error) { q := getBookQuery(coll, query) num, err = q.Count() if err != nil { return } if start != 0 { q = q.Skip(start) } if length != 0 { q = q.Limit(length) } err = q.All(&books) return } func getBookQuery(coll *mgo.Collection, query bson.M) *mgo.Query { sort := []string{"$textScore:score"} if _, present := query["bad_quality"]; present { sort = append(sort, "-bad_quality") } sort = append(sort, "-_id") return coll.Find(query).Select(bson.M{"score": bson.M{"$meta": "textScore"}}).Sort(sort...) } func getBookId(coll *mgo.Collection, id string) (Book, error) { var book Book err := coll.Find(bson.M{"id": id}).One(&book) return book, err } func deleteBook(coll *mgo.Collection, id string) error { return coll.Remove(bson.M{"id": id}) } func updateBook(coll *mgo.Collection, id string, data map[string]interface{}) error { var book map[string]interface{} record := history{time.Now(), bson.M{}} err := coll.Find(bson.M{"id": id}).One(&book) if err != nil { return err } for k, _ := range data { record.Changes[k] = book[k] if k == "lang" { data["_lang"] = metadataLang(data) } } return coll.Update(bson.M{"id": id}, bson.M{"$set": data, "$push": bson.M{"history": record}}) } func flagBadQuality(coll *mgo.Collection, id string, user string) error { b, err := getBookId(coll, id) if err != nil { return err } for _, reporter := range b.BadQualityReporters { if reporter == user { return nil } } return coll.Update( bson.M{"id": id}, bson.M{ "$inc": bson.M{"bad_quality": 1}, "$addToSet": bson.M{"bad_quality_reporters": user}, }, ) } func activeBook(coll *mgo.Collection, id string) error { data := map[string]interface{}{"active": true} return coll.Update(bson.M{"id": id}, bson.M{"$set": data}) } func isBookActive(coll *mgo.Collection, id string) bool { var book Book err := coll.Find(bson.M{"id": id}).One(&book) if err != nil { return false } return book.Active } func buildQuery(q string) bson.M { text := "" query := bson.M{"active": true} words := strings.Split(q, " ") for _, w := range words { tag := strings.SplitN(w, ":", 2) if len(tag) > 1 { if tag[0] == "flag" { query[tag[1]] = bson.M{"$gt": 0} } else { query[tag[0]] = bson.RegEx{tag[1], "i"} //FIXME: this should be a list } } else { if len(text) != 0 { text += " " } text += w } } if len(text) > 0 { query["$text"] = bson.M{"$search": text} } return query } func metadataLang(book map[string]interface{}) string { text_search_langs := map[string]bool{ "da": true, "nl": true, "en": true, "fi": true, "fr": true, "de": true, "hu": true, "it": true, "nb": true, "pt": true, "ro": true, "ru": true, "es": true, "sv": true, "tr": true} lang, ok := book["lang"].([]string) if !ok || len(lang) == 0 || len(lang[0]) < 2 { return "none" } lcode := strings.ToLower(lang[0][0:2]) if text_search_langs[lcode] { return lcode } return "none" }