12 // ---------------------------------------------------------------------------
15 Age string // recommended age, e.g. "beginner", "junior", "ya" (Young Adult), "adult"
16 AuthorGrouping string // unique rendering of the author's name, used for internal grouping
17 AuthorReading string // reading order of author's name, e.g. "Charles Dickens"
18 AuthorSort string // sort order of author's name, e.g. "Dickens, Charles"
19 CoverId int // index into EFS table for cover, if there is one
20 DDC string // Dewey Decimal Classification
21 Description string // Back cover / inside flap blurb, describing the book
22 Genre string // e.g. "adventure", "historical", "mystery", "romance", "sf" (Science Fiction)
24 LCC string // Library of Congress Classification
30 // ---------------------------------------------------------------------------
33 Author, Language, List, Series, Sort, Title Field = "aut", "lan", "lst", "ser", "srt", "tit"
36 func (f Field) String() string {
40 // ---------------------------------------------------------------------------
41 type SearchTerm struct {
46 // ---------------------------------------------------------------------------
49 ByArrival, ByAuthor, ByPublication, ByTitle SortOrder = "arr", "aut", "pub", "tit"
52 func (so SortOrder) String() string {
56 // ---------------------------------------------------------------------------
57 var g_db *sql.DB = nil
58 var g_mutex = &sync.Mutex{}
60 // ============================================================================
61 func conditional(count int) (string, int) {
65 return "AND", (count + 1)
75 func getDb() (*sql.DB) {
78 defer g_mutex.Unlock()
81 g_db = openDb(config.user, config.pass, config.dbName)
88 func niVal(ni sql.NullInt64) int {
95 func nsVal(ns sql.NullString) string {
102 func openDb(user, pass, dbName string) (*sql.DB) {
103 db, err := sql.Open("postgres","user=" + user + " password=" + pass + " dbname=" + dbName + " sslmode=disable")
105 report("Error: DB arguments incorrect?", err)
111 report("Error: could not connect to DB.", err)
119 func queryBooksByIds(ids []int) []Book {
120 query := `SELECT s.age,a.grouping,a.reading,a.sort,b.cover,c.ddc,b.description,s.genre,b.language,c.lcc,s.descr,b.title,b.volume
122 INNER JOIN Books b ON a.id=b.author
123 LEFT OUTER JOIN Classifications c ON c.id=b.classification
124 LEFT OUTER JOIN Series s ON s.id=b.series
127 ps, err := getDb().Prepare(query)
129 report("Error: failed to prepare statement: " + query, err)
135 for _, id := range ids {
141 res := make([]Book, count)
144 for _, id := range ids {
149 row := ps.QueryRow(id)
151 var age, grouping, reading, sort, ddc, description, genre, language, lcc, name, title, volume sql.NullString
152 var cover sql.NullInt64
154 err = row.Scan(&age, &grouping, &reading, &sort, &cover, &ddc, &description, &genre, &language, &lcc, &name, &title, &volume)
156 report("Error: Failed to read book:" + strconv.Itoa(id) + ":", err)
161 b.AuthorGrouping = nsVal(grouping)
162 b.AuthorReading = nsVal(reading)
163 b.AuthorSort = nsVal(sort)
164 b.CoverId = niVal(cover)
166 b.Description = nsVal(description)
167 b.Genre = nsVal(genre)
168 b.Language = nsVal(language)
170 b.SeriesName = nsVal(name)
171 b.Title = nsVal(title)
172 b.Volume = nsVal(volume)
179 if count < len(res) {
186 func queryBookPathById(id int) (string) {
187 query := "SELECT b.path FROM Books b WHERE b.id=$1"
189 ps, err := getDb().Prepare(query)
191 report("Failed to Prepare query: " + query, err)
196 row := ps.QueryRow(id)
197 var path sql.NullString
198 err = row.Scan(&path)
200 report(fmt.Sprintf("Failed to retrieve path for book id %v: ", id), err)
207 func queryIds(criteria []SearchTerm) []int {
208 query := "SELECT b.id FROM Books b" +
209 " INNER JOIN Authors a ON a.id=b.author" +
210 " LEFT OUTER JOIN Series s ON s.id=b.series" +
211 " LEFT OUTER JOIN Lists_Books lb ON b.id=lb.book" +
212 " LEFT OUTER JOIN Lists l ON l.id=lb.list" +
215 args := make([]interface{}, 0)
216 conjunction := "WHERE"
220 for _, criterion := range criteria {
221 switch criterion.Attribute {
223 conjunction, count = conditional(count)
224 query += conjunction + " UPPER(a.grouping) LIKE UPPER($" + strconv.Itoa(count) + ")"
225 args = append(args, strings.Replace(criterion.Text, " ", "", -1))
227 conjunction, count = conditional(count)
228 query += conjunction + " UPPER(b.language) LIKE UPPER($" + strconv.Itoa(count) + ")"
229 args = append(args, criterion.Text)
231 conjunction, count = conditional(count)
232 codes := strings.Split(criterion.Text, ",")
233 query += conjunction + " UPPER(l.code) IN ("
235 for j, code := range codes {
239 query += "UPPER($" + strconv.Itoa(count + j) + ")"
240 args = append(args, code)
243 count += len(codes) - 1
245 conjunction, count = conditional(count)
246 query += conjunction + " UPPER(s.descr) LIKE UPPER($" + strconv.Itoa(count) + ")"
247 args = append(args, criterion.Text)
249 sort = SortOrder(criterion.Text)
251 conjunction, count = conditional(count)
252 query += conjunction + " UPPER(b.title) LIKE UPPER($" + strconv.Itoa(count) + ")"
253 args = append(args, criterion.Text)
255 report("Error: unrecognized search field in queryIds(): " + criterion.Attribute.String(), nil)
262 query += " ORDER BY b.arrived DESC,a.grouping,s.descr,b.volume,b.title,b.path"
264 query += " ORDER BY a.grouping,s.descr,b.volume,b.title,b.path,b.arrived DESC"
266 report("Error: cannot sort by publication (not yet implemented)", nil)
269 query += " ORDER BY b.title,a.grouping,s.descr,b.volume,b.path,b.arrived DESC"
271 report("Error: unrecognized sort order in queryIds(): " + sort.String(), nil)
277 ps, err := getDb().Prepare(query)
279 report("Failed to Prepare query: " + query, err)
285 rows, err = ps.Query(args...)
287 report("Failed to execute query: " + query, err)
295 res = append(res, id)
301 func queryMimeTypeByEfsId(efsId int) string {
302 const query = "SELECT mimeType FROM Efs WHERE id=$1"
304 ps, err := getDb().Prepare(query)
306 report("Failed to Prepare query: " + query, err)
311 row := ps.QueryRow(efsId)
312 var mimeType sql.NullString
313 err = row.Scan(&mimeType)
315 report(fmt.Sprintf("Failed to retrieve mimeType for id %v: ", efsId), err)
319 return nsVal(mimeType)
322 func report(msg string, err error) {
323 fmt.Println("Error: " + msg, err)