wanderinghorse.net website

search.s2 at tip
Login

search.s2 at tip

File cgi-bin/search.s2 from the latest check-in


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
/************************************************************************
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();