wanderinghorse.net website

Artifact [0215f670c4]
Login

Artifact [0215f670c4]

Artifact 0215f670c41a722aa4aea5acfcaa619a1b4cf89757c06d1fa74d5befb230261e:


/************************************************************************
A basic site-search script intended for use with s2cgi and an sqlite3
FTS5-indexed database of the site content.
************************************************************************/
affirm typeinfo(iscontainer cgi);
// cgi.config.scrubExceptions = false;
const s2 = s2,
      cgi = cgi,
      cliFlags = (s2.ARGV ? s2.ARGV.flags : 0) ||| {prototype:null}
;

const app = {
    sqlite3: cgi.sqlite3,
    config:{
        dbFile: cliFlags['search-db'] ||| __FILEDIR+'fts-pages.sqlite3',
        maxQueryLength: 100 /* try to discourage hypothetical "massive query attacks" */
    },
    db: proc x(){
        x.$ && return x.$;
        affirm typeinfo(isobject this.sqlite3);
        return x.$ = new this.sqlite3(this.config.dbFile,'r');
    },
    search:proc(q){
        if(!q || q.#>this.config.maxQueryLength) return false;
        const s = this.db()
              .prepare(
                  //"select round(abs(rank),2) rank, uri uri from fts where content match ?1 or uri match ?1 order by rank DESC limit 15"
                  /**
                     It turns out that we can give a match in the URI
                     a higher rank than the content :) (significant
                     for uri /gaming/painting when searching for
                     "painting") ==> bm25(mtime, uri, content). */
                  "select uri uri, round(abs(rank),2) rank from fts where fts match ?1 and rank match 'bm25(0.0,100.0,1.0)' order by rank desc limit 15"
              ).bind(1,q),
              r = [];
        for(var v; v=s.stepObject(); r[]=v);
        s.finalize();
        return r;
    }
};
const out = s2.require(['s2cgi/basicJsonOutputHandler']).0;
cgi.request.pathList && return out.tooMuchPath();
const q = cgi.getGET('q');
out.main({q, results:app.search(q)});
app.db().close();