package fact import ( "database/sql" "fmt" "github.com/jmoiron/sqlx" "github.com/rs/zerolog/log" "time" ) // Factoid stores info about our factoid for lookup and later interaction type Factoid struct { ID sql.NullInt64 Fact string Tidbit string Verb string Owner string Created time.Time Accessed time.Time Count int } type alias struct { Fact string Next string } func (a *alias) resolve(db *sqlx.DB) (*Factoid, error) { // perform db query to fill the To field q := `select fact, next from factoid_alias where fact=?` var next alias err := db.Get(&next, q, a.Next) if err != nil { // we hit the end of the chain, get a factoid named Next fact, err := GetSingleFact(db, a.Next) if err != nil { err := fmt.Errorf("Error resolvig alias %v: %v", a, err) return nil, err } return fact, nil } return next.resolve(db) } func findAlias(db *sqlx.DB, fact string) (bool, *Factoid) { q := `select * from factoid_alias where fact=?` var a alias err := db.Get(&a, q, fact) if err != nil { return false, nil } f, err := a.resolve(db) return err == nil, f } func (a *alias) save(db *sqlx.DB) error { q := `select * from factoid_alias where fact=?` var offender alias err := db.Get(&offender, q, a.Next) if err == nil { return fmt.Errorf("DANGER: an opposite alias already exists") } _, err = a.resolve(db) if err != nil { return fmt.Errorf("there is no fact at that destination") } q = `insert or replace into factoid_alias (fact, next) values (?, ?)` _, err = db.Exec(q, a.Fact, a.Next) if err != nil { return err } return nil } func aliasFromStrings(from, to string) *alias { return &alias{from, to} } func (f *Factoid) Save(db *sqlx.DB) error { var err error if f.ID.Valid { // update _, err = db.Exec(`update factoid set fact=?, tidbit=?, verb=?, owner=?, accessed=?, count=? where id=?`, f.Fact, f.Tidbit, f.Verb, f.Owner, f.Accessed.Unix(), f.Count, f.ID.Int64) } else { f.Created = time.Now() f.Accessed = time.Now() // insert res, err := db.Exec(`insert into factoid ( fact, tidbit, verb, owner, created, accessed, count ) values (?, ?, ?, ?, ?, ?, ?);`, f.Fact, f.Tidbit, f.Verb, f.Owner, f.Created.Unix(), f.Accessed.Unix(), f.Count, ) if err != nil { return err } id, err := res.LastInsertId() // hackhackhack? f.ID.Int64 = id f.ID.Valid = true } return err } func (f *Factoid) delete(db *sqlx.DB) error { var err error if f.ID.Valid { _, err = db.Exec(`delete from factoid where id=?`, f.ID) } f.ID.Valid = false return err } func getFacts(db *sqlx.DB, fact string, tidbit string) ([]*Factoid, error) { var fs []*Factoid query := `select id, fact, tidbit, verb, owner, created, accessed, count from factoid where fact like ? and tidbit like ?;` rows, err := db.Query(query, "%"+fact+"%", "%"+tidbit+"%") if err != nil { log.Error().Err(err).Msg("Error regexping for facts") return nil, err } for rows.Next() { var f Factoid var tmpCreated int64 var tmpAccessed int64 err := rows.Scan( &f.ID, &f.Fact, &f.Tidbit, &f.Verb, &f.Owner, &tmpCreated, &tmpAccessed, &f.Count, ) if err != nil { return nil, err } f.Created = time.Unix(tmpCreated, 0) f.Accessed = time.Unix(tmpAccessed, 0) fs = append(fs, &f) } return fs, err } func GetSingle(db *sqlx.DB) (*Factoid, error) { var f Factoid var tmpCreated int64 var tmpAccessed int64 err := db.QueryRow(`select id, fact, tidbit, verb, owner, created, accessed, count from factoid order by random() limit 1;`).Scan( &f.ID, &f.Fact, &f.Tidbit, &f.Verb, &f.Owner, &tmpCreated, &tmpAccessed, &f.Count, ) f.Created = time.Unix(tmpCreated, 0) f.Accessed = time.Unix(tmpAccessed, 0) return &f, err } func GetSingleFact(db *sqlx.DB, fact string) (*Factoid, error) { var f Factoid var tmpCreated int64 var tmpAccessed int64 err := db.QueryRow(`select id, fact, tidbit, verb, owner, created, accessed, count from factoid where fact like ? order by random() limit 1;`, fact).Scan( &f.ID, &f.Fact, &f.Tidbit, &f.Verb, &f.Owner, &tmpCreated, &tmpAccessed, &f.Count, ) f.Created = time.Unix(tmpCreated, 0) f.Accessed = time.Unix(tmpAccessed, 0) return &f, err }