package main import ( "crypto/md5" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "time" ) const ( META_TYPE_TAGS = "tags updated" ) var db *DB type Book struct { Id string `bson:"_id"` 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 File bson.ObjectId Cover bson.ObjectId CoverSmall bson.ObjectId Active bool Keywords []string } type DB struct { session *mgo.Session meta *mgo.Collection books *mgo.Collection tags *mgo.Collection user *mgo.Collection } func initDB() *DB { var err error d := new(DB) d.session, err = mgo.Dial(DB_IP) if err != nil { panic(err) } database := d.session.DB(DB_NAME) d.meta = database.C(META_COLL) d.books = database.C(BOOKS_COLL) d.tags = database.C(TAGS_COLL) d.user = database.C(USERS_COLL) return d } func (d *DB) Close() { d.session.Close() } func md5Pass(pass string) []byte { h := md5.New() hash := h.Sum(([]byte)(PASS_SALT + pass)) return hash } func (d *DB) SetPassword(user string, pass string) error { hash := md5Pass(pass) return d.user.Update(bson.M{"user": user}, bson.M{"$set": bson.M{"pass": hash}}) } func (d *DB) UserValid(user string, pass string) bool { hash := md5Pass(pass) n, err := d.user.Find(bson.M{"user": user, "pass": hash}).Count() if err != nil { return false } return n != 0 } func (d *DB) InsertBook(book interface{}) error { return d.books.Insert(book) } func (d *DB) RemoveBook(id bson.ObjectId) error { return d.books.Remove(bson.M{"_id": id}) } func (d *DB) UpdateBook(id bson.ObjectId, data interface{}) error { return d.books.Update(bson.M{"_id": id}, bson.M{"$set": data}) } func (d *DB) IncVisit(id bson.ObjectId) error { return d.books.Update(bson.M{"_id": id}, bson.M{"$inc": bson.M{"VisitsCount": 1}}) } func (d *DB) IncDownload(id bson.ObjectId) error { return d.books.Update(bson.M{"_id": id}, bson.M{"$inc": bson.M{"DownloadCount": 1}}) } /* optional parameters: length and start index * * Returns: list of books, number found and err */ func (d *DB) GetBooks(query bson.M, r ...int) (books []Book, num int, err error) { var start, length int if len(r) > 0 { length = r[0] if len(r) > 1 { start = r[1] } } q := d.books.Find(query).Sort("-_id") 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) for i, b := range books { books[i].Id = bson.ObjectId(b.Id).Hex() } return } /* Get the most visited books */ func (d *DB) GetVisitedBooks(num int) (books []Book, err error) { var q *mgo.Query q = d.books.Find(bson.M{"active": true}).Sort("-VisitsCount").Limit(num) err = q.All(&books) for i, b := range books { books[i].Id = bson.ObjectId(b.Id).Hex() } return } /* Get the most downloaded books */ func (d *DB) GetDownloadedBooks(num int) (books []Book, err error) { var q *mgo.Query q = d.books.Find(bson.M{"active": true}).Sort("-DownloadCount").Limit(num) err = q.All(&books) for i, b := range books { books[i].Id = bson.ObjectId(b.Id).Hex() } return } /* optional parameters: length and start index * * Returns: list of books, number found and err */ func (d *DB) GetNewBooks(r ...int) (books []Book, num int, err error) { return d.GetBooks(bson.M{"$nor": []bson.M{{"active": true}}}, r...) } func (d *DB) BookActive(id bson.ObjectId) bool { var book Book err := d.books.Find(bson.M{"_id": id}).One(&book) if err != nil { return false } return book.Active } func (d *DB) GetFS(prefix string) *mgo.GridFS { return d.session.DB(DB_NAME).GridFS(prefix) } func (d *DB) areTagsOutdated() bool { var result struct { Id bson.ObjectId `bson:"_id"` } err := d.meta.Find(bson.M{"type": META_TYPE_TAGS}).One(&result) if err != nil { return true } lastUpdate := result.Id.Time() return time.Since(lastUpdate).Minutes() > MINUTES_UPDATE_TAGS } func (d *DB) updateTags() error { _, err := d.meta.RemoveAll(bson.M{"type": META_TYPE_TAGS}) if err != nil { return err } var mr mgo.MapReduce mr.Map = "function() { " + "if (this.active) { this.subject.forEach(function(s) { emit(s, 1); }); }" + "}" mr.Reduce = "function(tag, vals) { " + "var count = 0;" + "vals.forEach(function() { count += 1; });" + "return count;" + "}" mr.Out = bson.M{"replace": TAGS_COLL} _, err = d.books.Find(bson.M{"active": true}).MapReduce(&mr, nil) if err != nil { return err } return d.meta.Insert(bson.M{"type": META_TYPE_TAGS}) } func (d *DB) GetTags(numTags int) ([]string, error) { if d.areTagsOutdated() { err := d.updateTags() if err != nil { return nil, err } } var result []struct { Tag string "_id" } err := d.tags.Find(nil).Sort("-value").Limit(numTags).All(&result) if err != nil { return nil, err } tags := make([]string, len(result)) for i, r := range result { tags[i] = r.Tag } return tags, nil }