1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-01 06:27:03 +03:00

If a token within an FTS query is prefixed with a '^' character, it must be the first token in a column of data to match.

FossilOrigin-Name: 63ac33c860eb32ce96699f06bf83121cec2ffaca
This commit is contained in:
dan
2011-10-18 19:39:41 +00:00
parent a986d33fd3
commit 3f1ea8d114
9 changed files with 183 additions and 14 deletions

View File

@ -2347,6 +2347,67 @@ static void fts3DoclistPhraseMerge(
*pnRight = p - aOut;
}
/*
** When this function is called, pList points to a doclist containing position
** data, length *pnList bytes. This removes all entries from the doclist that
** do not correspond to the first token in a column and overwrites pList
** with the result. *pnList is set to the length of the new doclist before
** returning.
**
** If bDescDoclist is true, then both the input and output are in descending
** order. Otherwise, ascending.
*/
static void fts3DoclistFirstFilter(
int bDescDoclist, /* True if pList is a descending doclist */
char *pList, /* Buffer containing doclist */
int *pnList /* IN/OUT: Size of doclist */
){
char *p = pList;
char *pOut = pList;
char *pEnd = &pList[*pnList];
sqlite3_int64 iDoc;
sqlite3_int64 iPrev;
int bFirstOut = 0;
fts3GetDeltaVarint3(&p, pEnd, 0, &iDoc);
while( p ){
int bWritten = 0;
if( *p!=0x01 ){
if( *p==0x02 ){
fts3PutDeltaVarint3(&pOut, bDescDoclist, &iPrev, &bFirstOut, iDoc);
*pOut++ = 0x02;
bWritten = 1;
}
fts3ColumnlistCopy(0, &p);
}
while( *p==0x01 ){
sqlite3_int64 iCol;
p++;
p += sqlite3Fts3GetVarint(p, &iCol);
if( *p==0x02 ){
if( bWritten==0 ){
fts3PutDeltaVarint3(&pOut, bDescDoclist, &iPrev, &bFirstOut, iDoc);
bWritten = 1;
}
pOut += sqlite3Fts3PutVarint(pOut, iCol);
*pOut++ = 0x02;
}
fts3ColumnlistCopy(0, &p);
}
if( bWritten ){
*pOut++ = 0x00;
}
assert( *p==0x00 );
p++;
fts3GetDeltaVarint3(&p, pEnd, bDescDoclist, &iDoc);
}
*pnList = (pOut - pList);
}
/*
** Merge all doclists in the TermSelect.aaOutput[] array into a single
@ -3518,6 +3579,10 @@ static void fts3EvalPhraseMergeToken(
){
assert( iToken!=p->iDoclistToken );
if( p->aToken[iToken].bFirst ){
fts3DoclistFirstFilter(pTab->bDescIdx, pList, &nList);
}
if( pList==0 ){
sqlite3_free(p->doclist.aAll);
p->doclist.aAll = 0;
@ -3721,6 +3786,7 @@ static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
&& p->nToken==1
&& pFirst->pSegcsr
&& pFirst->pSegcsr->bLookup
&& pFirst->bFirst==0
){
/* Use the incremental approach. */
int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);

View File

@ -310,6 +310,7 @@ struct Fts3PhraseToken {
char *z; /* Text of the token */
int n; /* Number of bytes in buffer z */
int isPrefix; /* True if token ends with a "*" character */
int bFirst; /* True if token must appear at position 0 */
/* Variables above this point are populated when the expression is
** parsed (by code in fts3_expr.c). Below this point the variables are

View File

@ -180,9 +180,21 @@ static int getNextToken(
pRet->pPhrase->aToken[0].isPrefix = 1;
iEnd++;
}
if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
pParse->isNot = 1;
while( 1 ){
if( !sqlite3_fts3_enable_parentheses
&& iStart>0 && z[iStart-1]=='-'
){
pParse->isNot = 1;
iStart--;
}else if( iStart>0 && z[iStart-1]=='^' ){
pRet->pPhrase->aToken[0].bFirst = 1;
iStart--;
}else{
break;
}
}
}
nConsumed = iEnd;
}
@ -281,6 +293,7 @@ static int getNextString(
pToken->n = nByte;
pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
nToken = ii+1;
}
}

View File

@ -3117,6 +3117,7 @@ int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
Fts3PhraseToken *pPT = pDef->pToken;
if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
&& (pPT->bFirst==0 || iPos==0)
&& (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
&& (0==memcmp(zToken, pPT->z, pPT->n))
){