Fossil SCM
Begin work on searching technotes. Credit goes to Chris Rydalch for the idea. Sadly, this version doesn't work yet. Technote searches fail at this time.
Commit
94650be8de06dc37ecf8ea71948e350bf60157e9d48c989388fbb507493b0908
Parent
19e896543a1346e…
2 files changed
+93
-37
+3
-1
+93
-37
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -633,15 +633,16 @@ | ||
| 633 | 633 | db_finalize(&q); |
| 634 | 634 | } |
| 635 | 635 | |
| 636 | 636 | #if INTERFACE |
| 637 | 637 | /* What to search for */ |
| 638 | -#define SRCH_CKIN 0x0001 /* Search over check-in comments */ | |
| 639 | -#define SRCH_DOC 0x0002 /* Search over embedded documents */ | |
| 640 | -#define SRCH_TKT 0x0004 /* Search over tickets */ | |
| 641 | -#define SRCH_WIKI 0x0008 /* Search over wiki */ | |
| 642 | -#define SRCH_ALL 0x000f /* Search over everything */ | |
| 638 | +#define SRCH_CKIN 0x0001 /* Search over check-in comments */ | |
| 639 | +#define SRCH_DOC 0x0002 /* Search over embedded documents */ | |
| 640 | +#define SRCH_TKT 0x0004 /* Search over tickets */ | |
| 641 | +#define SRCH_WIKI 0x0008 /* Search over wiki */ | |
| 642 | +#define SRCH_TECHNOTE 0x0010 /* Search over tech notes */ | |
| 643 | +#define SRCH_ALL 0x001f /* Search over everything */ | |
| 643 | 644 | #endif |
| 644 | 645 | |
| 645 | 646 | /* |
| 646 | 647 | ** Remove bits from srchFlags which are disallowed by either the |
| 647 | 648 | ** current server configuration or by user permissions. |
| @@ -648,17 +649,18 @@ | ||
| 648 | 649 | */ |
| 649 | 650 | unsigned int search_restrict(unsigned int srchFlags){ |
| 650 | 651 | static unsigned int knownGood = 0; |
| 651 | 652 | static unsigned int knownBad = 0; |
| 652 | 653 | static const struct { unsigned m; const char *zKey; } aSetng[] = { |
| 653 | - { SRCH_CKIN, "search-ci" }, | |
| 654 | - { SRCH_DOC, "search-doc" }, | |
| 655 | - { SRCH_TKT, "search-tkt" }, | |
| 656 | - { SRCH_WIKI, "search-wiki" }, | |
| 654 | + { SRCH_CKIN, "search-ci" }, | |
| 655 | + { SRCH_DOC, "search-doc" }, | |
| 656 | + { SRCH_TKT, "search-tkt" }, | |
| 657 | + { SRCH_WIKI, "search-wiki" }, | |
| 658 | + { SRCH_TECHNOTE, "search-technote" }, | |
| 657 | 659 | }; |
| 658 | 660 | int i; |
| 659 | - if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC); | |
| 661 | + if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE); | |
| 660 | 662 | if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT); |
| 661 | 663 | if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI); |
| 662 | 664 | for(i=0; i<count(aSetng); i++){ |
| 663 | 665 | unsigned int m = aSetng[i].m; |
| 664 | 666 | if( (srchFlags & m)==0 ) continue; |
| @@ -770,10 +772,30 @@ | ||
| 770 | 772 | " datetime(tkt_mtime)," |
| 771 | 773 | " search_snippet()" |
| 772 | 774 | " FROM ticket" |
| 773 | 775 | " WHERE search_match(title('t',tkt_id,NULL),body('t',tkt_id,NULL));" |
| 774 | 776 | ); |
| 777 | + } | |
| 778 | + if( (srchFlags & SRCH_TECHNOTE)!=0 ){ | |
| 779 | + db_multi_exec( | |
| 780 | + "WITH technote(uuid,rid,mtime) AS (" | |
| 781 | + " SELECT substr(tagname,7), tagxref.rid, max(tagxref.mtime)" | |
| 782 | + " FROM tag, tagxref" | |
| 783 | + " WHERE tag.tagname GLOB 'event-*'" | |
| 784 | + " AND tagxref.tagid=tag.tagid" | |
| 785 | + " GROUP BY 1" | |
| 786 | + ")" | |
| 787 | + "INSERT INTO x(label,url,score,id,date,snip)" | |
| 788 | + " SELECT printf('Tech Note: %%s',uuid)," | |
| 789 | + " printf('/technote/%%s',uuid)," | |
| 790 | + " search_score()," | |
| 791 | + " 'e'||rid," | |
| 792 | + " datetime(mtime)," | |
| 793 | + " search_snippet()" | |
| 794 | + " FROM technote" | |
| 795 | + " WHERE search_match('',body('e',rid,NULL));" | |
| 796 | + ); | |
| 775 | 797 | } |
| 776 | 798 | } |
| 777 | 799 | |
| 778 | 800 | /* |
| 779 | 801 | ** Number of significant bits in a u32 |
| @@ -886,14 +908,15 @@ | ||
| 886 | 908 | zPattern |
| 887 | 909 | ); |
| 888 | 910 | if( srchFlags!=SRCH_ALL ){ |
| 889 | 911 | const char *zSep = " AND ("; |
| 890 | 912 | static const struct { unsigned m; char c; } aMask[] = { |
| 891 | - { SRCH_CKIN, 'c' }, | |
| 892 | - { SRCH_DOC, 'd' }, | |
| 893 | - { SRCH_TKT, 't' }, | |
| 894 | - { SRCH_WIKI, 'w' }, | |
| 913 | + { SRCH_CKIN, 'c' }, | |
| 914 | + { SRCH_DOC, 'd' }, | |
| 915 | + { SRCH_TKT, 't' }, | |
| 916 | + { SRCH_WIKI, 'w' }, | |
| 917 | + { SRCH_TECHNOTE, 'e' }, | |
| 895 | 918 | }; |
| 896 | 919 | int i; |
| 897 | 920 | for(i=0; i<count(aMask); i++){ |
| 898 | 921 | if( srchFlags & aMask[i].m ){ |
| 899 | 922 | blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c); |
| @@ -1037,14 +1060,15 @@ | ||
| 1037 | 1060 | const char *zDisable2; |
| 1038 | 1061 | const char *zPattern; |
| 1039 | 1062 | int fDebug = PB("debug"); |
| 1040 | 1063 | srchFlags = search_restrict(srchFlags); |
| 1041 | 1064 | switch( srchFlags ){ |
| 1042 | - case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break; | |
| 1043 | - case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break; | |
| 1044 | - case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break; | |
| 1045 | - case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break; | |
| 1065 | + case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break; | |
| 1066 | + case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break; | |
| 1067 | + case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break; | |
| 1068 | + case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break; | |
| 1069 | + case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break; | |
| 1046 | 1070 | } |
| 1047 | 1071 | if( srchFlags==0 ){ |
| 1048 | 1072 | zDisable1 = " disabled"; |
| 1049 | 1073 | zDisable2 = " disabled"; |
| 1050 | 1074 | zPattern = ""; |
| @@ -1060,15 +1084,16 @@ | ||
| 1060 | 1084 | @ <div class='searchForm'> |
| 1061 | 1085 | } |
| 1062 | 1086 | @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)> |
| 1063 | 1087 | if( useYparam && (srchFlags & (srchFlags-1))!=0 && useYparam ){ |
| 1064 | 1088 | static const struct { char *z; char *zNm; unsigned m; } aY[] = { |
| 1065 | - { "all", "All", SRCH_ALL }, | |
| 1066 | - { "c", "Check-ins", SRCH_CKIN }, | |
| 1067 | - { "d", "Docs", SRCH_DOC }, | |
| 1068 | - { "t", "Tickets", SRCH_TKT }, | |
| 1069 | - { "w", "Wiki", SRCH_WIKI }, | |
| 1089 | + { "all", "All", SRCH_ALL }, | |
| 1090 | + { "c", "Check-ins", SRCH_CKIN }, | |
| 1091 | + { "d", "Docs", SRCH_DOC }, | |
| 1092 | + { "t", "Tickets", SRCH_TKT }, | |
| 1093 | + { "w", "Wiki", SRCH_WIKI }, | |
| 1094 | + { "e", "Tech Notes", SRCH_TECHNOTE }, | |
| 1070 | 1095 | }; |
| 1071 | 1096 | const char *zY = PD("y","all"); |
| 1072 | 1097 | unsigned newFlags = srchFlags; |
| 1073 | 1098 | int i; |
| 1074 | 1099 | @ <select size='1' name='y'> |
| @@ -1116,10 +1141,11 @@ | ||
| 1116 | 1141 | ** y=TYPE What to search. |
| 1117 | 1142 | ** c -> check-ins |
| 1118 | 1143 | ** d -> documentation |
| 1119 | 1144 | ** t -> tickets |
| 1120 | 1145 | ** w -> wiki |
| 1146 | +** e -> tech notes | |
| 1121 | 1147 | ** all -> everything |
| 1122 | 1148 | */ |
| 1123 | 1149 | void search_page(void){ |
| 1124 | 1150 | login_check_credentials(); |
| 1125 | 1151 | style_header("Search"); |
| @@ -1222,10 +1248,11 @@ | ||
| 1222 | 1248 | ** |
| 1223 | 1249 | ** cType: d Embedded documentation |
| 1224 | 1250 | ** w Wiki page |
| 1225 | 1251 | ** c Check-in comment |
| 1226 | 1252 | ** t Ticket text |
| 1253 | +** e Tech note | |
| 1227 | 1254 | ** |
| 1228 | 1255 | ** rid The RID of an artifact that defines the object |
| 1229 | 1256 | ** being searched. |
| 1230 | 1257 | ** |
| 1231 | 1258 | ** zName Name of the object being searched. This is used |
| @@ -1247,12 +1274,14 @@ | ||
| 1247 | 1274 | blob_to_utf8_no_bom(&doc, 0); |
| 1248 | 1275 | get_stext_by_mimetype(&doc, mimetype_from_name(zName), pOut); |
| 1249 | 1276 | blob_reset(&doc); |
| 1250 | 1277 | break; |
| 1251 | 1278 | } |
| 1279 | + case 'e': /* Tech Notes */ | |
| 1252 | 1280 | case 'w': { /* Wiki */ |
| 1253 | - Manifest *pWiki = manifest_get(rid, CFTYPE_WIKI,0); | |
| 1281 | + Manifest *pWiki = manifest_get(rid, | |
| 1282 | + cType == 'e' ? CFTYPE_EVENT : CFTYPE_WIKI, 0); | |
| 1254 | 1283 | Blob wiki; |
| 1255 | 1284 | if( pWiki==0 ) break; |
| 1256 | 1285 | blob_init(&wiki, pWiki->zWiki, -1); |
| 1257 | 1286 | get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype), |
| 1258 | 1287 | pOut); |
| @@ -1367,11 +1396,11 @@ | ||
| 1367 | 1396 | ** COMMAND: test-search-stext |
| 1368 | 1397 | ** |
| 1369 | 1398 | ** Usage: fossil test-search-stext TYPE RID NAME |
| 1370 | 1399 | ** |
| 1371 | 1400 | ** Compute the search text for document TYPE-RID whose name is NAME. |
| 1372 | -** The TYPE is one of "c", "d", "t", or "w". The RID is the document | |
| 1401 | +** The TYPE is one of "c", "d", "t", "w", or "e". The RID is the document | |
| 1373 | 1402 | ** ID. The NAME is used to figure out a mimetype to use for formatting |
| 1374 | 1403 | ** the raw document text. |
| 1375 | 1404 | */ |
| 1376 | 1405 | void test_search_stext(void){ |
| 1377 | 1406 | Blob out; |
| @@ -1484,10 +1513,14 @@ | ||
| 1484 | 1513 | ); |
| 1485 | 1514 | db_multi_exec( |
| 1486 | 1515 | "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)" |
| 1487 | 1516 | " SELECT 't', tkt_id, 0 FROM ticket;" |
| 1488 | 1517 | ); |
| 1518 | + db_multi_exec( | |
| 1519 | + "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)" | |
| 1520 | + " SELECT 'e', objid, 0 FROM event WHERE type='e';" | |
| 1521 | + ); | |
| 1489 | 1522 | } |
| 1490 | 1523 | |
| 1491 | 1524 | /* |
| 1492 | 1525 | ** The document described by cType,rid,zName is about to be added or |
| 1493 | 1526 | ** updated. If the document has already been indexed, then unindex it |
| @@ -1508,19 +1541,19 @@ | ||
| 1508 | 1541 | db_multi_exec( |
| 1509 | 1542 | "REPLACE INTO ftsdocs(type,rid,name,idxed)" |
| 1510 | 1543 | " VALUES(%Q,%d,%Q,0)", |
| 1511 | 1544 | zType, rid, zName |
| 1512 | 1545 | ); |
| 1513 | - if( cType=='w' ){ | |
| 1546 | + if( cType=='w' || cType=='e' ){ | |
| 1514 | 1547 | db_multi_exec( |
| 1515 | 1548 | "DELETE FROM ftsidx WHERE docid IN" |
| 1516 | - " (SELECT rowid FROM ftsdocs WHERE type='w' AND name=%Q AND idxed)", | |
| 1517 | - zName | |
| 1549 | + " (SELECT rowid FROM ftsdocs WHERE type='%c' AND name=%Q AND idxed)", | |
| 1550 | + cType, zName | |
| 1518 | 1551 | ); |
| 1519 | 1552 | db_multi_exec( |
| 1520 | - "DELETE FROM ftsdocs WHERE type='w' AND name=%Q AND rid!=%d", | |
| 1521 | - zName, rid | |
| 1553 | + "DELETE FROM ftsdocs WHERE type='%c' AND name=%Q AND rid!=%d", | |
| 1554 | + cType, zName, rid | |
| 1522 | 1555 | ); |
| 1523 | 1556 | } |
| 1524 | 1557 | } |
| 1525 | 1558 | } |
| 1526 | 1559 | |
| @@ -1647,10 +1680,32 @@ | ||
| 1647 | 1680 | " tagxref.mtime" |
| 1648 | 1681 | " FROM tagxref WHERE tagxref.rid=ftsdocs.rid)" |
| 1649 | 1682 | " WHERE ftsdocs.type='w' AND NOT ftsdocs.idxed" |
| 1650 | 1683 | ); |
| 1651 | 1684 | } |
| 1685 | + | |
| 1686 | +/* | |
| 1687 | +** Deal with all of the unindexed 'e' terms in FTSDOCS | |
| 1688 | +*/ | |
| 1689 | +static void search_update_technote_index(void){ | |
| 1690 | + db_multi_exec( | |
| 1691 | + "INSERT INTO ftsidx(docid,title,body)" | |
| 1692 | + " SELECT rowid, title('e',rid,NULL),body('e',rid,NULL) FROM ftsdocs" | |
| 1693 | + " WHERE type='e' AND NOT idxed;" | |
| 1694 | + ); | |
| 1695 | + if( db_changes()==0 ) return; | |
| 1696 | + db_multi_exec( | |
| 1697 | + "UPDATE ftsdocs SET idxed=1," | |
| 1698 | + " (name,label,url,mtime) = " | |
| 1699 | + " (SELECT ftsdocs.name," | |
| 1700 | + " 'Tech Note: '||ftsdocs.name," | |
| 1701 | + " '/technote/'||urlencode(ftsdocs.name)," | |
| 1702 | + " tagxref.mtime" | |
| 1703 | + " FROM tagxref WHERE tagxref.rid=ftsdocs.rid)" | |
| 1704 | + " WHERE ftsdocs.type='e' AND NOT ftsdocs.idxed" | |
| 1705 | + ); | |
| 1706 | +} | |
| 1652 | 1707 | |
| 1653 | 1708 | /* |
| 1654 | 1709 | ** Deal with all of the unindexed entries in the FTSDOCS table - that |
| 1655 | 1710 | ** is to say, all the entries with FTSDOCS.IDXED=0. Add them to the |
| 1656 | 1711 | ** index. |
| @@ -1694,14 +1749,14 @@ | ||
| 1694 | 1749 | ** reindex Rebuild the search index. This is a no-op if |
| 1695 | 1750 | ** index search is disabled |
| 1696 | 1751 | ** |
| 1697 | 1752 | ** index (on|off) Turn the search index on or off |
| 1698 | 1753 | ** |
| 1699 | -** enable cdtw Enable various kinds of search. c=Check-ins, | |
| 1700 | -** d=Documents, t=Tickets, w=Wiki. | |
| 1754 | +** enable cdtwe Enable various kinds of search. c=Check-ins, | |
| 1755 | +** d=Documents, t=Tickets, w=Wiki, e=Tech Notes. | |
| 1701 | 1756 | ** |
| 1702 | -** disable cdtw Disable various kinds of search | |
| 1757 | +** disable cdtwe Disable various kinds of search | |
| 1703 | 1758 | ** |
| 1704 | 1759 | ** stemmer (on|off) Turn the Porter stemmer on or off for indexed |
| 1705 | 1760 | ** search. (Unindexed search is never stemmed.) |
| 1706 | 1761 | ** |
| 1707 | 1762 | ** The current search settings are displayed after any changes are applied. |
| @@ -1714,14 +1769,15 @@ | ||
| 1714 | 1769 | { 3, "disable" }, |
| 1715 | 1770 | { 4, "enable" }, |
| 1716 | 1771 | { 5, "stemmer" }, |
| 1717 | 1772 | }; |
| 1718 | 1773 | static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = { |
| 1719 | - { "search-ckin", "check-in search:", "c" }, | |
| 1720 | - { "search-doc", "document search:", "d" }, | |
| 1721 | - { "search-tkt", "ticket search:", "t" }, | |
| 1722 | - { "search-wiki", "wiki search:", "w" }, | |
| 1774 | + { "search-ckin", "check-in search:", "c" }, | |
| 1775 | + { "search-doc", "document search:", "d" }, | |
| 1776 | + { "search-tkt", "ticket search:", "t" }, | |
| 1777 | + { "search-wiki", "wiki search:", "w" }, | |
| 1778 | + { "search-technote", "tech note search:", "e" }, | |
| 1723 | 1779 | }; |
| 1724 | 1780 | char *zSubCmd = 0; |
| 1725 | 1781 | int i, j, n; |
| 1726 | 1782 | int iCmd = 0; |
| 1727 | 1783 | int iAction = 0; |
| 1728 | 1784 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -633,15 +633,16 @@ | |
| 633 | db_finalize(&q); |
| 634 | } |
| 635 | |
| 636 | #if INTERFACE |
| 637 | /* What to search for */ |
| 638 | #define SRCH_CKIN 0x0001 /* Search over check-in comments */ |
| 639 | #define SRCH_DOC 0x0002 /* Search over embedded documents */ |
| 640 | #define SRCH_TKT 0x0004 /* Search over tickets */ |
| 641 | #define SRCH_WIKI 0x0008 /* Search over wiki */ |
| 642 | #define SRCH_ALL 0x000f /* Search over everything */ |
| 643 | #endif |
| 644 | |
| 645 | /* |
| 646 | ** Remove bits from srchFlags which are disallowed by either the |
| 647 | ** current server configuration or by user permissions. |
| @@ -648,17 +649,18 @@ | |
| 648 | */ |
| 649 | unsigned int search_restrict(unsigned int srchFlags){ |
| 650 | static unsigned int knownGood = 0; |
| 651 | static unsigned int knownBad = 0; |
| 652 | static const struct { unsigned m; const char *zKey; } aSetng[] = { |
| 653 | { SRCH_CKIN, "search-ci" }, |
| 654 | { SRCH_DOC, "search-doc" }, |
| 655 | { SRCH_TKT, "search-tkt" }, |
| 656 | { SRCH_WIKI, "search-wiki" }, |
| 657 | }; |
| 658 | int i; |
| 659 | if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC); |
| 660 | if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT); |
| 661 | if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI); |
| 662 | for(i=0; i<count(aSetng); i++){ |
| 663 | unsigned int m = aSetng[i].m; |
| 664 | if( (srchFlags & m)==0 ) continue; |
| @@ -770,10 +772,30 @@ | |
| 770 | " datetime(tkt_mtime)," |
| 771 | " search_snippet()" |
| 772 | " FROM ticket" |
| 773 | " WHERE search_match(title('t',tkt_id,NULL),body('t',tkt_id,NULL));" |
| 774 | ); |
| 775 | } |
| 776 | } |
| 777 | |
| 778 | /* |
| 779 | ** Number of significant bits in a u32 |
| @@ -886,14 +908,15 @@ | |
| 886 | zPattern |
| 887 | ); |
| 888 | if( srchFlags!=SRCH_ALL ){ |
| 889 | const char *zSep = " AND ("; |
| 890 | static const struct { unsigned m; char c; } aMask[] = { |
| 891 | { SRCH_CKIN, 'c' }, |
| 892 | { SRCH_DOC, 'd' }, |
| 893 | { SRCH_TKT, 't' }, |
| 894 | { SRCH_WIKI, 'w' }, |
| 895 | }; |
| 896 | int i; |
| 897 | for(i=0; i<count(aMask); i++){ |
| 898 | if( srchFlags & aMask[i].m ){ |
| 899 | blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c); |
| @@ -1037,14 +1060,15 @@ | |
| 1037 | const char *zDisable2; |
| 1038 | const char *zPattern; |
| 1039 | int fDebug = PB("debug"); |
| 1040 | srchFlags = search_restrict(srchFlags); |
| 1041 | switch( srchFlags ){ |
| 1042 | case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break; |
| 1043 | case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break; |
| 1044 | case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break; |
| 1045 | case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break; |
| 1046 | } |
| 1047 | if( srchFlags==0 ){ |
| 1048 | zDisable1 = " disabled"; |
| 1049 | zDisable2 = " disabled"; |
| 1050 | zPattern = ""; |
| @@ -1060,15 +1084,16 @@ | |
| 1060 | @ <div class='searchForm'> |
| 1061 | } |
| 1062 | @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)> |
| 1063 | if( useYparam && (srchFlags & (srchFlags-1))!=0 && useYparam ){ |
| 1064 | static const struct { char *z; char *zNm; unsigned m; } aY[] = { |
| 1065 | { "all", "All", SRCH_ALL }, |
| 1066 | { "c", "Check-ins", SRCH_CKIN }, |
| 1067 | { "d", "Docs", SRCH_DOC }, |
| 1068 | { "t", "Tickets", SRCH_TKT }, |
| 1069 | { "w", "Wiki", SRCH_WIKI }, |
| 1070 | }; |
| 1071 | const char *zY = PD("y","all"); |
| 1072 | unsigned newFlags = srchFlags; |
| 1073 | int i; |
| 1074 | @ <select size='1' name='y'> |
| @@ -1116,10 +1141,11 @@ | |
| 1116 | ** y=TYPE What to search. |
| 1117 | ** c -> check-ins |
| 1118 | ** d -> documentation |
| 1119 | ** t -> tickets |
| 1120 | ** w -> wiki |
| 1121 | ** all -> everything |
| 1122 | */ |
| 1123 | void search_page(void){ |
| 1124 | login_check_credentials(); |
| 1125 | style_header("Search"); |
| @@ -1222,10 +1248,11 @@ | |
| 1222 | ** |
| 1223 | ** cType: d Embedded documentation |
| 1224 | ** w Wiki page |
| 1225 | ** c Check-in comment |
| 1226 | ** t Ticket text |
| 1227 | ** |
| 1228 | ** rid The RID of an artifact that defines the object |
| 1229 | ** being searched. |
| 1230 | ** |
| 1231 | ** zName Name of the object being searched. This is used |
| @@ -1247,12 +1274,14 @@ | |
| 1247 | blob_to_utf8_no_bom(&doc, 0); |
| 1248 | get_stext_by_mimetype(&doc, mimetype_from_name(zName), pOut); |
| 1249 | blob_reset(&doc); |
| 1250 | break; |
| 1251 | } |
| 1252 | case 'w': { /* Wiki */ |
| 1253 | Manifest *pWiki = manifest_get(rid, CFTYPE_WIKI,0); |
| 1254 | Blob wiki; |
| 1255 | if( pWiki==0 ) break; |
| 1256 | blob_init(&wiki, pWiki->zWiki, -1); |
| 1257 | get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype), |
| 1258 | pOut); |
| @@ -1367,11 +1396,11 @@ | |
| 1367 | ** COMMAND: test-search-stext |
| 1368 | ** |
| 1369 | ** Usage: fossil test-search-stext TYPE RID NAME |
| 1370 | ** |
| 1371 | ** Compute the search text for document TYPE-RID whose name is NAME. |
| 1372 | ** The TYPE is one of "c", "d", "t", or "w". The RID is the document |
| 1373 | ** ID. The NAME is used to figure out a mimetype to use for formatting |
| 1374 | ** the raw document text. |
| 1375 | */ |
| 1376 | void test_search_stext(void){ |
| 1377 | Blob out; |
| @@ -1484,10 +1513,14 @@ | |
| 1484 | ); |
| 1485 | db_multi_exec( |
| 1486 | "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)" |
| 1487 | " SELECT 't', tkt_id, 0 FROM ticket;" |
| 1488 | ); |
| 1489 | } |
| 1490 | |
| 1491 | /* |
| 1492 | ** The document described by cType,rid,zName is about to be added or |
| 1493 | ** updated. If the document has already been indexed, then unindex it |
| @@ -1508,19 +1541,19 @@ | |
| 1508 | db_multi_exec( |
| 1509 | "REPLACE INTO ftsdocs(type,rid,name,idxed)" |
| 1510 | " VALUES(%Q,%d,%Q,0)", |
| 1511 | zType, rid, zName |
| 1512 | ); |
| 1513 | if( cType=='w' ){ |
| 1514 | db_multi_exec( |
| 1515 | "DELETE FROM ftsidx WHERE docid IN" |
| 1516 | " (SELECT rowid FROM ftsdocs WHERE type='w' AND name=%Q AND idxed)", |
| 1517 | zName |
| 1518 | ); |
| 1519 | db_multi_exec( |
| 1520 | "DELETE FROM ftsdocs WHERE type='w' AND name=%Q AND rid!=%d", |
| 1521 | zName, rid |
| 1522 | ); |
| 1523 | } |
| 1524 | } |
| 1525 | } |
| 1526 | |
| @@ -1647,10 +1680,32 @@ | |
| 1647 | " tagxref.mtime" |
| 1648 | " FROM tagxref WHERE tagxref.rid=ftsdocs.rid)" |
| 1649 | " WHERE ftsdocs.type='w' AND NOT ftsdocs.idxed" |
| 1650 | ); |
| 1651 | } |
| 1652 | |
| 1653 | /* |
| 1654 | ** Deal with all of the unindexed entries in the FTSDOCS table - that |
| 1655 | ** is to say, all the entries with FTSDOCS.IDXED=0. Add them to the |
| 1656 | ** index. |
| @@ -1694,14 +1749,14 @@ | |
| 1694 | ** reindex Rebuild the search index. This is a no-op if |
| 1695 | ** index search is disabled |
| 1696 | ** |
| 1697 | ** index (on|off) Turn the search index on or off |
| 1698 | ** |
| 1699 | ** enable cdtw Enable various kinds of search. c=Check-ins, |
| 1700 | ** d=Documents, t=Tickets, w=Wiki. |
| 1701 | ** |
| 1702 | ** disable cdtw Disable various kinds of search |
| 1703 | ** |
| 1704 | ** stemmer (on|off) Turn the Porter stemmer on or off for indexed |
| 1705 | ** search. (Unindexed search is never stemmed.) |
| 1706 | ** |
| 1707 | ** The current search settings are displayed after any changes are applied. |
| @@ -1714,14 +1769,15 @@ | |
| 1714 | { 3, "disable" }, |
| 1715 | { 4, "enable" }, |
| 1716 | { 5, "stemmer" }, |
| 1717 | }; |
| 1718 | static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = { |
| 1719 | { "search-ckin", "check-in search:", "c" }, |
| 1720 | { "search-doc", "document search:", "d" }, |
| 1721 | { "search-tkt", "ticket search:", "t" }, |
| 1722 | { "search-wiki", "wiki search:", "w" }, |
| 1723 | }; |
| 1724 | char *zSubCmd = 0; |
| 1725 | int i, j, n; |
| 1726 | int iCmd = 0; |
| 1727 | int iAction = 0; |
| 1728 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -633,15 +633,16 @@ | |
| 633 | db_finalize(&q); |
| 634 | } |
| 635 | |
| 636 | #if INTERFACE |
| 637 | /* What to search for */ |
| 638 | #define SRCH_CKIN 0x0001 /* Search over check-in comments */ |
| 639 | #define SRCH_DOC 0x0002 /* Search over embedded documents */ |
| 640 | #define SRCH_TKT 0x0004 /* Search over tickets */ |
| 641 | #define SRCH_WIKI 0x0008 /* Search over wiki */ |
| 642 | #define SRCH_TECHNOTE 0x0010 /* Search over tech notes */ |
| 643 | #define SRCH_ALL 0x001f /* Search over everything */ |
| 644 | #endif |
| 645 | |
| 646 | /* |
| 647 | ** Remove bits from srchFlags which are disallowed by either the |
| 648 | ** current server configuration or by user permissions. |
| @@ -648,17 +649,18 @@ | |
| 649 | */ |
| 650 | unsigned int search_restrict(unsigned int srchFlags){ |
| 651 | static unsigned int knownGood = 0; |
| 652 | static unsigned int knownBad = 0; |
| 653 | static const struct { unsigned m; const char *zKey; } aSetng[] = { |
| 654 | { SRCH_CKIN, "search-ci" }, |
| 655 | { SRCH_DOC, "search-doc" }, |
| 656 | { SRCH_TKT, "search-tkt" }, |
| 657 | { SRCH_WIKI, "search-wiki" }, |
| 658 | { SRCH_TECHNOTE, "search-technote" }, |
| 659 | }; |
| 660 | int i; |
| 661 | if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE); |
| 662 | if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT); |
| 663 | if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI); |
| 664 | for(i=0; i<count(aSetng); i++){ |
| 665 | unsigned int m = aSetng[i].m; |
| 666 | if( (srchFlags & m)==0 ) continue; |
| @@ -770,10 +772,30 @@ | |
| 772 | " datetime(tkt_mtime)," |
| 773 | " search_snippet()" |
| 774 | " FROM ticket" |
| 775 | " WHERE search_match(title('t',tkt_id,NULL),body('t',tkt_id,NULL));" |
| 776 | ); |
| 777 | } |
| 778 | if( (srchFlags & SRCH_TECHNOTE)!=0 ){ |
| 779 | db_multi_exec( |
| 780 | "WITH technote(uuid,rid,mtime) AS (" |
| 781 | " SELECT substr(tagname,7), tagxref.rid, max(tagxref.mtime)" |
| 782 | " FROM tag, tagxref" |
| 783 | " WHERE tag.tagname GLOB 'event-*'" |
| 784 | " AND tagxref.tagid=tag.tagid" |
| 785 | " GROUP BY 1" |
| 786 | ")" |
| 787 | "INSERT INTO x(label,url,score,id,date,snip)" |
| 788 | " SELECT printf('Tech Note: %%s',uuid)," |
| 789 | " printf('/technote/%%s',uuid)," |
| 790 | " search_score()," |
| 791 | " 'e'||rid," |
| 792 | " datetime(mtime)," |
| 793 | " search_snippet()" |
| 794 | " FROM technote" |
| 795 | " WHERE search_match('',body('e',rid,NULL));" |
| 796 | ); |
| 797 | } |
| 798 | } |
| 799 | |
| 800 | /* |
| 801 | ** Number of significant bits in a u32 |
| @@ -886,14 +908,15 @@ | |
| 908 | zPattern |
| 909 | ); |
| 910 | if( srchFlags!=SRCH_ALL ){ |
| 911 | const char *zSep = " AND ("; |
| 912 | static const struct { unsigned m; char c; } aMask[] = { |
| 913 | { SRCH_CKIN, 'c' }, |
| 914 | { SRCH_DOC, 'd' }, |
| 915 | { SRCH_TKT, 't' }, |
| 916 | { SRCH_WIKI, 'w' }, |
| 917 | { SRCH_TECHNOTE, 'e' }, |
| 918 | }; |
| 919 | int i; |
| 920 | for(i=0; i<count(aMask); i++){ |
| 921 | if( srchFlags & aMask[i].m ){ |
| 922 | blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c); |
| @@ -1037,14 +1060,15 @@ | |
| 1060 | const char *zDisable2; |
| 1061 | const char *zPattern; |
| 1062 | int fDebug = PB("debug"); |
| 1063 | srchFlags = search_restrict(srchFlags); |
| 1064 | switch( srchFlags ){ |
| 1065 | case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break; |
| 1066 | case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break; |
| 1067 | case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break; |
| 1068 | case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break; |
| 1069 | case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break; |
| 1070 | } |
| 1071 | if( srchFlags==0 ){ |
| 1072 | zDisable1 = " disabled"; |
| 1073 | zDisable2 = " disabled"; |
| 1074 | zPattern = ""; |
| @@ -1060,15 +1084,16 @@ | |
| 1084 | @ <div class='searchForm'> |
| 1085 | } |
| 1086 | @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)> |
| 1087 | if( useYparam && (srchFlags & (srchFlags-1))!=0 && useYparam ){ |
| 1088 | static const struct { char *z; char *zNm; unsigned m; } aY[] = { |
| 1089 | { "all", "All", SRCH_ALL }, |
| 1090 | { "c", "Check-ins", SRCH_CKIN }, |
| 1091 | { "d", "Docs", SRCH_DOC }, |
| 1092 | { "t", "Tickets", SRCH_TKT }, |
| 1093 | { "w", "Wiki", SRCH_WIKI }, |
| 1094 | { "e", "Tech Notes", SRCH_TECHNOTE }, |
| 1095 | }; |
| 1096 | const char *zY = PD("y","all"); |
| 1097 | unsigned newFlags = srchFlags; |
| 1098 | int i; |
| 1099 | @ <select size='1' name='y'> |
| @@ -1116,10 +1141,11 @@ | |
| 1141 | ** y=TYPE What to search. |
| 1142 | ** c -> check-ins |
| 1143 | ** d -> documentation |
| 1144 | ** t -> tickets |
| 1145 | ** w -> wiki |
| 1146 | ** e -> tech notes |
| 1147 | ** all -> everything |
| 1148 | */ |
| 1149 | void search_page(void){ |
| 1150 | login_check_credentials(); |
| 1151 | style_header("Search"); |
| @@ -1222,10 +1248,11 @@ | |
| 1248 | ** |
| 1249 | ** cType: d Embedded documentation |
| 1250 | ** w Wiki page |
| 1251 | ** c Check-in comment |
| 1252 | ** t Ticket text |
| 1253 | ** e Tech note |
| 1254 | ** |
| 1255 | ** rid The RID of an artifact that defines the object |
| 1256 | ** being searched. |
| 1257 | ** |
| 1258 | ** zName Name of the object being searched. This is used |
| @@ -1247,12 +1274,14 @@ | |
| 1274 | blob_to_utf8_no_bom(&doc, 0); |
| 1275 | get_stext_by_mimetype(&doc, mimetype_from_name(zName), pOut); |
| 1276 | blob_reset(&doc); |
| 1277 | break; |
| 1278 | } |
| 1279 | case 'e': /* Tech Notes */ |
| 1280 | case 'w': { /* Wiki */ |
| 1281 | Manifest *pWiki = manifest_get(rid, |
| 1282 | cType == 'e' ? CFTYPE_EVENT : CFTYPE_WIKI, 0); |
| 1283 | Blob wiki; |
| 1284 | if( pWiki==0 ) break; |
| 1285 | blob_init(&wiki, pWiki->zWiki, -1); |
| 1286 | get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype), |
| 1287 | pOut); |
| @@ -1367,11 +1396,11 @@ | |
| 1396 | ** COMMAND: test-search-stext |
| 1397 | ** |
| 1398 | ** Usage: fossil test-search-stext TYPE RID NAME |
| 1399 | ** |
| 1400 | ** Compute the search text for document TYPE-RID whose name is NAME. |
| 1401 | ** The TYPE is one of "c", "d", "t", "w", or "e". The RID is the document |
| 1402 | ** ID. The NAME is used to figure out a mimetype to use for formatting |
| 1403 | ** the raw document text. |
| 1404 | */ |
| 1405 | void test_search_stext(void){ |
| 1406 | Blob out; |
| @@ -1484,10 +1513,14 @@ | |
| 1513 | ); |
| 1514 | db_multi_exec( |
| 1515 | "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)" |
| 1516 | " SELECT 't', tkt_id, 0 FROM ticket;" |
| 1517 | ); |
| 1518 | db_multi_exec( |
| 1519 | "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)" |
| 1520 | " SELECT 'e', objid, 0 FROM event WHERE type='e';" |
| 1521 | ); |
| 1522 | } |
| 1523 | |
| 1524 | /* |
| 1525 | ** The document described by cType,rid,zName is about to be added or |
| 1526 | ** updated. If the document has already been indexed, then unindex it |
| @@ -1508,19 +1541,19 @@ | |
| 1541 | db_multi_exec( |
| 1542 | "REPLACE INTO ftsdocs(type,rid,name,idxed)" |
| 1543 | " VALUES(%Q,%d,%Q,0)", |
| 1544 | zType, rid, zName |
| 1545 | ); |
| 1546 | if( cType=='w' || cType=='e' ){ |
| 1547 | db_multi_exec( |
| 1548 | "DELETE FROM ftsidx WHERE docid IN" |
| 1549 | " (SELECT rowid FROM ftsdocs WHERE type='%c' AND name=%Q AND idxed)", |
| 1550 | cType, zName |
| 1551 | ); |
| 1552 | db_multi_exec( |
| 1553 | "DELETE FROM ftsdocs WHERE type='%c' AND name=%Q AND rid!=%d", |
| 1554 | cType, zName, rid |
| 1555 | ); |
| 1556 | } |
| 1557 | } |
| 1558 | } |
| 1559 | |
| @@ -1647,10 +1680,32 @@ | |
| 1680 | " tagxref.mtime" |
| 1681 | " FROM tagxref WHERE tagxref.rid=ftsdocs.rid)" |
| 1682 | " WHERE ftsdocs.type='w' AND NOT ftsdocs.idxed" |
| 1683 | ); |
| 1684 | } |
| 1685 | |
| 1686 | /* |
| 1687 | ** Deal with all of the unindexed 'e' terms in FTSDOCS |
| 1688 | */ |
| 1689 | static void search_update_technote_index(void){ |
| 1690 | db_multi_exec( |
| 1691 | "INSERT INTO ftsidx(docid,title,body)" |
| 1692 | " SELECT rowid, title('e',rid,NULL),body('e',rid,NULL) FROM ftsdocs" |
| 1693 | " WHERE type='e' AND NOT idxed;" |
| 1694 | ); |
| 1695 | if( db_changes()==0 ) return; |
| 1696 | db_multi_exec( |
| 1697 | "UPDATE ftsdocs SET idxed=1," |
| 1698 | " (name,label,url,mtime) = " |
| 1699 | " (SELECT ftsdocs.name," |
| 1700 | " 'Tech Note: '||ftsdocs.name," |
| 1701 | " '/technote/'||urlencode(ftsdocs.name)," |
| 1702 | " tagxref.mtime" |
| 1703 | " FROM tagxref WHERE tagxref.rid=ftsdocs.rid)" |
| 1704 | " WHERE ftsdocs.type='e' AND NOT ftsdocs.idxed" |
| 1705 | ); |
| 1706 | } |
| 1707 | |
| 1708 | /* |
| 1709 | ** Deal with all of the unindexed entries in the FTSDOCS table - that |
| 1710 | ** is to say, all the entries with FTSDOCS.IDXED=0. Add them to the |
| 1711 | ** index. |
| @@ -1694,14 +1749,14 @@ | |
| 1749 | ** reindex Rebuild the search index. This is a no-op if |
| 1750 | ** index search is disabled |
| 1751 | ** |
| 1752 | ** index (on|off) Turn the search index on or off |
| 1753 | ** |
| 1754 | ** enable cdtwe Enable various kinds of search. c=Check-ins, |
| 1755 | ** d=Documents, t=Tickets, w=Wiki, e=Tech Notes. |
| 1756 | ** |
| 1757 | ** disable cdtwe Disable various kinds of search |
| 1758 | ** |
| 1759 | ** stemmer (on|off) Turn the Porter stemmer on or off for indexed |
| 1760 | ** search. (Unindexed search is never stemmed.) |
| 1761 | ** |
| 1762 | ** The current search settings are displayed after any changes are applied. |
| @@ -1714,14 +1769,15 @@ | |
| 1769 | { 3, "disable" }, |
| 1770 | { 4, "enable" }, |
| 1771 | { 5, "stemmer" }, |
| 1772 | }; |
| 1773 | static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = { |
| 1774 | { "search-ckin", "check-in search:", "c" }, |
| 1775 | { "search-doc", "document search:", "d" }, |
| 1776 | { "search-tkt", "ticket search:", "t" }, |
| 1777 | { "search-wiki", "wiki search:", "w" }, |
| 1778 | { "search-technote", "tech note search:", "e" }, |
| 1779 | }; |
| 1780 | char *zSubCmd = 0; |
| 1781 | int i, j, n; |
| 1782 | int iCmd = 0; |
| 1783 | int iAction = 0; |
| 1784 |
+3
-1
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -2281,11 +2281,13 @@ | ||
| 2281 | 2281 | @ <br /> |
| 2282 | 2282 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2283 | 2283 | @ <br /> |
| 2284 | 2284 | onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); |
| 2285 | 2285 | @ <br /> |
| 2286 | - onoff_attribute("Search Wiki","search-wiki", "sw", 0, 0); | |
| 2286 | + onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); | |
| 2287 | + @ <br /> | |
| 2288 | + onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); | |
| 2287 | 2289 | @ <hr /> |
| 2288 | 2290 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 2289 | 2291 | @ <hr /> |
| 2290 | 2292 | if( P("fts0") ){ |
| 2291 | 2293 | search_drop_index(); |
| 2292 | 2294 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -2281,11 +2281,13 @@ | |
| 2281 | @ <br /> |
| 2282 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2283 | @ <br /> |
| 2284 | onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); |
| 2285 | @ <br /> |
| 2286 | onoff_attribute("Search Wiki","search-wiki", "sw", 0, 0); |
| 2287 | @ <hr /> |
| 2288 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 2289 | @ <hr /> |
| 2290 | if( P("fts0") ){ |
| 2291 | search_drop_index(); |
| 2292 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -2281,11 +2281,13 @@ | |
| 2281 | @ <br /> |
| 2282 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2283 | @ <br /> |
| 2284 | onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); |
| 2285 | @ <br /> |
| 2286 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2287 | @ <br /> |
| 2288 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2289 | @ <hr /> |
| 2290 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 2291 | @ <hr /> |
| 2292 | if( P("fts0") ){ |
| 2293 | search_drop_index(); |
| 2294 |