/*
** There are also two SQL user functions registered:
**
** rank()
** erank()
**
** rank() interprets the return value of the FTS3 matchinfo() function and
** returns a score for the match (a real number). The higher the score, the
** more relevant the document is considered. This is used to order query
** results when the user searchs the database. The rank() function takes
** (nCol+1) arguments, where nCol is the number of columns in the FTS3
** table. The first argument is the return value of matchinfo(). The
** second argument is the number of tokens in column 0 of the current FTS3
** table row. The third argument is the number of tokens in column 1, and
** so on.
**
** Function erank() is called in exactly the same way as rank(). Instead
** of returning a score, it returns an HTML formatted table containing
** data that may be used to understand how the score for the current row
** was calculated.
*/
#include <tcl.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include "sqlite3.h"
typedef unsigned int u32;
typedef unsigned char u8;
typedef sqlite3_uint64 u64;
/*
** Implementation of search result ranking function.
*/
static void rankfunc(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
u32 *aMatchinfo;
double score = 0.0;
int iCol;
int iPhrase;
int nCol;
int nPhrase;
int isExplain = sqlite3_user_data(pCtx);
char *zExplain = 0;
if( nVal==0 ) goto wna;
aMatchinfo = (u32 *)sqlite3_value_blob(apVal[0]);
nPhrase = aMatchinfo[0];
nCol = aMatchinfo[1];
if( nVal!=nCol+1 ) goto wna;
if( isExplain ) zExplain = sqlite3_mprintf("<table width=100%%>");
for(iCol=0; iCol<nCol; iCol++){
int nToken = sqlite3_value_int(apVal[iCol+1]);
double colscore = 0.0;
if( isExplain ){
zExplain = sqlite3_mprintf("%z<tr><td>%d.<td>( ", zExplain, iCol);
}
for(iPhrase=0; iPhrase<nPhrase; iPhrase++){
u32 nGlobal = aMatchinfo[2 + iPhrase*nCol + iCol];
u32 nHit = aMatchinfo[2 + nPhrase*nCol + iPhrase*nCol + iCol];
if( nHit ) colscore += (double)nHit / (double)nGlobal;
if( isExplain ){
const char *zDiv = (iPhrase==0 ? "" : "+ ");
zExplain = sqlite3_mprintf("%z%s%d/%d ", zExplain, zDiv, nHit, nGlobal);
}
}
colscore = colscore / (log(100+nToken)/log(10));
score += colscore;
if( isExplain ){
zExplain = sqlite3_mprintf(
"%z) / log(100+%d)<td> = %.4f", zExplain, nToken, colscore);
}
}
if( isExplain ){
sqlite3_result_text(pCtx, sqlite3_mprintf(
"%z<tr><td><td width=100%%><td>= <b>%.4f</b></table>", zExplain, score
), -1, sqlite3_free);
}else{
sqlite3_result_double(pCtx, score);
}
return;
wna:
sqlite3_result_error(pCtx,"wrong number of arguments to function rank()",-1);
}
int Sqlite3_Init(Tcl_Interp *interp);
static int initDb(sqlite3 *db, char **pzErr, void *p){
sqlite3_create_function(db, "rank",-1, SQLITE_UTF8, 0, rankfunc,0,0);
sqlite3_create_function(db, "erank", -1, SQLITE_UTF8, (void*)1, rankfunc,0,0);
}
static int AppInit(Tcl_Interp *interp) {
int rc;
rc = Sqlite3_Init(interp);
if( rc!=TCL_OK ) return rc;
sqlite3_auto_extension(initDb);
return TCL_OK;
}
int main(int argc, char *argv[]) {
Tcl_Main(argc, argv, AppInit);
return 0;
}