def find_by_hash(self, hash=None, book=-1):
        '''Search notes for a given (possibly abbreviated) hash'''
        if hash:
            self.fyi("nota.find_by_hash() with abbreviated hash %s; book=%s" % (hash, book))
        try:
            if book < 0:
                rows = self.cur.execute("SELECT noteId, hash FROM note WHERE book > 0;").fetchall()
            else:
                rows = self.cur.execute("SELECT noteId, hash FROM note WHERE book=?;", [book]).fetchall()
        except:
            self.error("nota.find_by_hash() cannot look up note list")
        # Possibly save time by finding IDs first.
        noteIds = []
        if hash:
            l = len(hash)
            for r in rows:
                if hash == r[1][0:l]:
                    noteIds.append((r[0],))
        else:
            for r in rows:
                noteIds.append((r[0],))
        self.fyi("noteIds: %s" % noteIds)
        rval = []
        for n in noteIds:
            try:
                note = self.cur.execute("SELECT noteId, authorId, date, title, content, due, privacy, modified, hash, book FROM note WHERE noteId=?;", n).fetchone()
            except:
                self.warning("Problem extracting note %s from database" % n)
                next
            if note:
                date = note[2]
                due = note[5]
                privacy = note[6]
                keywordIds = []
                keywordIds.extend(self.con.execute("SELECT keywordid FROM notekeyword WHERE notekeyword.noteid=?;", n))
                keywords = []
                for k in keywordIds:
                    keywords.append(self.cur.execute("SELECT keyword FROM keyword WHERE keywordId=?;", k).fetchone()[0])
                rval.append({"noteId":note[0], "title":note[3], "keywords":keywords,
                    "content":note[4], "due":note[5], "privacy":note[6],
                    "date":note[2], "modified":note[7], "hash":note[8], "book":note[9]})
        return rval