Fossil SCM
Populate the search index with check-ins, tickets, and wiki.
Commit
786a3632a8e0d10494facab0ff3812a751c2f891
Parent
91da57d911c1485…
1 file changed
+100
-88
+100
-88
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -778,10 +778,11 @@ | ||
| 778 | 778 | "CREATE TEMP TABLE x(label,url,score,date,snip);" |
| 779 | 779 | ); |
| 780 | 780 | if( !search_index_exists() ){ |
| 781 | 781 | search_fullscan(zPattern, srchFlags); |
| 782 | 782 | }else{ |
| 783 | + search_update_index(); | |
| 783 | 784 | search_indexed(zPattern, srchFlags); |
| 784 | 785 | } |
| 785 | 786 | db_prepare(&q, "SELECT url, snip, label" |
| 786 | 787 | " FROM x" |
| 787 | 788 | " ORDER BY score DESC, date DESC;"); |
| @@ -983,66 +984,21 @@ | ||
| 983 | 984 | break; |
| 984 | 985 | } |
| 985 | 986 | } |
| 986 | 987 | } |
| 987 | 988 | |
| 988 | -/* | |
| 989 | -** The arguments cType,rid,zName define an object that can be searched | |
| 990 | -** for. Return a URL (relative to the root of the Fossil project) that | |
| 991 | -** will jump to that document. | |
| 992 | -** | |
| 993 | -** Space to hold the returned string is obtained from mprintf() and should | |
| 994 | -** be freed by the caller using fossil_free() or the equivalent. | |
| 995 | -*/ | |
| 996 | -char *search_url( | |
| 997 | - char cType, /* Type of document */ | |
| 998 | - int rid, /* BLOB.RID or TAG.TAGID for the object */ | |
| 999 | - const char *zName /* Name of the object */ | |
| 1000 | -){ | |
| 1001 | - char *zUrl = 0; | |
| 1002 | - switch( cType ){ | |
| 1003 | - case 'd': { /* Documents */ | |
| 1004 | - zUrl = db_text(0, | |
| 1005 | - "SELECT printf('/doc/%%s%%s', substr(blob.uuid,20), %Q)" | |
| 1006 | - " FROM mlink, blob" | |
| 1007 | - " WHERE mlink.fid=%d AND mlink.mid=blob.rid", | |
| 1008 | - zName, rid); | |
| 1009 | - break; | |
| 1010 | - } | |
| 1011 | - case 'w': { /* Wiki */ | |
| 1012 | - char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 1013 | - zUrl = mprintf("/wiki?id=%z&name=%t", zId, zName); | |
| 1014 | - break; | |
| 1015 | - } | |
| 1016 | - case 'c': { /* Ckeck-in Comment */ | |
| 1017 | - char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 1018 | - zUrl = mprintf("/info/%z", zId); | |
| 1019 | - break; | |
| 1020 | - } | |
| 1021 | - case 't': { /* Tickets */ | |
| 1022 | - char *zId = db_text(0, "SELECT tkt_uuid FROM ticket" | |
| 1023 | - " WHERE tkt_id=%d", rid); | |
| 1024 | - zUrl = mprintf("/tktview/%.20z", zId); | |
| 1025 | - break; | |
| 1026 | - } | |
| 1027 | - } | |
| 1028 | - return zUrl; | |
| 1029 | -} | |
| 1030 | - | |
| 1031 | 989 | /* |
| 1032 | 990 | ** COMMAND: test-search-stext |
| 1033 | 991 | ** |
| 1034 | 992 | ** Usage: fossil test-search-stext TYPE ARG1 ARG2 |
| 1035 | 993 | */ |
| 1036 | 994 | void test_search_stext(void){ |
| 1037 | 995 | Blob out; |
| 1038 | - char *zUrl; | |
| 1039 | 996 | db_find_and_open_repository(0,0); |
| 1040 | 997 | if( g.argc!=5 ) usage("TYPE RID NAME"); |
| 1041 | 998 | search_stext(g.argv[2][0], atoi(g.argv[3]), g.argv[4], &out); |
| 1042 | - zUrl = search_url(g.argv[2][0], atoi(g.argv[3]), g.argv[4]); | |
| 1043 | - fossil_print("%s\n%z\n",blob_str(&out),zUrl); | |
| 999 | + fossil_print("%s\n",blob_str(&out)); | |
| 1044 | 1000 | blob_reset(&out); |
| 1045 | 1001 | } |
| 1046 | 1002 | |
| 1047 | 1003 | /* The schema for the full-text index |
| 1048 | 1004 | */ |
| @@ -1058,10 +1014,11 @@ | ||
| 1058 | 1014 | @ url TEXT, -- URL to access this document |
| 1059 | 1015 | @ mtime DATE, -- Date when document created |
| 1060 | 1016 | @ UNIQUE(type,rid) |
| 1061 | 1017 | @ ); |
| 1062 | 1018 | @ CREATE INDEX "%w".ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0; |
| 1019 | +@ CREATE INDEX "%w".ftsdocName ON ftsdocs(name) WHERE type='w'; | |
| 1063 | 1020 | @ CREATE VIEW IF NOT EXISTS "%w".ftscontent AS |
| 1064 | 1021 | @ SELECT rowid, type, rid, name, idxed, label, url, mtime, |
| 1065 | 1022 | @ stext(type,rid,name) AS 'stext' |
| 1066 | 1023 | @ FROM ftsdocs; |
| 1067 | 1024 | @ CREATE VIRTUAL TABLE IF NOT EXISTS "%w".ftsidx |
| @@ -1077,11 +1034,12 @@ | ||
| 1077 | 1034 | ** Create or drop the tables associated with a full-text index. |
| 1078 | 1035 | */ |
| 1079 | 1036 | void search_create_index(void){ |
| 1080 | 1037 | const char *zDb = db_name("repository"); |
| 1081 | 1038 | search_sql_setup(g.db); |
| 1082 | - db_multi_exec(zFtsSchema/*works-like:"%w%w%w%w"*/, zDb, zDb, zDb, zDb); | |
| 1039 | + db_multi_exec(zFtsSchema/*works-like:"%w%w%w%w%w"*/, | |
| 1040 | + zDb, zDb, zDb, zDb, zDb); | |
| 1083 | 1041 | } |
| 1084 | 1042 | void search_drop_index(void){ |
| 1085 | 1043 | const char *zDb = db_name("repository"); |
| 1086 | 1044 | db_multi_exec(zFtsDrop/*works-like:"%w%w%w"*/, zDb, zDb, zDb); |
| 1087 | 1045 | } |
| @@ -1130,20 +1088,34 @@ | ||
| 1130 | 1088 | ** now while we still have access to the old content. Add the document |
| 1131 | 1089 | ** to the queue of documents that need to be indexed or reindexed. |
| 1132 | 1090 | */ |
| 1133 | 1091 | void search_doc_touch(char cType, int rid, const char *zName){ |
| 1134 | 1092 | if( search_index_exists() ){ |
| 1093 | + char zType[2]; | |
| 1094 | + zType[0] = cType; | |
| 1095 | + zType[1] = 0; | |
| 1135 | 1096 | db_multi_exec( |
| 1136 | 1097 | "DELETE FROM ftsidx WHERE docid IN" |
| 1137 | 1098 | " (SELECT rowid FROM ftsdocs WHERE type=%Q AND rid=%d AND idxed)", |
| 1138 | - cType, rid | |
| 1099 | + zType, rid | |
| 1139 | 1100 | ); |
| 1140 | 1101 | db_multi_exec( |
| 1141 | 1102 | "REPLACE INTO ftsdocs(type,rid,name,idxed)" |
| 1142 | 1103 | " VALUES(%Q,%d,%Q,0)", |
| 1143 | - cType, rid, zName | |
| 1104 | + zType, rid, zName | |
| 1144 | 1105 | ); |
| 1106 | + if( cType=='w' ){ | |
| 1107 | + db_multi_exec( | |
| 1108 | + "DELETE FROM ftsidx WHERE docid IN" | |
| 1109 | + " (SELECT rowid FROM ftsdocs WHERE type='w' AND name=%Q AND idxed)", | |
| 1110 | + zName | |
| 1111 | + ); | |
| 1112 | + db_multi_exec( | |
| 1113 | + "DELETE FROM ftsdocs WHERE type='w' AND name=%Q AND rid!=%d", | |
| 1114 | + zName, rid | |
| 1115 | + ); | |
| 1116 | + } | |
| 1145 | 1117 | } |
| 1146 | 1118 | } |
| 1147 | 1119 | |
| 1148 | 1120 | /* |
| 1149 | 1121 | ** If the doc-glob and doc-br settings are valid for document search |
| @@ -1197,21 +1169,90 @@ | ||
| 1197 | 1169 | ); |
| 1198 | 1170 | db_multi_exec( |
| 1199 | 1171 | "UPDATE ftsdocs SET idxed=1 WHERE type='d' AND NOT idxed" |
| 1200 | 1172 | ); |
| 1201 | 1173 | } |
| 1174 | + | |
| 1175 | +/* | |
| 1176 | +** Deal with all of the unindexed 'c' terms in FTSDOCS | |
| 1177 | +*/ | |
| 1178 | +static void search_update_checkin_index(void){ | |
| 1179 | + db_multi_exec( | |
| 1180 | + "INSERT INTO ftsidx(docid,stext)" | |
| 1181 | + " SELECT rowid, stext('c',rid,NULL) FROM ftsdocs" | |
| 1182 | + " WHERE type='c' AND NOT idxed;" | |
| 1183 | + ); | |
| 1184 | + db_multi_exec( | |
| 1185 | + "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)" | |
| 1186 | + " SELECT ftsdocs.rowid, 1, 'c', ftsdocs.rid, NULL," | |
| 1187 | + " printf('Check-in [%%.16s] on %%s',blob.uuid,datetime(event.mtime))," | |
| 1188 | + " printf('/timeline?y=ci&n=9&c=%%.20s',blob.uuid)," | |
| 1189 | + " event.mtime" | |
| 1190 | + " FROM ftsdocs, event, blob" | |
| 1191 | + " WHERE ftsdocs.type='c' AND NOT ftsdocs.idxed" | |
| 1192 | + " AND event.objid=ftsdocs.rid" | |
| 1193 | + " AND blob.rid=ftsdocs.rid" | |
| 1194 | + ); | |
| 1195 | +} | |
| 1196 | + | |
| 1197 | +/* | |
| 1198 | +** Deal with all of the unindexed 't' terms in FTSDOCS | |
| 1199 | +*/ | |
| 1200 | +static void search_update_ticket_index(void){ | |
| 1201 | + db_multi_exec( | |
| 1202 | + "INSERT INTO ftsidx(docid,stext)" | |
| 1203 | + " SELECT rowid, stext('t',rid,NULL) FROM ftsdocs" | |
| 1204 | + " WHERE type='t' AND NOT idxed;" | |
| 1205 | + ); | |
| 1206 | + if( db_changes()==0 ) return; | |
| 1207 | + db_multi_exec( | |
| 1208 | + "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)" | |
| 1209 | + " SELECT ftsdocs.rowid, 1, 't', ftsdocs.rid, NULL," | |
| 1210 | + " printf('Ticket [%%.16s] on %%s',tkt_uuid,datetime(tkt_mtime))," | |
| 1211 | + " printf('/tktview/%%.20s',tkt_uuid)," | |
| 1212 | + " tkt_mtime" | |
| 1213 | + " FROM ftsdocs, ticket" | |
| 1214 | + " WHERE ftsdocs.type='t' AND NOT ftsdocs.idxed" | |
| 1215 | + " AND ticket.tkt_id=ftsdocs.rid" | |
| 1216 | + ); | |
| 1217 | +} | |
| 1218 | + | |
| 1219 | +/* | |
| 1220 | +** Deal with all of the unindexed 'w' terms in FTSDOCS | |
| 1221 | +*/ | |
| 1222 | +static void search_update_wiki_index(void){ | |
| 1223 | + db_multi_exec( | |
| 1224 | + "INSERT INTO ftsidx(docid,stext)" | |
| 1225 | + " SELECT rowid, stext('w',rid,NULL) FROM ftsdocs" | |
| 1226 | + " WHERE type='t' AND NOT idxed;" | |
| 1227 | + ); | |
| 1228 | + if( db_changes()==0 ) return; | |
| 1229 | + db_multi_exec( | |
| 1230 | + "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)" | |
| 1231 | + " SELECT ftsdocs.rowid, 1, 'w', ftsdocs.rid, ftsdocs.name," | |
| 1232 | + " 'Wiki: '||tsdocs.name)," | |
| 1233 | + " '/wiki?name='||urlencode(ftsdocs.name)," | |
| 1234 | + " tagxref.mtime" | |
| 1235 | + " FROM ftsdocs, tagxref" | |
| 1236 | + " WHERE ftsdocs.type='t' AND NOT ftsdocs.idxed" | |
| 1237 | + " AND tagxref.rid=ftsdocs.rid" | |
| 1238 | + ); | |
| 1239 | +} | |
| 1202 | 1240 | |
| 1203 | 1241 | /* |
| 1204 | 1242 | ** Deal with all of the unindexed entries in the FTSDOCS table - that |
| 1205 | 1243 | ** is to say, all the entries with FTSDOCS.IDXED=0. Add them to the |
| 1206 | 1244 | ** index. |
| 1207 | 1245 | */ |
| 1208 | 1246 | void search_update_index(void){ |
| 1209 | 1247 | if( !search_index_exists() ) return; |
| 1248 | + if( !db_exists("SELECT 1 FROM ftsdocs WHERE NOT idxed") ) return; | |
| 1210 | 1249 | search_sql_setup(g.db); |
| 1211 | 1250 | search_update_doc_index(); |
| 1212 | - | |
| 1251 | + search_update_checkin_index(); | |
| 1252 | + search_update_ticket_index(); | |
| 1253 | + search_update_wiki_index(); | |
| 1213 | 1254 | } |
| 1214 | 1255 | |
| 1215 | 1256 | /* |
| 1216 | 1257 | ** COMMAND: test-fts |
| 1217 | 1258 | */ |
| @@ -1221,13 +1262,12 @@ | ||
| 1221 | 1262 | static const struct { int iCmd; const char *z; } aCmd[] = { |
| 1222 | 1263 | { 1, "create" }, |
| 1223 | 1264 | { 2, "drop" }, |
| 1224 | 1265 | { 3, "exists" }, |
| 1225 | 1266 | { 4, "fill" }, |
| 1226 | - { 8, "refill" }, | |
| 1267 | + { 8, "refill" }, | |
| 1227 | 1268 | { 5, "pending" }, |
| 1228 | - { 6, "all" }, | |
| 1229 | 1269 | { 7, "update" }, |
| 1230 | 1270 | }; |
| 1231 | 1271 | db_find_and_open_repository(0, 0); |
| 1232 | 1272 | if( g.argc<3 ) usage("SUBCMD ..."); |
| 1233 | 1273 | zSubCmd = g.argv[2]; |
| @@ -1267,46 +1307,18 @@ | ||
| 1267 | 1307 | break; |
| 1268 | 1308 | } |
| 1269 | 1309 | case 5: { assert( fossil_strncmp(zSubCmd, "pending", n)==0 ); |
| 1270 | 1310 | Stmt q; |
| 1271 | 1311 | if( !search_index_exists() ) break; |
| 1272 | - db_prepare(&q, "SELECT rowid, type, rid, quote(label), url, date(mtime)" | |
| 1273 | - " FROM ftsdocs" | |
| 1274 | - " WHERE NOT idxed"); | |
| 1275 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 1276 | - const char *zUrl = db_column_text(&q,4); | |
| 1277 | - if( zUrl && zUrl[0] ){ | |
| 1278 | - fossil_print("%6d: %s %6d %s %s\n %s\n", | |
| 1279 | - db_column_int(&q, 0), | |
| 1280 | - db_column_text(&q, 1), | |
| 1281 | - db_column_int(&q, 2), | |
| 1282 | - db_column_text(&q, 5), | |
| 1283 | - db_column_text(&q, 3), | |
| 1284 | - zUrl); | |
| 1285 | - }else{ | |
| 1286 | - fossil_print("%6d: %s %6d %s %s\n", | |
| 1287 | - db_column_int(&q, 0), | |
| 1288 | - db_column_text(&q, 1), | |
| 1289 | - db_column_int(&q, 2), | |
| 1290 | - db_column_text(&q, 5), | |
| 1291 | - db_column_text(&q, 3)); | |
| 1292 | - } | |
| 1293 | - } | |
| 1294 | - db_finalize(&q); | |
| 1295 | - break; | |
| 1296 | - } | |
| 1297 | - case 6: { assert( fossil_strncmp(zSubCmd, "all", n)==0 ); | |
| 1298 | - Stmt q; | |
| 1299 | - if( !search_index_exists() ) break; | |
| 1300 | - db_prepare(&q, "SELECT rowid,type,rid,quote(name),idxed FROM ftsdocs"); | |
| 1301 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 1302 | - fossil_print("%6d: %s %6d %s%s\n", | |
| 1303 | - db_column_int(&q, 0), | |
| 1304 | - db_column_text(&q, 1), | |
| 1305 | - db_column_int(&q, 2), | |
| 1306 | - db_column_text(&q, 3), | |
| 1307 | - db_column_int(&q, 4) ? "" : " (NOT INDEXED)" | |
| 1312 | + db_prepare(&q, "SELECT rowid,type,rid,quote(name) FROM ftsdocs" | |
| 1313 | + " WHERE NOT idxed"); | |
| 1314 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1315 | + fossil_print("%6d: %s %6d %s\n", | |
| 1316 | + db_column_int(&q, 0), | |
| 1317 | + db_column_text(&q, 1), | |
| 1318 | + db_column_int(&q, 2), | |
| 1319 | + db_column_text(&q, 3) | |
| 1308 | 1320 | ); |
| 1309 | 1321 | } |
| 1310 | 1322 | db_finalize(&q); |
| 1311 | 1323 | break; |
| 1312 | 1324 | } |
| 1313 | 1325 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -778,10 +778,11 @@ | |
| 778 | "CREATE TEMP TABLE x(label,url,score,date,snip);" |
| 779 | ); |
| 780 | if( !search_index_exists() ){ |
| 781 | search_fullscan(zPattern, srchFlags); |
| 782 | }else{ |
| 783 | search_indexed(zPattern, srchFlags); |
| 784 | } |
| 785 | db_prepare(&q, "SELECT url, snip, label" |
| 786 | " FROM x" |
| 787 | " ORDER BY score DESC, date DESC;"); |
| @@ -983,66 +984,21 @@ | |
| 983 | break; |
| 984 | } |
| 985 | } |
| 986 | } |
| 987 | |
| 988 | /* |
| 989 | ** The arguments cType,rid,zName define an object that can be searched |
| 990 | ** for. Return a URL (relative to the root of the Fossil project) that |
| 991 | ** will jump to that document. |
| 992 | ** |
| 993 | ** Space to hold the returned string is obtained from mprintf() and should |
| 994 | ** be freed by the caller using fossil_free() or the equivalent. |
| 995 | */ |
| 996 | char *search_url( |
| 997 | char cType, /* Type of document */ |
| 998 | int rid, /* BLOB.RID or TAG.TAGID for the object */ |
| 999 | const char *zName /* Name of the object */ |
| 1000 | ){ |
| 1001 | char *zUrl = 0; |
| 1002 | switch( cType ){ |
| 1003 | case 'd': { /* Documents */ |
| 1004 | zUrl = db_text(0, |
| 1005 | "SELECT printf('/doc/%%s%%s', substr(blob.uuid,20), %Q)" |
| 1006 | " FROM mlink, blob" |
| 1007 | " WHERE mlink.fid=%d AND mlink.mid=blob.rid", |
| 1008 | zName, rid); |
| 1009 | break; |
| 1010 | } |
| 1011 | case 'w': { /* Wiki */ |
| 1012 | char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1013 | zUrl = mprintf("/wiki?id=%z&name=%t", zId, zName); |
| 1014 | break; |
| 1015 | } |
| 1016 | case 'c': { /* Ckeck-in Comment */ |
| 1017 | char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1018 | zUrl = mprintf("/info/%z", zId); |
| 1019 | break; |
| 1020 | } |
| 1021 | case 't': { /* Tickets */ |
| 1022 | char *zId = db_text(0, "SELECT tkt_uuid FROM ticket" |
| 1023 | " WHERE tkt_id=%d", rid); |
| 1024 | zUrl = mprintf("/tktview/%.20z", zId); |
| 1025 | break; |
| 1026 | } |
| 1027 | } |
| 1028 | return zUrl; |
| 1029 | } |
| 1030 | |
| 1031 | /* |
| 1032 | ** COMMAND: test-search-stext |
| 1033 | ** |
| 1034 | ** Usage: fossil test-search-stext TYPE ARG1 ARG2 |
| 1035 | */ |
| 1036 | void test_search_stext(void){ |
| 1037 | Blob out; |
| 1038 | char *zUrl; |
| 1039 | db_find_and_open_repository(0,0); |
| 1040 | if( g.argc!=5 ) usage("TYPE RID NAME"); |
| 1041 | search_stext(g.argv[2][0], atoi(g.argv[3]), g.argv[4], &out); |
| 1042 | zUrl = search_url(g.argv[2][0], atoi(g.argv[3]), g.argv[4]); |
| 1043 | fossil_print("%s\n%z\n",blob_str(&out),zUrl); |
| 1044 | blob_reset(&out); |
| 1045 | } |
| 1046 | |
| 1047 | /* The schema for the full-text index |
| 1048 | */ |
| @@ -1058,10 +1014,11 @@ | |
| 1058 | @ url TEXT, -- URL to access this document |
| 1059 | @ mtime DATE, -- Date when document created |
| 1060 | @ UNIQUE(type,rid) |
| 1061 | @ ); |
| 1062 | @ CREATE INDEX "%w".ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0; |
| 1063 | @ CREATE VIEW IF NOT EXISTS "%w".ftscontent AS |
| 1064 | @ SELECT rowid, type, rid, name, idxed, label, url, mtime, |
| 1065 | @ stext(type,rid,name) AS 'stext' |
| 1066 | @ FROM ftsdocs; |
| 1067 | @ CREATE VIRTUAL TABLE IF NOT EXISTS "%w".ftsidx |
| @@ -1077,11 +1034,12 @@ | |
| 1077 | ** Create or drop the tables associated with a full-text index. |
| 1078 | */ |
| 1079 | void search_create_index(void){ |
| 1080 | const char *zDb = db_name("repository"); |
| 1081 | search_sql_setup(g.db); |
| 1082 | db_multi_exec(zFtsSchema/*works-like:"%w%w%w%w"*/, zDb, zDb, zDb, zDb); |
| 1083 | } |
| 1084 | void search_drop_index(void){ |
| 1085 | const char *zDb = db_name("repository"); |
| 1086 | db_multi_exec(zFtsDrop/*works-like:"%w%w%w"*/, zDb, zDb, zDb); |
| 1087 | } |
| @@ -1130,20 +1088,34 @@ | |
| 1130 | ** now while we still have access to the old content. Add the document |
| 1131 | ** to the queue of documents that need to be indexed or reindexed. |
| 1132 | */ |
| 1133 | void search_doc_touch(char cType, int rid, const char *zName){ |
| 1134 | if( search_index_exists() ){ |
| 1135 | db_multi_exec( |
| 1136 | "DELETE FROM ftsidx WHERE docid IN" |
| 1137 | " (SELECT rowid FROM ftsdocs WHERE type=%Q AND rid=%d AND idxed)", |
| 1138 | cType, rid |
| 1139 | ); |
| 1140 | db_multi_exec( |
| 1141 | "REPLACE INTO ftsdocs(type,rid,name,idxed)" |
| 1142 | " VALUES(%Q,%d,%Q,0)", |
| 1143 | cType, rid, zName |
| 1144 | ); |
| 1145 | } |
| 1146 | } |
| 1147 | |
| 1148 | /* |
| 1149 | ** If the doc-glob and doc-br settings are valid for document search |
| @@ -1197,21 +1169,90 @@ | |
| 1197 | ); |
| 1198 | db_multi_exec( |
| 1199 | "UPDATE ftsdocs SET idxed=1 WHERE type='d' AND NOT idxed" |
| 1200 | ); |
| 1201 | } |
| 1202 | |
| 1203 | /* |
| 1204 | ** Deal with all of the unindexed entries in the FTSDOCS table - that |
| 1205 | ** is to say, all the entries with FTSDOCS.IDXED=0. Add them to the |
| 1206 | ** index. |
| 1207 | */ |
| 1208 | void search_update_index(void){ |
| 1209 | if( !search_index_exists() ) return; |
| 1210 | search_sql_setup(g.db); |
| 1211 | search_update_doc_index(); |
| 1212 | |
| 1213 | } |
| 1214 | |
| 1215 | /* |
| 1216 | ** COMMAND: test-fts |
| 1217 | */ |
| @@ -1221,13 +1262,12 @@ | |
| 1221 | static const struct { int iCmd; const char *z; } aCmd[] = { |
| 1222 | { 1, "create" }, |
| 1223 | { 2, "drop" }, |
| 1224 | { 3, "exists" }, |
| 1225 | { 4, "fill" }, |
| 1226 | { 8, "refill" }, |
| 1227 | { 5, "pending" }, |
| 1228 | { 6, "all" }, |
| 1229 | { 7, "update" }, |
| 1230 | }; |
| 1231 | db_find_and_open_repository(0, 0); |
| 1232 | if( g.argc<3 ) usage("SUBCMD ..."); |
| 1233 | zSubCmd = g.argv[2]; |
| @@ -1267,46 +1307,18 @@ | |
| 1267 | break; |
| 1268 | } |
| 1269 | case 5: { assert( fossil_strncmp(zSubCmd, "pending", n)==0 ); |
| 1270 | Stmt q; |
| 1271 | if( !search_index_exists() ) break; |
| 1272 | db_prepare(&q, "SELECT rowid, type, rid, quote(label), url, date(mtime)" |
| 1273 | " FROM ftsdocs" |
| 1274 | " WHERE NOT idxed"); |
| 1275 | while( db_step(&q)==SQLITE_ROW ){ |
| 1276 | const char *zUrl = db_column_text(&q,4); |
| 1277 | if( zUrl && zUrl[0] ){ |
| 1278 | fossil_print("%6d: %s %6d %s %s\n %s\n", |
| 1279 | db_column_int(&q, 0), |
| 1280 | db_column_text(&q, 1), |
| 1281 | db_column_int(&q, 2), |
| 1282 | db_column_text(&q, 5), |
| 1283 | db_column_text(&q, 3), |
| 1284 | zUrl); |
| 1285 | }else{ |
| 1286 | fossil_print("%6d: %s %6d %s %s\n", |
| 1287 | db_column_int(&q, 0), |
| 1288 | db_column_text(&q, 1), |
| 1289 | db_column_int(&q, 2), |
| 1290 | db_column_text(&q, 5), |
| 1291 | db_column_text(&q, 3)); |
| 1292 | } |
| 1293 | } |
| 1294 | db_finalize(&q); |
| 1295 | break; |
| 1296 | } |
| 1297 | case 6: { assert( fossil_strncmp(zSubCmd, "all", n)==0 ); |
| 1298 | Stmt q; |
| 1299 | if( !search_index_exists() ) break; |
| 1300 | db_prepare(&q, "SELECT rowid,type,rid,quote(name),idxed FROM ftsdocs"); |
| 1301 | while( db_step(&q)==SQLITE_ROW ){ |
| 1302 | fossil_print("%6d: %s %6d %s%s\n", |
| 1303 | db_column_int(&q, 0), |
| 1304 | db_column_text(&q, 1), |
| 1305 | db_column_int(&q, 2), |
| 1306 | db_column_text(&q, 3), |
| 1307 | db_column_int(&q, 4) ? "" : " (NOT INDEXED)" |
| 1308 | ); |
| 1309 | } |
| 1310 | db_finalize(&q); |
| 1311 | break; |
| 1312 | } |
| 1313 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -778,10 +778,11 @@ | |
| 778 | "CREATE TEMP TABLE x(label,url,score,date,snip);" |
| 779 | ); |
| 780 | if( !search_index_exists() ){ |
| 781 | search_fullscan(zPattern, srchFlags); |
| 782 | }else{ |
| 783 | search_update_index(); |
| 784 | search_indexed(zPattern, srchFlags); |
| 785 | } |
| 786 | db_prepare(&q, "SELECT url, snip, label" |
| 787 | " FROM x" |
| 788 | " ORDER BY score DESC, date DESC;"); |
| @@ -983,66 +984,21 @@ | |
| 984 | break; |
| 985 | } |
| 986 | } |
| 987 | } |
| 988 | |
| 989 | /* |
| 990 | ** COMMAND: test-search-stext |
| 991 | ** |
| 992 | ** Usage: fossil test-search-stext TYPE ARG1 ARG2 |
| 993 | */ |
| 994 | void test_search_stext(void){ |
| 995 | Blob out; |
| 996 | db_find_and_open_repository(0,0); |
| 997 | if( g.argc!=5 ) usage("TYPE RID NAME"); |
| 998 | search_stext(g.argv[2][0], atoi(g.argv[3]), g.argv[4], &out); |
| 999 | fossil_print("%s\n",blob_str(&out)); |
| 1000 | blob_reset(&out); |
| 1001 | } |
| 1002 | |
| 1003 | /* The schema for the full-text index |
| 1004 | */ |
| @@ -1058,10 +1014,11 @@ | |
| 1014 | @ url TEXT, -- URL to access this document |
| 1015 | @ mtime DATE, -- Date when document created |
| 1016 | @ UNIQUE(type,rid) |
| 1017 | @ ); |
| 1018 | @ CREATE INDEX "%w".ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0; |
| 1019 | @ CREATE INDEX "%w".ftsdocName ON ftsdocs(name) WHERE type='w'; |
| 1020 | @ CREATE VIEW IF NOT EXISTS "%w".ftscontent AS |
| 1021 | @ SELECT rowid, type, rid, name, idxed, label, url, mtime, |
| 1022 | @ stext(type,rid,name) AS 'stext' |
| 1023 | @ FROM ftsdocs; |
| 1024 | @ CREATE VIRTUAL TABLE IF NOT EXISTS "%w".ftsidx |
| @@ -1077,11 +1034,12 @@ | |
| 1034 | ** Create or drop the tables associated with a full-text index. |
| 1035 | */ |
| 1036 | void search_create_index(void){ |
| 1037 | const char *zDb = db_name("repository"); |
| 1038 | search_sql_setup(g.db); |
| 1039 | db_multi_exec(zFtsSchema/*works-like:"%w%w%w%w%w"*/, |
| 1040 | zDb, zDb, zDb, zDb, zDb); |
| 1041 | } |
| 1042 | void search_drop_index(void){ |
| 1043 | const char *zDb = db_name("repository"); |
| 1044 | db_multi_exec(zFtsDrop/*works-like:"%w%w%w"*/, zDb, zDb, zDb); |
| 1045 | } |
| @@ -1130,20 +1088,34 @@ | |
| 1088 | ** now while we still have access to the old content. Add the document |
| 1089 | ** to the queue of documents that need to be indexed or reindexed. |
| 1090 | */ |
| 1091 | void search_doc_touch(char cType, int rid, const char *zName){ |
| 1092 | if( search_index_exists() ){ |
| 1093 | char zType[2]; |
| 1094 | zType[0] = cType; |
| 1095 | zType[1] = 0; |
| 1096 | db_multi_exec( |
| 1097 | "DELETE FROM ftsidx WHERE docid IN" |
| 1098 | " (SELECT rowid FROM ftsdocs WHERE type=%Q AND rid=%d AND idxed)", |
| 1099 | zType, rid |
| 1100 | ); |
| 1101 | db_multi_exec( |
| 1102 | "REPLACE INTO ftsdocs(type,rid,name,idxed)" |
| 1103 | " VALUES(%Q,%d,%Q,0)", |
| 1104 | zType, rid, zName |
| 1105 | ); |
| 1106 | if( cType=='w' ){ |
| 1107 | db_multi_exec( |
| 1108 | "DELETE FROM ftsidx WHERE docid IN" |
| 1109 | " (SELECT rowid FROM ftsdocs WHERE type='w' AND name=%Q AND idxed)", |
| 1110 | zName |
| 1111 | ); |
| 1112 | db_multi_exec( |
| 1113 | "DELETE FROM ftsdocs WHERE type='w' AND name=%Q AND rid!=%d", |
| 1114 | zName, rid |
| 1115 | ); |
| 1116 | } |
| 1117 | } |
| 1118 | } |
| 1119 | |
| 1120 | /* |
| 1121 | ** If the doc-glob and doc-br settings are valid for document search |
| @@ -1197,21 +1169,90 @@ | |
| 1169 | ); |
| 1170 | db_multi_exec( |
| 1171 | "UPDATE ftsdocs SET idxed=1 WHERE type='d' AND NOT idxed" |
| 1172 | ); |
| 1173 | } |
| 1174 | |
| 1175 | /* |
| 1176 | ** Deal with all of the unindexed 'c' terms in FTSDOCS |
| 1177 | */ |
| 1178 | static void search_update_checkin_index(void){ |
| 1179 | db_multi_exec( |
| 1180 | "INSERT INTO ftsidx(docid,stext)" |
| 1181 | " SELECT rowid, stext('c',rid,NULL) FROM ftsdocs" |
| 1182 | " WHERE type='c' AND NOT idxed;" |
| 1183 | ); |
| 1184 | db_multi_exec( |
| 1185 | "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)" |
| 1186 | " SELECT ftsdocs.rowid, 1, 'c', ftsdocs.rid, NULL," |
| 1187 | " printf('Check-in [%%.16s] on %%s',blob.uuid,datetime(event.mtime))," |
| 1188 | " printf('/timeline?y=ci&n=9&c=%%.20s',blob.uuid)," |
| 1189 | " event.mtime" |
| 1190 | " FROM ftsdocs, event, blob" |
| 1191 | " WHERE ftsdocs.type='c' AND NOT ftsdocs.idxed" |
| 1192 | " AND event.objid=ftsdocs.rid" |
| 1193 | " AND blob.rid=ftsdocs.rid" |
| 1194 | ); |
| 1195 | } |
| 1196 | |
| 1197 | /* |
| 1198 | ** Deal with all of the unindexed 't' terms in FTSDOCS |
| 1199 | */ |
| 1200 | static void search_update_ticket_index(void){ |
| 1201 | db_multi_exec( |
| 1202 | "INSERT INTO ftsidx(docid,stext)" |
| 1203 | " SELECT rowid, stext('t',rid,NULL) FROM ftsdocs" |
| 1204 | " WHERE type='t' AND NOT idxed;" |
| 1205 | ); |
| 1206 | if( db_changes()==0 ) return; |
| 1207 | db_multi_exec( |
| 1208 | "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)" |
| 1209 | " SELECT ftsdocs.rowid, 1, 't', ftsdocs.rid, NULL," |
| 1210 | " printf('Ticket [%%.16s] on %%s',tkt_uuid,datetime(tkt_mtime))," |
| 1211 | " printf('/tktview/%%.20s',tkt_uuid)," |
| 1212 | " tkt_mtime" |
| 1213 | " FROM ftsdocs, ticket" |
| 1214 | " WHERE ftsdocs.type='t' AND NOT ftsdocs.idxed" |
| 1215 | " AND ticket.tkt_id=ftsdocs.rid" |
| 1216 | ); |
| 1217 | } |
| 1218 | |
| 1219 | /* |
| 1220 | ** Deal with all of the unindexed 'w' terms in FTSDOCS |
| 1221 | */ |
| 1222 | static void search_update_wiki_index(void){ |
| 1223 | db_multi_exec( |
| 1224 | "INSERT INTO ftsidx(docid,stext)" |
| 1225 | " SELECT rowid, stext('w',rid,NULL) FROM ftsdocs" |
| 1226 | " WHERE type='t' AND NOT idxed;" |
| 1227 | ); |
| 1228 | if( db_changes()==0 ) return; |
| 1229 | db_multi_exec( |
| 1230 | "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)" |
| 1231 | " SELECT ftsdocs.rowid, 1, 'w', ftsdocs.rid, ftsdocs.name," |
| 1232 | " 'Wiki: '||tsdocs.name)," |
| 1233 | " '/wiki?name='||urlencode(ftsdocs.name)," |
| 1234 | " tagxref.mtime" |
| 1235 | " FROM ftsdocs, tagxref" |
| 1236 | " WHERE ftsdocs.type='t' AND NOT ftsdocs.idxed" |
| 1237 | " AND tagxref.rid=ftsdocs.rid" |
| 1238 | ); |
| 1239 | } |
| 1240 | |
| 1241 | /* |
| 1242 | ** Deal with all of the unindexed entries in the FTSDOCS table - that |
| 1243 | ** is to say, all the entries with FTSDOCS.IDXED=0. Add them to the |
| 1244 | ** index. |
| 1245 | */ |
| 1246 | void search_update_index(void){ |
| 1247 | if( !search_index_exists() ) return; |
| 1248 | if( !db_exists("SELECT 1 FROM ftsdocs WHERE NOT idxed") ) return; |
| 1249 | search_sql_setup(g.db); |
| 1250 | search_update_doc_index(); |
| 1251 | search_update_checkin_index(); |
| 1252 | search_update_ticket_index(); |
| 1253 | search_update_wiki_index(); |
| 1254 | } |
| 1255 | |
| 1256 | /* |
| 1257 | ** COMMAND: test-fts |
| 1258 | */ |
| @@ -1221,13 +1262,12 @@ | |
| 1262 | static const struct { int iCmd; const char *z; } aCmd[] = { |
| 1263 | { 1, "create" }, |
| 1264 | { 2, "drop" }, |
| 1265 | { 3, "exists" }, |
| 1266 | { 4, "fill" }, |
| 1267 | { 8, "refill" }, |
| 1268 | { 5, "pending" }, |
| 1269 | { 7, "update" }, |
| 1270 | }; |
| 1271 | db_find_and_open_repository(0, 0); |
| 1272 | if( g.argc<3 ) usage("SUBCMD ..."); |
| 1273 | zSubCmd = g.argv[2]; |
| @@ -1267,46 +1307,18 @@ | |
| 1307 | break; |
| 1308 | } |
| 1309 | case 5: { assert( fossil_strncmp(zSubCmd, "pending", n)==0 ); |
| 1310 | Stmt q; |
| 1311 | if( !search_index_exists() ) break; |
| 1312 | db_prepare(&q, "SELECT rowid,type,rid,quote(name) FROM ftsdocs" |
| 1313 | " WHERE NOT idxed"); |
| 1314 | while( db_step(&q)==SQLITE_ROW ){ |
| 1315 | fossil_print("%6d: %s %6d %s\n", |
| 1316 | db_column_int(&q, 0), |
| 1317 | db_column_text(&q, 1), |
| 1318 | db_column_int(&q, 2), |
| 1319 | db_column_text(&q, 3) |
| 1320 | ); |
| 1321 | } |
| 1322 | db_finalize(&q); |
| 1323 | break; |
| 1324 | } |
| 1325 |