boolsrch.c 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595
  1. /*
  2. * CDE - Common Desktop Environment
  3. *
  4. * Copyright (c) 1993-2012, The Open Group. All rights reserved.
  5. *
  6. * These libraries and programs are free software; you can
  7. * redistribute them and/or modify them under the terms of the GNU
  8. * Lesser General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option)
  10. * any later version.
  11. *
  12. * These libraries and programs are distributed in the hope that
  13. * they will be useful, but WITHOUT ANY WARRANTY; without even the
  14. * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15. * PURPOSE. See the GNU Lesser General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with these libraries and programs; if not, write
  20. * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  21. * Floor, Boston, MA 02110-1301 USA
  22. */
  23. /* $XConsortium: boolsrch.c /main/4 1996/09/23 21:00:18 cde-ibm $
  24. *
  25. * (c) Copyright 1996 Digital Equipment Corporation.
  26. * (c) Copyright 1996 Hewlett-Packard Company.
  27. * (c) Copyright 1996 International Business Machines Corp.
  28. * (c) Copyright 1996 Sun Microsystems, Inc.
  29. * (c) Copyright 1996 Novell, Inc.
  30. * (c) Copyright 1996 FUJITSU LIMITED.
  31. * (c) Copyright 1996 Hitachi.
  32. */
  33. /*
  34. * COMPONENT_NAME: austext
  35. *
  36. * FUNCTIONS: boolean_search
  37. * calc_result_bitvec_WK
  38. * calculate_idfs
  39. * dbread_filter_WK
  40. * get_proximity
  41. * got_USR_STOPSRCH
  42. * load_DtSrResults_WK
  43. * load_or_wordrecs
  44. * read_d99
  45. * read_recno
  46. * read_stem_bitvec_WK
  47. * stuff_DtSrResult
  48. * weights_filter_WK
  49. *
  50. * ORIGINS: 27
  51. *
  52. *
  53. * (C) COPYRIGHT International Business Machines Corp. 1996
  54. * All Rights Reserved
  55. * Licensed Materials - Property of IBM
  56. * US Government Users Restricted Rights - Use, duplication or
  57. * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  58. */
  59. /********************* BOOLSRCH.C **********************
  60. * $Id: boolsrch.c /main/4 1996/09/23 21:00:18 cde-ibm $
  61. * February 1996.
  62. * The vista code from the original vewords.c.
  63. * Given a final truth table and stems array from the user's boolean
  64. * query (output of boolean_search()), find all database records
  65. * containing the truth table's set operations and return
  66. * their database addresses in a resultlist.
  67. * See boolpars.h for format and limitations of TRUTHTAB.
  68. *
  69. *-------------- D99DBA TO DBA CONVERSION ----------------
  70. * 'd99dbas' are not real vista dbas! They were modified
  71. * as follows to permit shorter bit vectors,
  72. * and to minimize bit shifts at search time.
  73. * vista_dba <- (OR_D00 << 24) | vista_slot
  74. * vista_slot <- ((d99recno - 1) * or_recslots) + 2
  75. * d99dba <- (d99recno << 8) | weight_byte
  76. * d99recno <- ((vista_slot - 2) / or_recslots) + 1
  77. * The d99 and bitvec recno of the first rec is 1.
  78. * The slotno (vista dba) of the first rec is 2
  79. * (dbrec occupies first slot and vista slots begin at 1).
  80. *
  81. * $Log$
  82. * Revision 1.5 1996/03/20 19:21:49 miker
  83. * Completed collocations code. Restored get_colloc_bitvec() from colloc.c.
  84. *
  85. * Revision 1.4 1996/03/18 22:06:24 miker
  86. * Bug fix. Zero permute NOT queries always returned no hits.
  87. *
  88. * Revision 1.3 1996/03/13 23:05:24 miker
  89. * Change long double constant to regular float for better portability.
  90. *
  91. * Revision 1.2 1996/03/13 22:36:37 miker
  92. * Changed char to UCHAR several places; similar typecasts.
  93. * Moved collocations processing to colloc.c.
  94. *
  95. * Revision 1.1 1996/03/05 15:52:06 miker
  96. * Initial revision
  97. */
  98. /***#define _ALL_SOURCE****/ /* to pickup typedefs for shm vnodes */
  99. #include "SearchE.h"
  100. #include <string.h>
  101. #include <stdio.h>
  102. #include <stdlib.h>
  103. #include <math.h>
  104. #include "vista.h"
  105. #include "boolpars.h"
  106. #define PROGNAME "BOOLSRCH"
  107. #define INIT_ITERATIONS 50
  108. #define MS_boolsrch 16
  109. /*
  110. * DBAS_PER_BLOCK is the max number of dbas to be read
  111. * from d99 file. Note DBAS_PER_BLOCK * sizeof(DB_ADDR) = 512 bytes,
  112. * the standard blksize of one hard disk block.
  113. */
  114. #define DBAS_PER_BLOCK 128
  115. #define RESET_BIT(bv, by, bm) bv[by] &= (UCHAR) ~bm
  116. #if (DtSrMAX_STEMCOUNT != 8)
  117. #error DtSrMAX_STEMCOUNT does not equal 8.
  118. #endif
  119. /****************************************/
  120. /* */
  121. /* PROXWT */
  122. /* */
  123. /****************************************/
  124. typedef struct {
  125. float wt;
  126. long byteno;
  127. int bitmask;
  128. int proximity;
  129. } PROXWT;
  130. /****************************************/
  131. /* */
  132. /* GLOBALS */
  133. /* */
  134. /****************************************/
  135. int debugging_boolsrch = FALSE;
  136. static int all_key_types = TRUE;
  137. static UCHAR *bitvec_allocp = NULL;
  138. static size_t bitvec_allocsz = 0;
  139. static long bitveclen; /* 1/8 of tot_addr_count */
  140. static UCHAR *bitvecs [DtSrMAX_STEMCOUNT];
  141. static int check_dates = FALSE;
  142. static int do_stat_sort = FALSE;
  143. static double idf [DtSrMAX_STEMCOUNT];
  144. static char *msgbuf = NULL;
  145. static int need_zero_permute = FALSE;
  146. static struct or_objrec objrec;
  147. static DB_ADDR objrecdba;
  148. static int or_abstrsz = 0;
  149. static int or_fzkeysz = 0;
  150. static short or_language = DtSrLaENG;
  151. static long or_maxdba; /* largest dba in database */
  152. static long or_reccount; /* tot num db obj (real_num_rec) */
  153. static long or_recslots; /* D00 slots per obj (slot_d00) */
  154. static struct or_hwordrec
  155. *or_wordrecs = NULL;
  156. static PROXWT *proxwts = NULL;
  157. static int proxwtct;
  158. static UCHAR *result_bitvec;
  159. static long result_count = 0;
  160. static DtSrResult *resultlist = NULL;
  161. static int save_stemno = 0;
  162. static long tot_addr_count; /* may be > reccount bcs deletes */
  163. static int vistano;
  164. static float *wtvec = NULL;
  165. extern void find_keyword (char *cur_word, int vista_num);
  166. extern void read_wordstr (struct or_hwordrec * glob_word, int vista_num);
  167. /************************************************/
  168. /* */
  169. /* got_USR_STOPSRCH */
  170. /* */
  171. /************************************************/
  172. /* Called at beginning of every workproc.
  173. * Returns TRUE if user pushed STOP SEARCH button,
  174. * else FALSE.
  175. */
  176. static int got_USR_STOPSRCH (void)
  177. {
  178. if ((usrblk.flags & USR_STOPSRCH) == 0)
  179. return FALSE;
  180. if (OE_flags & OE_AUDIT)
  181. oe_write_audit_rec (-1L);
  182. usrblk.retncode = OE_USER_STOP;
  183. return TRUE;
  184. }
  185. /****************************************/
  186. /* */
  187. /* read_recno */
  188. /* */
  189. /****************************************/
  190. /* Utility function.
  191. * Reads a database record given a d99 record number.
  192. * Returns TRUE and loads globals objrec and objrecdba
  193. * on success, else returns FALSE.
  194. */
  195. static int read_recno (long recno)
  196. {
  197. /* Convert recno to a real dba */
  198. objrecdba = (recno - 1) * or_recslots + 2;
  199. if (objrecdba >= or_maxdba)
  200. return FALSE;
  201. objrecdba |= (OR_D00 << 24);
  202. /* Read the object record.
  203. * Skip records with database read errors.
  204. * Use d_crset instead of CRSET and d_recread
  205. * instead of RECREAD to trap vista errors
  206. * without aborting.
  207. */
  208. d_crset (&objrecdba, vistano);
  209. if (db_status != S_OKAY) {
  210. BAD_DBA:
  211. if (debugging_boolsrch) {
  212. fprintf (aa_stderr,
  213. PROGNAME"434 Invalid dba %ld. "
  214. "recno=%ld bitvec[%ld]=%02x db_status=%d.\n",
  215. objrecdba, recno, recno>>3, 1<<(recno%8), db_status);
  216. fflush (aa_stderr);
  217. }
  218. return FALSE;
  219. }
  220. d_recread (&objrec, vistano);
  221. if (db_status != S_OKAY)
  222. goto BAD_DBA;
  223. swab_objrec (&objrec, NTOH);
  224. return TRUE;
  225. } /* read_recno() */
  226. /************************************************/
  227. /* */
  228. /* calculate_idfs */
  229. /* */
  230. /************************************************/
  231. /* Subroutine of boolean_search() initialization.
  232. * Loads idf[] (inverse doc frequency) for each stem.
  233. * IDF = 1.0 for a word that occurs in every record.
  234. * For a word that occurs only once in entire database:
  235. * NUM OF DB RECS IDF OF SINGULAR WORD
  236. * 10 4.32
  237. * 100 7.64
  238. * 1,000 10.97
  239. * 10,000 14.29
  240. * 100,000 17.61
  241. * 1,000,000 20.93
  242. * 10,000,000 24.25
  243. */
  244. static void calculate_idfs (void)
  245. {
  246. int i;
  247. double dbl;
  248. for (i = 0; i < saveusr.stemcount; i++) {
  249. if ( or_wordrecs[i].or_hwaddrs == 0 ||
  250. or_wordrecs[i].or_hwordkey[0] == '@')
  251. idf[i] = 0.0;
  252. else {
  253. /* ln(2) = 0.693147181 */
  254. dbl = (double) or_reccount / (double) or_wordrecs[i].or_hwaddrs;
  255. idf[i] = log(dbl) / 0.693147181 + 1.0;
  256. if (debugging_boolsrch)
  257. fprintf (aa_stderr,
  258. PROGNAME"733 IDF[%d] numdocs=%5ld idf=%lf\n",
  259. i, (long) or_wordrecs[i].or_hwaddrs, idf[i]);
  260. }
  261. }
  262. return;
  263. } /* calculate_idfs() */
  264. /************************************************/
  265. /* */
  266. /* load_or_wordrecs */
  267. /* */
  268. /************************************************/
  269. /* Subroutine of boolean_search() initialization.
  270. * Loads or_wordrecs[] array with vista key file
  271. * records for each term in saveusr.stems.
  272. * Returns TRUE on success. Else returns FALSE with
  273. * appropriate usrblk.retncode and user msgs on msglist.
  274. */
  275. static int load_or_wordrecs (void)
  276. {
  277. int i, j, k;
  278. int stemno;
  279. struct or_hwordrec
  280. *wordrec;
  281. int colloc_count = 0;
  282. int not_found_count = 0;
  283. if (or_wordrecs)
  284. free (or_wordrecs);
  285. or_wordrecs = austext_malloc (
  286. saveusr.stemcount * sizeof (struct or_hwordrec) + 16,
  287. PROGNAME "782", NULL);
  288. for (stemno = 0; stemno < saveusr.stemcount; stemno++) {
  289. wordrec = &or_wordrecs [stemno];
  290. /* If this is a collocation term,
  291. * save the two indexes and the collocation
  292. * value in the wordrec buffer instead of usual
  293. * offsets and dba counts.
  294. */
  295. if (saveusr.stems[stemno][0] == '@') {
  296. strcpy (wordrec->or_hwordkey, saveusr.stems[stemno]);
  297. sscanf (saveusr.stems[stemno], COLLOC_STEM_FORMAT, &i, &j, &k);
  298. wordrec->or_hwoffset = i;
  299. wordrec->or_hwfree = j;
  300. wordrec->or_hwaddrs = k;
  301. colloc_count++;
  302. continue;
  303. }
  304. if (debugging_boolsrch)
  305. fprintf (aa_stderr, PROGNAME"823 KEYFIND[%d] ", stemno);
  306. find_keyword (saveusr.stems[stemno], vistano);
  307. /*
  308. * If term is found, add it to the or_wordrecs[] array.
  309. * But it is an error to include a word in more records
  310. * than the max specified in site config file. This is
  311. * meaningful for databases where certain common high
  312. * frequency words slip by which should be on the stoplist.
  313. * It's possible in huge databases to run out of memory
  314. * assembling very long resultlists.
  315. */
  316. if (db_status == S_OKAY) {
  317. strncpy (wordrec->or_hwordkey, saveusr.stems[stemno],
  318. DtSrMAXWIDTH_HWORD);
  319. wordrec->or_hwordkey [DtSrMAXWIDTH_HWORD - 1] = 0;
  320. read_wordstr (wordrec, vistano);
  321. if (db_status != S_OKAY) {
  322. /* Probable corrupted database. The btree
  323. * read succeeded but the record read failed.
  324. */
  325. sprintf (msgbuf, CATGETS(dtsearch_catd, MS_boolsrch, 6,
  326. "%s Database Error. Word '%s' is\n"
  327. "listed in database '%s' but has no index record.") ,
  328. PROGNAME"295", usrblk.stems[stemno], usrblk.dblk->label);
  329. DtSearchAddMessage (msgbuf);
  330. usrblk.retncode = OE_SYSTEM_STOP;
  331. if (debugging_boolsrch)
  332. fprintf (aa_stderr,
  333. "db error, db_status = %d.\n", db_status);
  334. return FALSE;
  335. }
  336. if (debugging_boolsrch)
  337. fprintf (aa_stderr, "ofs=%ld addrs=%ld free=%ld\n",
  338. (long) wordrec->or_hwoffset,
  339. (long) wordrec->or_hwaddrs,
  340. (long) wordrec->or_hwfree);
  341. if (wordrec->or_hwaddrs > OE_words_hitlimit) {
  342. sprintf (msgbuf, CATGETS(dtsearch_catd, MS_boolsrch, 14,
  343. "%s '%s' has more than %ld hits.\n"
  344. "Please remove it from the query or raise the WHITLIM\n"
  345. "value in the search engine configuration file."),
  346. PROGNAME"1444", wordrec->or_hwordkey, OE_words_hitlimit);
  347. DtSearchAddMessage (msgbuf);
  348. /* Also log WHITLIM msg for administrator... */
  349. fprintf (aa_stderr, "%s\n", msgbuf);
  350. usrblk.retncode = OE_BAD_QUERY;
  351. return FALSE;
  352. }
  353. }
  354. /* Only other possible nonfatal vista return is S_NOTFOUND.
  355. * If qry_is_all_ANDs we can quit right now.
  356. * Otherwise switch off all bits in the word's bit vector.
  357. */
  358. else if (qry_is_all_ANDs) {
  359. if (debugging_boolsrch)
  360. fputs ("not found, qry_all_ANDs, quit.\n", aa_stderr);
  361. usrblk.retncode = OE_NOTAVAIL;
  362. return FALSE;
  363. }
  364. else {
  365. memset (wordrec, 0, sizeof(struct or_hwordrec));
  366. if (debugging_boolsrch)
  367. fputs ("not found, addrs-->0.\n", aa_stderr);
  368. not_found_count++;
  369. }
  370. } /* end loop for each term in saveusr.stems[] */
  371. /* It's a failure if all the user's words
  372. * don't exist in database.
  373. */
  374. if (not_found_count + colloc_count >= saveusr.stemcount) {
  375. usrblk.retncode = OE_NOTAVAIL;
  376. return FALSE;
  377. }
  378. return TRUE;
  379. } /* load_or_wordrecs() */
  380. /****************************************/
  381. /* */
  382. /* get_proximity */
  383. /* */
  384. /****************************************/
  385. /* Subroutine of stuff_DtSrResult().
  386. * Given d99recno, finds proxwt[] for record,
  387. * calculates and returns integer proximity.
  388. */
  389. static int get_proximity (long recno)
  390. {
  391. long byteno = recno >> 3;
  392. int bitmask = 1 << (recno % 8);
  393. int i;
  394. for (i = 0; i < proxwtct; i++)
  395. if (proxwts[i].byteno == byteno && proxwts[i].bitmask == bitmask)
  396. break;
  397. if (i >= proxwtct)
  398. return -1;
  399. return proxwts[i].proximity;
  400. } /* get_proximity() */
  401. /****************************************/
  402. /* */
  403. /* stuff_DtSrResult */
  404. /* */
  405. /****************************************/
  406. /* Subroutine of load_DtSrResults_WK().
  407. * Loads passed DtSrResult structure with data from global objrec.
  408. * Performs additional vista reads as necessary to get misc recs.
  409. */
  410. static void stuff_DtSrResult (
  411. DtSrResult *new,
  412. long recno)
  413. {
  414. int m;
  415. int fzkey_remaining;
  416. char *src, *targ, *targend;
  417. static struct or_miscrec
  418. miscrecbuf;
  419. new->objflags = objrec.or_objflags;
  420. new->objuflags = objrec.or_objuflags;
  421. new->objsize = objrec.or_objsize;
  422. new->objdate = objrec.or_objdate;
  423. new->objtype = objrec.or_objtype;
  424. new->objcost = objrec.or_objcost;
  425. new->dbn = OE_dbn;
  426. new->dba = objrecdba;
  427. new->language = or_language;
  428. strncpy (new->reckey, objrec.or_objkey, DtSrMAX_DB_KEYSIZE);
  429. if (do_stat_sort)
  430. new->proximity = get_proximity (recno);
  431. /* The abstract immediately follows the fuzzy key
  432. * in the FZKABS misc recs. It may span several recs.
  433. */
  434. new->abstractp = (char *) (new + 1);
  435. if (or_abstrsz > 0) {
  436. targ = new->abstractp;
  437. targend = targ + or_abstrsz - 1;
  438. fzkey_remaining = or_fzkeysz;
  439. CRSET (PROGNAME"226", &objrecdba, vistano);
  440. SETOR (PROGNAME"227", OR_OBJ_MISCS, saveusr.vistano);
  441. FINDFM (PROGNAME"228", OR_OBJ_MISCS, saveusr.vistano);
  442. while (db_status == S_OKAY) {
  443. RECREAD (PROGNAME"2209", &miscrecbuf, saveusr.vistano);
  444. NTOHS (miscrecbuf.or_misctype);
  445. if (miscrecbuf.or_misctype == ORM_FZKABS) {
  446. src = (char *) miscrecbuf.or_misc;
  447. for (m = 0; m < sizeof(miscrecbuf.or_misc); m++) {
  448. /* skip over the fzkey */
  449. if (fzkey_remaining > 0) {
  450. src++;
  451. fzkey_remaining--;
  452. continue;
  453. }
  454. /* copy the abstract */
  455. *targ = *src;
  456. if (*src++ == 0 || targ++ >= targend) {
  457. *targ = 0;
  458. targ = targend; /* force outer loop end */
  459. break;
  460. }
  461. } /* end for-loop m */
  462. } /* end (misctype == FZKABS) */
  463. if (targ >= targend)
  464. break;
  465. FINDNM (PROGNAME"545", OR_OBJ_MISCS, saveusr.vistano);
  466. } /* end while-loop */
  467. } /* endif: (or_abstrsz > 0) */
  468. return;
  469. } /* stuff_DtSrResult() */
  470. /****************************************/
  471. /* */
  472. /* load_DtSrResults_WK */
  473. /* */
  474. /****************************************/
  475. /* Builds DtSrResult list for every record
  476. * in result_bitvec, but not more than aa_maxhits.
  477. */
  478. static void load_DtSrResults_WK (void)
  479. {
  480. long recno;
  481. int bitno;
  482. long byteno;
  483. int i;
  484. long dittocount;
  485. DtSrResult *resultp;
  486. size_t resultsz = sizeof(DtSrResult) + or_abstrsz + 4;
  487. if (got_USR_STOPSRCH())
  488. return;
  489. if (resultlist) {
  490. DtSearchFreeResults (&resultlist);
  491. resultlist = NULL;
  492. }
  493. /* Make a single pass through the final result_bitvec.
  494. * For each nonzero bit, ie each database record
  495. * that satisfies the query requirements,
  496. * retrieve the record and push it onto the
  497. * DtSrResult list. If not sorting records,
  498. * stop when we reach the user's specified aa_maxhits count.
  499. */
  500. dittocount = 0;
  501. for (recno = 1; recno < tot_addr_count; recno++) {
  502. byteno = recno >> 3; /* divide by 8 */
  503. bitno = recno % 8;
  504. /* Skip zero bits */
  505. if ((result_bitvec[byteno] & (1 << bitno)) == 0)
  506. continue;
  507. if (!read_recno (recno))
  508. continue;
  509. /* Create new DtSrResult node, push it onto resultlist. */
  510. resultp = austext_malloc (resultsz + 4, PROGNAME"466", NULL);
  511. memset (resultp, 0, resultsz);
  512. resultp->link = resultlist;
  513. resultlist = resultp;
  514. /* Load the new DtSrResult node from the object record */
  515. stuff_DtSrResult (resultp, recno);
  516. /* Check if any more reads are necessary.
  517. * If not sorting, stop after aa_maxhits.
  518. * If sorting, there won't be more than
  519. * aa_maxhits recs in the bitvec anyway.
  520. */
  521. dittocount++;
  522. if (dittocount >= aa_maxhits)
  523. break;
  524. } /* end bitvec loop */
  525. /*--------- All Done. Clean up and return to caller. ---------*/
  526. /*@@@@@@ make separate workproc call if aa_maxhits > 100.
  527. @@@@@ sort may take a long time */
  528. if (wtvec) {
  529. free (wtvec);
  530. wtvec = NULL;
  531. }
  532. if (proxwts) {
  533. free (proxwts);
  534. proxwts = NULL;
  535. }
  536. if (dittocount <= 0) {
  537. usrblk.workproc = dummy_workproc;
  538. usrblk.retncode = OE_NOTAVAIL;
  539. return;
  540. }
  541. usrblk.retncode = OE_OK;
  542. usrblk.workproc = dummy_workproc;
  543. usrblk.stemcount = saveusr.stemcount;
  544. if (usrblk.search_type == 'W')
  545. memcpy (usrblk.stems, saveusr.stems,
  546. saveusr.stemcount * DtSrMAXWIDTH_HWORD);
  547. else
  548. /* Don't copy first char (ctrl-o) stem */
  549. for (i = 0; i < saveusr.stemcount; i++)
  550. strcpy (usrblk.stems[i], &saveusr.stems[i][1]);
  551. if (do_stat_sort)
  552. DtSearchSortResults (&resultlist, DtSrSORT_PROX);
  553. usrblk.dittocount = dittocount;
  554. if (usrblk.dittolist)
  555. DtSearchFreeResults (&usrblk.dittolist);
  556. usrblk.dittolist = resultlist;
  557. resultlist = NULL;
  558. return;
  559. } /* load_DtSrResults_WK() */
  560. /****************************************/
  561. /* */
  562. /* weights_filter_WK */
  563. /* */
  564. /****************************************/
  565. /* This workproc is called only if we're doing statistical sorting.
  566. * (1) It reduces the result_bitvec to it's final size,
  567. * containing only the highest aa_maxhits statistical weights
  568. * in wtvec.
  569. * (2) It replaces (possibly large) wtvec with (probably much smaller)
  570. * array of PROXWT structures containing the selected records'
  571. * weights and calculated proximities, for final ranking sort.
  572. *
  573. */
  574. static void weights_filter_WK (void)
  575. {
  576. int i;
  577. double scalefac;
  578. long recno;
  579. int smallest, biggest;
  580. float biggestwt;
  581. long byteno, smallest_byteno;
  582. int bitmask, smallest_bitmask;
  583. if (got_USR_STOPSRCH())
  584. return;
  585. /* Init weight filtering */
  586. if (proxwts)
  587. free (proxwts);
  588. proxwtct = (result_count < aa_maxhits)? result_count : aa_maxhits;
  589. proxwts = austext_malloc (proxwtct * sizeof(PROXWT) + 4,
  590. PROGNAME"429", NULL);
  591. memset (proxwts, 0, proxwtct * sizeof(PROXWT));
  592. smallest = 0;
  593. scalefac = 0.0;
  594. biggestwt = 0.0; /* biggest single wt of all docs */
  595. /* One pass thru entire result_bitvec */
  596. for (recno = 1; recno < tot_addr_count; recno++) {
  597. byteno = recno >> 3;
  598. bitmask = 1 << (recno % 8);
  599. /* Skip zero bits */
  600. if ((result_bitvec[byteno] & bitmask) == 0)
  601. continue;
  602. /* Make scalefac = sum of squares of all wts in bitvec.
  603. * It's possible that all or some of the weights are
  604. * zero (eg queries like "~aaa" or "~aaa | bbb").
  605. * In this case give them a very small positive number
  606. * so we don't divide by zero later on.
  607. */
  608. if (wtvec[recno] == 0.0)
  609. wtvec[recno] = 0.1;
  610. scalefac += (double) wtvec[recno] * (double) wtvec[recno];
  611. /*
  612. * The following logic first fills up the proxwts table.
  613. * After that if a bitvec's weight is larger than the smallest
  614. * proxwt, replace the smallest proxwt with the new weight
  615. * and switch off the previous smallest in the original bitvec.
  616. */
  617. /*
  618. * Just discard rec on bitvec if it's weight
  619. * is smaller than the current smallest.
  620. */
  621. if (wtvec [recno] <= proxwts[smallest].wt) {
  622. RESET_BIT (result_bitvec, byteno, bitmask);
  623. result_count--;
  624. continue;
  625. }
  626. /*
  627. * Else discard current smallest if
  628. * table full, ie it really points to something.
  629. */
  630. if (proxwts[smallest].wt > 0.0) {
  631. smallest_byteno = proxwts[smallest].byteno;
  632. smallest_bitmask = proxwts[smallest].bitmask;
  633. RESET_BIT (result_bitvec, smallest_byteno, smallest_bitmask);
  634. result_count--;
  635. }
  636. /* Add this weight to the proxwts table. */
  637. proxwts [smallest] .wt = wtvec [recno];
  638. proxwts [smallest] .byteno = byteno;
  639. proxwts [smallest] .bitmask = bitmask;
  640. /* Keep track of the table entry that has
  641. * the highest weight. This will eventually
  642. * be the first sorted hit on the hitlist.
  643. * It's weight/proximity will be used
  644. * to scale the proximities of the
  645. * other hits.
  646. */
  647. if (biggestwt < wtvec[recno]) {
  648. biggestwt = wtvec[recno];
  649. biggest = smallest;
  650. }
  651. /* Find the next smallest */
  652. smallest = 0;
  653. for (i = 1; i < proxwtct; i++) {
  654. if (proxwts[i].wt < proxwts[smallest].wt)
  655. smallest = i;
  656. }
  657. } /* end loop on every recno */
  658. free (wtvec);
  659. wtvec = NULL;
  660. /* PROXIMITY CALCULATIONS.
  661. * In order to translate statistical weight into an AusText
  662. * proximity, basically you have to invert it, then scale it.
  663. * The statistical weight is a similarity measure: the
  664. * larger it is the more similar the document to the query.
  665. * But AusText 'proximity' is like a 'distance' measure,
  666. * the smaller the number the closer the document is to the query.
  667. *
  668. * First 'normalize' each document's statistical
  669. * weight to be a fraction between 0 and 1. Done
  670. * by calculating a normalization factor,
  671. * the sqrt of the sum of squares of weights of all
  672. * docs that would have qualified for the hitlist
  673. * if we weren't truncating. Note cosine-based normalization
  674. * factor (Pythagorean) always >= largest wt so we can
  675. * guarantee all normalized weights are > 0.0 and <= 1.0.
  676. *
  677. * The proximity itself is calculated as the 'percent value'
  678. * that the doc is 'distant' from perfection (1.0 or 100%).
  679. * For example, if the normalized weight of the first record
  680. * is .931 then it's proximity will be 7 (100% - 93% = 7).
  681. *
  682. * The proximity of every other hit is scaled away
  683. * from the first because the normalization algorithm
  684. * tends to clump proximities when there are a lot of hits.
  685. * Specifically the proximity of every hit is a constant
  686. * scale factor (derived from the first proximity),
  687. * divided by it's weight.
  688. *
  689. * A "bulls eye" (normalized weight = 1.0, proximity == 0)
  690. * for the first hit is not allowed so scale factor will
  691. * not also be zero. Otherwise *all* hits in that particular
  692. * results list would be bulls eyes.
  693. */
  694. scalefac = (double) biggestwt / sqrt (scalefac);
  695. /* normalized weight of first hit */
  696. scalefac = (1.0 - scalefac) * 100.0;
  697. /* proximity of first hit */
  698. if (scalefac < 1.0)
  699. scalefac = 1.0;
  700. /* No bulls eyes */
  701. scalefac *= (double) biggestwt * 1.2;
  702. /* scale factor for other hits */
  703. for (i = 0; i < proxwtct; i++) {
  704. proxwts[i].proximity = (int) (scalefac / (double) proxwts[i].wt);
  705. if (proxwts[i].proximity > 9999)
  706. proxwts[i].proximity = 9999;
  707. }
  708. if (debugging_boolsrch) {
  709. fprintf (aa_stderr,
  710. PROGNAME"489 FINAL PROXWTS proxwtct=%d bigwt=%.2f scalefac=%.2lf\n",
  711. proxwtct, biggestwt, scalefac);
  712. for (i=0; i<10; i++) {
  713. if (i >= proxwtct)
  714. break;
  715. fprintf (aa_stderr,
  716. " byteno=%3ld bitmask=%02x wt=%.2f prox=%d\n",
  717. proxwts[i].byteno, proxwts[i].bitmask,
  718. proxwts[i].wt, proxwts[i].proximity);
  719. }
  720. fprintf (aa_stderr, PROGNAME"499 WEIGHT RESULTS resultct=%ld bv=\n",
  721. result_count);
  722. for (i=0; i<22; i++) {
  723. if (i >= bitveclen)
  724. break;
  725. fprintf (aa_stderr, " %02x", (int) result_bitvec[i]);
  726. }
  727. fputc ('\n', aa_stderr);
  728. fflush (aa_stderr);
  729. }
  730. usrblk.retncode = OE_SEARCHING;
  731. usrblk.workproc = load_DtSrResults_WK;
  732. return;
  733. } /* weights_filter_WK() */
  734. /****************************************/
  735. /* */
  736. /* dbread_filter_WK */
  737. /* */
  738. /****************************************/
  739. /* Called if we must remove documents from result_bitvec
  740. * because of keytype or date,
  741. */
  742. static void dbread_filter_WK (void)
  743. {
  744. long recno;
  745. long byteno;
  746. int bitmask;
  747. long discards;
  748. if (got_USR_STOPSRCH())
  749. return;
  750. if (debugging_boolsrch) {
  751. discards = 0;
  752. fputs (PROGNAME"865 DBREAD discards (k=keytype d=date):\n", aa_stderr);
  753. fflush (aa_stderr);
  754. }
  755. /* One pass thru entire result_bitvec */
  756. for (recno = 1; recno < tot_addr_count; recno++) {
  757. byteno = recno >> 3;
  758. bitmask = 1 << (recno % 8);
  759. if ((result_bitvec[byteno] & bitmask) == 0)
  760. continue;
  761. if (!read_recno (recno))
  762. continue;
  763. /* Skip undesired record types */
  764. if (!all_key_types) {
  765. if (strchr (saveusr.ktchars, objrec.or_objkey[0]) == NULL) {
  766. RESET_BIT (result_bitvec, byteno, bitmask);
  767. result_count--;
  768. if (debugging_boolsrch) {
  769. discards++;
  770. fputc ('k', aa_stderr);
  771. fflush (aa_stderr);
  772. }
  773. continue;
  774. }
  775. }
  776. /* Skip record if out of date range */
  777. if (check_dates) {
  778. if (!objdate_in_range (objrec.or_objdate,
  779. usrblk.objdate1, usrblk.objdate2)) {
  780. RESET_BIT (result_bitvec, byteno, bitmask);
  781. result_count--;
  782. if (debugging_boolsrch) {
  783. discards++;
  784. fputc ('d', aa_stderr);
  785. fflush (aa_stderr);
  786. }
  787. continue;
  788. }
  789. }
  790. } /* end loop on every recno */
  791. if (debugging_boolsrch) {
  792. int i;
  793. if (discards)
  794. fputc ('\n', aa_stderr);
  795. fprintf (aa_stderr,
  796. PROGNAME"857 DBREAD RESULTS discards=%ld resultct=%ld bv=\n",
  797. discards, result_count);
  798. for (i=0; i<22; i++) {
  799. if (i >= bitveclen)
  800. break;
  801. fprintf (aa_stderr, " %02x", (int) result_bitvec[i]);
  802. }
  803. fputc ('\n', aa_stderr);
  804. fflush (aa_stderr);
  805. }
  806. /* Determine next workproc.
  807. * (1) If no records survived the read db filter,
  808. * we're done, return 'no hits'.
  809. * (2) If we're sorting, the next workproc reduces the
  810. * bitvec to the aa_maxhits recs with the highest
  811. * statistical weights.
  812. * (3) Otherwise the next workproc just loads the hitlist.
  813. */
  814. if (result_count <= 0) {
  815. usrblk.retncode = OE_NOTAVAIL;
  816. usrblk.workproc = dummy_workproc;
  817. }
  818. else if (do_stat_sort) {
  819. usrblk.retncode = OE_SEARCHING;
  820. usrblk.workproc = weights_filter_WK;
  821. }
  822. else {
  823. if (debugging_boolsrch)
  824. fprintf (aa_stderr,
  825. PROGNAME"931 No sorting by statistical weights.\n");
  826. usrblk.retncode = OE_SEARCHING;
  827. usrblk.workproc = load_DtSrResults_WK;
  828. }
  829. return;
  830. } /* dbread_filter_WK() */
  831. /****************************************/
  832. /* */
  833. /* calc_result_bitvec_WK */
  834. /* */
  835. /****************************************/
  836. /* Second workproc after read_stem_bitvec_WK().
  837. * If possible, minimizes size of truth table permutes,
  838. * then applies them to stem bitvecs to create result_bitvec.
  839. */
  840. static void calc_result_bitvec_WK (void)
  841. {
  842. int mask;
  843. int cpm;
  844. long byteno;
  845. int bitno, stemno;
  846. UCHAR permute;
  847. UCHAR my_permutes [256];
  848. int my_pmsz;
  849. int i;
  850. if (got_USR_STOPSRCH())
  851. return;
  852. /* If there are fewer than a full complement of stems,
  853. * minimize size of truth table by discarding
  854. * permutes that refer to unused stems.
  855. */
  856. if (saveusr.stemcount < DtSrMAX_STEMCOUNT) {
  857. /* Set high order bits of mask to mark unused stem positions */
  858. mask = 0;
  859. for (i = 0; i < saveusr.stemcount; i++)
  860. mask |= 1 << i;
  861. mask = ~mask;
  862. /* 'cpm' is a candidate permute */
  863. my_pmsz = 0;
  864. for (cpm = 0; cpm < 256; cpm++) {
  865. /*
  866. * Discard candidate if it refers to an unused stem.
  867. */
  868. if (cpm & mask)
  869. continue;
  870. /*
  871. * Otherwise if candidate is in final_truthtab, keep it.
  872. */
  873. for (i = 0; i < final_truthtab.pmsz; i++) {
  874. if (final_truthtab.permutes[i] == (UCHAR) cpm) {
  875. my_permutes [my_pmsz] = (UCHAR) cpm;
  876. my_pmsz++;
  877. }
  878. }
  879. }
  880. if (debugging_boolsrch) {
  881. fprintf (aa_stderr,
  882. PROGNAME"565 Minimize truth table, pmsz=%d-->%d\n permutes=",
  883. final_truthtab.pmsz, my_pmsz);
  884. for (i=0; i<16; i++) {
  885. if (i >= my_pmsz)
  886. break;
  887. fprintf (aa_stderr, " %02x", (int) my_permutes [i]);
  888. }
  889. fputc ('\n', aa_stderr);
  890. fflush (aa_stderr);
  891. }
  892. final_truthtab.permutes = my_permutes;
  893. final_truthtab.pmsz = my_pmsz;
  894. } /* end minimize of permutes */
  895. /* Calculate result bit vector.
  896. * Loop 1 is a single pass through the bit vectors
  897. * (a bit loop inside a byte loop).
  898. * For each nonzero bit, ie each database record
  899. * that has at least one of the query terms in it,
  900. * build a 'permute' equivalent to the boolean
  901. * representation of the terms in that record (Loop 2).
  902. * Then search the truth table permutes for a match (Loop 3).
  903. * If found, set the record's bit in the result_bitvec.
  904. */
  905. /* LOOP 1. For each database addr... */
  906. result_count = 0;
  907. for (byteno = 0; byteno < bitveclen; byteno++) {
  908. for (bitno = 0; bitno < 8; bitno++) {
  909. mask = 1 << bitno;
  910. /* LOOP 2. Build permute for each query term. */
  911. permute = 0;
  912. for (stemno = 0; stemno < saveusr.stemcount; stemno++) {
  913. if (bitvecs [stemno] [byteno] & (UCHAR) mask)
  914. permute |= 1 << stemno;
  915. }
  916. /* LOOP 3. Search truth table for matching permute. */
  917. for (i = 0; i < final_truthtab.pmsz; i++) {
  918. if (final_truthtab.permutes[i] == permute) {
  919. result_bitvec [byteno] |= (UCHAR) mask;
  920. result_count++;
  921. }
  922. }
  923. }
  924. }
  925. if (debugging_boolsrch) {
  926. fprintf (aa_stderr, PROGNAME"621 PRELIM RESULTS resultct=%ld bv=\n",
  927. result_count);
  928. for (i=0; i<22; i++) {
  929. if (i >= bitveclen)
  930. break;
  931. fprintf (aa_stderr, " %02x", (int) result_bitvec[i]);
  932. }
  933. fputc ('\n', aa_stderr);
  934. fflush (aa_stderr);
  935. }
  936. /* The next workprocs are 'filters', reducing the size
  937. * of result_bitvec by removing various unwanted records.
  938. * They're done in the following order:
  939. * (1) If no records survived the truth table manipulations,
  940. * we're done, return 'no hits'.
  941. * (2) If we must remove documents because of keytype or date,
  942. * the next workproc is the filter that reads the database.
  943. * (3) If we're sorting, the next workproc reduces the
  944. * bitvec to the aa_maxhits recs with the highest
  945. * statistical weights.
  946. * (4) Otherwise the next workproc just loads the hitlist.
  947. */
  948. if (result_count <= 0) {
  949. usrblk.retncode = OE_NOTAVAIL;
  950. usrblk.workproc = dummy_workproc;
  951. }
  952. else if (!all_key_types || check_dates) {
  953. usrblk.retncode = OE_SEARCHING;
  954. usrblk.workproc = dbread_filter_WK;
  955. }
  956. else if (do_stat_sort) {
  957. if (debugging_boolsrch)
  958. fprintf (aa_stderr,
  959. PROGNAME"948 No db reads necessary for date or keytype.\n");
  960. usrblk.retncode = OE_SEARCHING;
  961. usrblk.workproc = weights_filter_WK;
  962. }
  963. else {
  964. if (debugging_boolsrch)
  965. fprintf (aa_stderr,
  966. PROGNAME"625 No filtering: no sort and no db reads.\n");
  967. usrblk.retncode = OE_SEARCHING;
  968. usrblk.workproc = load_DtSrResults_WK;
  969. }
  970. return;
  971. } /* calc_result_bitvec_WK() */
  972. /****************************************/
  973. /* */
  974. /* read_d99 */
  975. /* */
  976. /****************************************/
  977. /* Subroutine of read_stem_bitvec_WK().
  978. * Repeatedly called to get each d99dba in the inverted index
  979. * file (d99) for a specific index term. The first call passes
  980. * the term's wordrec with d99 offset and size information.
  981. * Subsequent calls pass NULL.
  982. * Returns valid d99dba, or 0 at end of term's index, or -1 on error.
  983. * Actual reads are performed a disk block at a time,
  984. * with dbas stored in a static buffer for the next call.
  985. */
  986. static DB_ADDR read_d99 (struct or_hwordrec *wordrec)
  987. {
  988. static DB_ADDR readbuf [DBAS_PER_BLOCK];
  989. static DB_ADDR *bufptr, *endbuf;
  990. static FILE *fptr;
  991. static long bal_read, request_read;
  992. /* First call for new term */
  993. if (wordrec) {
  994. fptr = usrblk.dblk->iifile;
  995. fseek (fptr, wordrec->or_hwoffset, SEEK_SET);
  996. bal_read = wordrec->or_hwaddrs;
  997. bufptr = endbuf = 0; /* triggers block read */
  998. }
  999. /* Time to read another block */
  1000. if (bufptr >= endbuf) {
  1001. if (bal_read <= 0)
  1002. return 0;
  1003. if (bal_read > DBAS_PER_BLOCK) {
  1004. request_read = DBAS_PER_BLOCK;
  1005. bal_read -= DBAS_PER_BLOCK;
  1006. endbuf = readbuf + DBAS_PER_BLOCK;
  1007. }
  1008. else {
  1009. /* last block is usually short */
  1010. request_read = bal_read;
  1011. bal_read = 0;
  1012. endbuf = readbuf + request_read;
  1013. }
  1014. if (fread (readbuf, sizeof(DB_ADDR), request_read, fptr)
  1015. != request_read) {
  1016. sprintf (msgbuf, CATGETS(dtsearch_catd, MS_boolsrch, 28,
  1017. "%s Database Read Error in %s.d99.") ,
  1018. PROGNAME"428", usrblk.dblk->name);
  1019. DtSearchAddMessage (msgbuf);
  1020. return -1;
  1021. }
  1022. bufptr = readbuf;
  1023. }
  1024. /******return *bufptr++;*******/
  1025. return ntohl (*bufptr++);
  1026. } /* read_d99() */
  1027. /****************************************/
  1028. /* */
  1029. /* get_colloc_bitvec */
  1030. /* */
  1031. /****************************************/
  1032. /* Subroutine of read_stem_bitvec_WK().
  1033. * Constructs a 'collocation bitvector' for current save_stemno.
  1034. * A collocation expression requests the return of all records
  1035. * containing both of two terms (a kind of boolean AND) such that
  1036. * the occurrences are within n characters of each other.
  1037. * For example "ICE @5 CREAM" requests the return of all records
  1038. * containing both "ICE" and "CREAM" but only if they are separated
  1039. * by no more than 5 characters.
  1040. *
  1041. * Since offset information is not stored in the inverted index
  1042. * this module initially returns the intersection of the two words'
  1043. * bit vectors (boolean AND). Then it retrieves each record,
  1044. * builds an offset (hilites) table for each of the two words,
  1045. * then compares the offset differences in the tables.
  1046. * If no occurrence pairs are within the specified separation
  1047. * range, the record is deleted from the bitvector.
  1048. * Returns 0 if successful, otherwise returns -1 and msgs.
  1049. @@@@ rewrite as its own workproc--reading/hiliting can take a long time...
  1050. */
  1051. static int get_colloc_bitvec (void)
  1052. {
  1053. int stemno_A = or_wordrecs[save_stemno].or_hwoffset;
  1054. int stemno_B = or_wordrecs[save_stemno].or_hwfree;
  1055. long range = or_wordrecs[save_stemno].or_hwaddrs;
  1056. UCHAR *bitvec_A = bitvecs [stemno_A];
  1057. UCHAR *bitvec_B = bitvecs [stemno_B];
  1058. UCHAR *bitvec_C = bitvecs [save_stemno];
  1059. long byteno, recno;
  1060. UCHAR bitmask;
  1061. int parse_type;
  1062. int got_a_colloc;
  1063. char *stemp;
  1064. DtSrHitword *hitwords_A, *hitwords_B;
  1065. long hitwcount_A, hitwcount_B;
  1066. long threshold_range;
  1067. DB_ADDR dba;
  1068. LLIST *bloblist;
  1069. long a, b, offset_A, offset_B;
  1070. /* First construct the set intersection (AND) of
  1071. * each of the collocated terms in the colloc bitvec.
  1072. */
  1073. for (byteno = 0; byteno < bitveclen; byteno++)
  1074. bitvec_C [byteno] = bitvec_A [byteno] & bitvec_B [byteno];
  1075. if (debugging_boolsrch) {
  1076. int i;
  1077. fprintf (aa_stderr,
  1078. PROGNAME"312 INTERSECT[%d] (colloc %d & %d):\n",
  1079. save_stemno, stemno_A, stemno_B);
  1080. for (i=0; i<bitveclen; i++) {
  1081. fprintf (aa_stderr, " %02x", bitvec_C[i]);
  1082. if (i > 22)
  1083. break;
  1084. }
  1085. fputc ('\n', aa_stderr);
  1086. fflush (aa_stderr);
  1087. }
  1088. /* Read cleartext for each rec in intersection/colloc bitvec.
  1089. * Get hitwords (hilite table) for each collocation term.
  1090. * Switch off recs in bitvec where no term pairs are in
  1091. * collocation range.
  1092. */
  1093. for (recno = 1; recno < tot_addr_count; recno++) {
  1094. byteno = recno >> 3; /* divide by 8 */
  1095. bitmask = 1 << (recno % 8);
  1096. /* Skip zero bits */
  1097. if ((bitvec_C[byteno] & bitmask) == 0)
  1098. continue;
  1099. /* Convert recno to vista database address.
  1100. * Silently skip rec if dba doesn't exist.
  1101. */
  1102. dba = (recno - 1) * or_recslots + 2;
  1103. if (dba >= or_maxdba) {
  1104. RESET_BIT (bitvec_C, byteno, bitmask);
  1105. continue;
  1106. }
  1107. dba |= (OR_D00 << 24);
  1108. /* Silently skip records that have no document text */
  1109. if ((bloblist = ve_getblobs (dba, vistano)) == NULL) {
  1110. if (debugging_boolsrch) {
  1111. fprintf (aa_stderr,
  1112. PROGNAME"126 No blobs for recno=%ld byteno=%ld mask%02x\n",
  1113. recno, byteno, bitmask);
  1114. fflush (aa_stderr);
  1115. }
  1116. RESET_BIT (bitvec_C, byteno, bitmask);
  1117. continue;
  1118. }
  1119. /* Uncompress record text into usrblk.cleartext */
  1120. if (oe_unblob (bloblist) != OE_OK)
  1121. return -1;
  1122. /* Build 'hilite' table for stem A. If stem
  1123. * can't be found in the record, silently skip it.
  1124. * Otherwise save the table.
  1125. */
  1126. stemp = saveusr.stems [stemno_A];
  1127. if (stemp[0] == STEM_CH) {
  1128. parse_type = 'S';
  1129. stemp++;
  1130. }
  1131. else
  1132. parse_type = 'W';
  1133. if (!hilite_cleartext (parse_type, stemp, 1)) {
  1134. RESET_BIT (bitvec_C, byteno, bitmask);
  1135. continue;
  1136. }
  1137. hitwords_A = usrblk.hitwords;
  1138. hitwcount_A = usrblk.hitwcount;
  1139. usrblk.hitwords = NULL;
  1140. usrblk.hitwcount = 0;
  1141. /* In the same way build 'hilite' table for stem B */
  1142. stemp = saveusr.stems [stemno_B];
  1143. if (stemp[0] == STEM_CH) {
  1144. parse_type = 'S';
  1145. stemp++;
  1146. }
  1147. else
  1148. parse_type = 'W';
  1149. if (!hilite_cleartext (parse_type, stemp, 1)) {
  1150. RESET_BIT (bitvec_C, byteno, bitmask);
  1151. free (hitwords_A);
  1152. continue;
  1153. }
  1154. hitwords_B = usrblk.hitwords;
  1155. hitwcount_B = usrblk.hitwcount;
  1156. usrblk.hitwords = NULL;
  1157. usrblk.hitwcount = 0;
  1158. /* Compare the two hilite tables for range matches */
  1159. got_a_colloc = FALSE;
  1160. b = 0;
  1161. for (a = 0; a < hitwcount_A; a++) {
  1162. offset_A = hitwords_A[a].offset;
  1163. threshold_range = offset_A + hitwords_A[a].length + range;
  1164. for (; b < hitwcount_B; b++) {
  1165. offset_B = hitwords_B[b].offset;
  1166. /* Advance B to first entry past A's offset */
  1167. if (offset_B <= offset_A )
  1168. continue; /* ...the B loop */
  1169. if (offset_B <= threshold_range)
  1170. got_a_colloc = TRUE;
  1171. break; /* ...the B loop */
  1172. } /* end B loop */
  1173. if (got_a_colloc || b >= hitwcount_B)
  1174. break; /* ...the A loop */
  1175. } /* end A loop */
  1176. free (hitwords_A);
  1177. free (hitwords_B);
  1178. /* If no collocations found within range,
  1179. * switch off rec in colloc bitvec.
  1180. */
  1181. if (!got_a_colloc)
  1182. RESET_BIT (bitvec_C, byteno, bitmask);
  1183. } /* end loop on each recno in intersection/colloc bitvec */
  1184. return 0;
  1185. } /* get_colloc_bitvec() */
  1186. /****************************************/
  1187. /* */
  1188. /* read_stem_bitvec_WK */
  1189. /* */
  1190. /****************************************/
  1191. /* First workproc after boolean_search().
  1192. * Each iterative call loads one (save_stemno) real stem's bitvec.
  1193. * After last stem bitvec loaded, sets up
  1194. * call to next workproc in sequence.
  1195. */
  1196. static void read_stem_bitvec_WK (void)
  1197. {
  1198. long byteno;
  1199. DB_ADDR d99recno;
  1200. float weight;
  1201. if (got_USR_STOPSRCH())
  1202. return;
  1203. /* Process collocation 'stems' */
  1204. if (saveusr.stems [save_stemno] [0] == '@') {
  1205. d99recno = get_colloc_bitvec();
  1206. goto DONE_READING;
  1207. }
  1208. for ( d99recno = read_d99 (&or_wordrecs [save_stemno]);
  1209. d99recno;
  1210. d99recno = read_d99 (NULL)) {
  1211. if (d99recno == -1) /* read error */
  1212. break;
  1213. /* Save low byte 'statistical weight' value.
  1214. * It can only be 0 - 255.
  1215. */
  1216. if (do_stat_sort)
  1217. weight = (float) (d99recno & 0x000000ff) + 1.0;
  1218. d99recno = (d99recno >> 8) & 0x00ffffff;
  1219. /* Set correct bit in bitvec.
  1220. * The byte number is the recno divided by 8.
  1221. * The bit number is the remainder after division by 8.
  1222. */
  1223. if ((byteno = d99recno >> 3) >= bitveclen) {
  1224. sprintf (msgbuf, CATGETS(dtsearch_catd, MS_boolsrch, 32,
  1225. "%s Database Error: %s '%s'\n"
  1226. "in database '%s' has invalid d99 record number %ld.") ,
  1227. PROGNAME"394",
  1228. (usrblk.search_type == 'W') ?
  1229. CATGETS(dtsearch_catd, MS_boolsrch, 33, "Word") :
  1230. CATGETS(dtsearch_catd, MS_boolsrch, 34, "Stem of"),
  1231. usrblk.stems [save_stemno],
  1232. usrblk.dblk->label,
  1233. d99recno);
  1234. DtSearchAddMessage (msgbuf);
  1235. d99recno = -1; /* force error return */
  1236. goto DONE_READING;
  1237. }
  1238. bitvecs [save_stemno] [byteno] |= 1 << (d99recno % 8);
  1239. /* Add to correct weight in weight vector.
  1240. * IDF ranges between 1.0 and 20.0, and weight
  1241. * is 1 - 256, so we're adding 1 - ~5000 to wtvec.
  1242. */
  1243. if (do_stat_sort)
  1244. wtvec [d99recno] += weight * (float) idf [save_stemno];
  1245. } /* end loop that retrieves every d99recno for curr stem */
  1246. DONE_READING:
  1247. if (debugging_boolsrch) {
  1248. int i;
  1249. if (debugging_boolsrch)
  1250. fprintf (aa_stderr, PROGNAME"313 BITVEC[%d]:\n", save_stemno);
  1251. for (i=0; i<bitveclen; i++) {
  1252. fprintf (aa_stderr, " %02x", bitvecs[save_stemno][i]);
  1253. if (i > 22)
  1254. break;
  1255. }
  1256. fputc ('\n', aa_stderr);
  1257. fflush (aa_stderr);
  1258. }
  1259. if (d99recno == 0) {
  1260. /* Normal conclusion. Increment to next stem.
  1261. * If not all stems have been read,
  1262. * this is still the next workproc.
  1263. * Otherwise the next workproc is the one
  1264. * merging all bitvectors into the final
  1265. * result bitvec using the truth table.
  1266. */
  1267. usrblk.retncode = OE_SEARCHING;
  1268. if (++save_stemno < saveusr.stemcount)
  1269. usrblk.workproc = read_stem_bitvec_WK;
  1270. else
  1271. usrblk.workproc = calc_result_bitvec_WK;
  1272. }
  1273. else
  1274. /* d99recno must be -1 */
  1275. usrblk.retncode = OE_SYSTEM_STOP;
  1276. return;
  1277. } /* read_stem_bitvec_WK() */
  1278. /****************************************/
  1279. /* */
  1280. /* boolean_search */
  1281. /* */
  1282. /****************************************/
  1283. /* Called from Opera_Engine after successful boolean_parse().
  1284. * Expects valid globals: saveusr.stems, saveusr.stemcount,
  1285. * usrblk.stems (contains original unstemmed query terms for msgs),
  1286. * usrblk.search_type, final_truthtab, qry_has_no_NOTs,
  1287. * and qry_is_all_ANDs.
  1288. * Based on parts of the function ve_word_search().
  1289. * Upon return, usrblk.retncode, msglist, etc is appropriately loaded.
  1290. * Upon successful return usrblk.stems, usrblk.stemcount,
  1291. * and dittolist are also loaded.
  1292. */
  1293. void boolean_search (void)
  1294. {
  1295. int i, j;
  1296. size_t allocsz_needed;
  1297. /* Sanity checks */
  1298. if ( saveusr.stemcount <= 0 ||
  1299. final_truthtab.pmsz <= 0 ||
  1300. final_truthtab.pmsz >= 256 ) {
  1301. fprintf (aa_stderr, CATGETS(dtsearch_catd, MS_boolsrch, 35,
  1302. "%s Program Error: stemct=%d pmsz=%d\n") ,
  1303. PROGNAME"1404", saveusr.stemcount, final_truthtab.pmsz);
  1304. DtSearchExit (14);
  1305. }
  1306. /*---------- Init globals ----------*/
  1307. if (!msgbuf)
  1308. msgbuf = austext_malloc (500, PROGNAME"393", NULL);
  1309. debugging_boolsrch = (usrblk.debug & USRDBG_SRCHCMPL);
  1310. need_zero_permute = (final_truthtab.permutes[0] == 0);
  1311. do_stat_sort = ((usrblk.flags & USR_SORT_WHITL) != 0);
  1312. check_dates = (usrblk.objdate1 || usrblk.objdate2);
  1313. or_abstrsz = usrblk.dblk->dbrec.or_abstrsz;
  1314. or_fzkeysz = usrblk.dblk->dbrec.or_fzkeysz;
  1315. or_language = usrblk.dblk->dbrec.or_language;
  1316. or_maxdba = usrblk.dblk->dbrec.or_maxdba;
  1317. usrblk.flags &= ~USR_STOPSRCH; /* turn off stop button */
  1318. saveusr.vistano = vistano = usrblk.dblk->vistano;
  1319. saveusr.dittolist = NULL;
  1320. saveusr.dittocount = 0L;
  1321. saveusr.iterations = INIT_ITERATIONS;
  1322. /*
  1323. * saveusr.ktchars is a string holding
  1324. * first char of desired record ids.
  1325. */
  1326. all_key_types = TRUE;
  1327. for (i = 0, j = 0; i < usrblk.dblk->ktcount; i++) {
  1328. if (usrblk.dblk->keytypes[i].is_selected)
  1329. saveusr.ktchars[j++] = usrblk.dblk->keytypes[i].ktchar;
  1330. else
  1331. all_key_types = FALSE;
  1332. }
  1333. saveusr.ktchars[j] = '\0';
  1334. or_recslots = (long) (usrblk.dblk->dbrec.or_recslots);
  1335. or_reccount = usrblk.dblk->dbrec.or_reccount;
  1336. /* RECFRST is just to get the slot# (dba) of the
  1337. * first real object record after the dbrec.
  1338. * Currently the dbrec occupies only one slot,
  1339. * the first (#1), so dba will usually be #2.
  1340. */
  1341. /********
  1342. RECFRST(PROGNAME"2545", OR_OBJREC, saveusr.vistano);
  1343. CRGET(PROGNAME"2546", &dba, saveusr.vistano);
  1344. dba &= 0x00FFFFFF;
  1345. ********/
  1346. tot_addr_count = ((usrblk.dblk->dbrec.or_maxdba + 1) / or_recslots) + 1;
  1347. bitveclen = (tot_addr_count >> 3) + 1;
  1348. if (debugging_boolsrch) {
  1349. fprintf (aa_stderr, PROGNAME"360 "
  1350. "boolean_search: typ='%c' needzpm?=%d sort?=%d maxhits=%d\n"
  1351. " maxdba=%ld recct=%ld recslts=%ld\n"
  1352. " totnmadr=%ld bvln=%ld allkts?=%d ktchars='%s'\n"
  1353. ,usrblk.search_type
  1354. ,need_zero_permute
  1355. ,do_stat_sort
  1356. ,aa_maxhits
  1357. ,(long) usrblk.dblk->dbrec.or_maxdba
  1358. ,or_reccount
  1359. ,or_recslots
  1360. ,tot_addr_count
  1361. ,bitveclen
  1362. ,all_key_types
  1363. ,saveusr.ktchars
  1364. );
  1365. fflush (aa_stderr);
  1366. }
  1367. /*---------- Read vista btree ----------
  1368. * Load or_wordrecs[] array for each term in saveusr.stems.
  1369. */
  1370. if (!load_or_wordrecs())
  1371. return;
  1372. /* If statistically sorting final resultlist, calculate
  1373. * idf (inverse document frequency) for each term using
  1374. * the frequency data in or_wordrecs[].
  1375. */
  1376. if (do_stat_sort)
  1377. calculate_idfs();
  1378. /* Bitvector allocation. Number needed is one for each stem,
  1379. * plus one extra to accumulate the result bitvector.
  1380. */
  1381. allocsz_needed = bitveclen * (saveusr.stemcount + 1);
  1382. if (debugging_boolsrch)
  1383. fprintf (aa_stderr, PROGNAME"430 "
  1384. "bitvecs[] alloc needed=%lu (bvln=%ld stems=%d+1), have=%lu.\n",
  1385. (unsigned long) allocsz_needed, bitveclen, saveusr.stemcount, (unsigned long) bitvec_allocsz);
  1386. if (bitvec_allocsz < allocsz_needed) {
  1387. if (bitvec_allocp)
  1388. free (bitvec_allocp);
  1389. bitvec_allocp = austext_malloc (allocsz_needed + 16,
  1390. PROGNAME"508", NULL);
  1391. if (debugging_boolsrch)
  1392. fprintf (aa_stderr, PROGNAME"432 bitvecs[] realloc %lu-->%lu.\n",
  1393. (unsigned long) bitvec_allocsz, (unsigned long) allocsz_needed);
  1394. bitvec_allocsz = allocsz_needed;
  1395. }
  1396. /* Clear all bitvecs to zero and assign them */
  1397. memset (bitvec_allocp, 0, allocsz_needed);
  1398. for (i = 0; i < saveusr.stemcount; i++)
  1399. bitvecs[i] = bitvec_allocp + (i * bitveclen);
  1400. result_bitvec = bitvec_allocp + (i * bitveclen);
  1401. /* If sorting statistically, allocate weight vector.
  1402. * One float for each db record.
  1403. */
  1404. if (wtvec) {
  1405. free (wtvec);
  1406. wtvec = NULL;
  1407. }
  1408. if (do_stat_sort) {
  1409. wtvec = austext_malloc ((tot_addr_count + 4) * sizeof(float) + 4,
  1410. PROGNAME"040", NULL);
  1411. memset (wtvec, 0, (tot_addr_count + 4) * sizeof(float));
  1412. }
  1413. /* The 'zero permute' is every record that has
  1414. * NONE of the query terms in it. It can only be
  1415. * generated if a NOT operator was included in the query.
  1416. */
  1417. if (need_zero_permute) {
  1418. sprintf (msgbuf, CATGETS(dtsearch_catd, MS_boolsrch, 15,
  1419. "%s Your query requires retrieving every\n"
  1420. "document in the database that does not have any of\n"
  1421. "your query words. This type of search may take an\n"
  1422. "unusually long time."),
  1423. PROGNAME"1536");
  1424. DtSearchAddMessage (msgbuf);
  1425. }
  1426. if (debugging_boolsrch)
  1427. fflush (aa_stderr);
  1428. /* Searches may take a long time. To allow gui to put a
  1429. * a 'working' dialog box and a 'cancel' button,
  1430. * we pass execution to workprocs.
  1431. * If user cannot cancel search no matter how
  1432. * long it may take, we call each of the subsequent
  1433. * workproc functions directly from here.
  1434. * Otherwise they will themselves setup each
  1435. * subsequent call to usrblk.workproc(), as long as
  1436. * the previous call returns OE_SEARCHING and the user
  1437. * hasn't pushed USR_STOPSRCH.
  1438. */
  1439. usrblk.workproc = read_stem_bitvec_WK;
  1440. save_stemno = 0; /* global arg for first workproc */
  1441. usrblk.workproc(); /* direct call to first workproc */
  1442. if ((usrblk.flags & USR_NO_ITERATE) != 0 &&
  1443. (usrblk.debug & USRDBG_ITERATE) == 0) {
  1444. while (usrblk.retncode == OE_SEARCHING)
  1445. usrblk.workproc();
  1446. }
  1447. return;
  1448. } /* boolean_search() */
  1449. /************************** BOOLSRCH.C **********************/