3 # FILE: SearchEngine.php
5 # Open Source Metadata Archive Search Engine (OSMASE)
6 # Copyright 2002-2014 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
15 # ---- PUBLIC INTERFACE --------------------------------------------------
17 # possible types of logical operators
21 # flags used for indicating field types
27 # flags used for indicating word states
40 # create database object for our use
43 # save item access parameters
47 # set default debug state
60 $FieldName, $FieldType, $Weight, $UsedInKeywordSearch)
63 $this->FieldInfo[$FieldName][
"FieldType"] = $FieldType;
64 $this->FieldInfo[$FieldName][
"Weight"] = $Weight;
65 $this->FieldInfo[$FieldName][
"InKeywordSearch"] =
66 $UsedInKeywordSearch ? TRUE : FALSE;
76 return $this->FieldInfo[$FieldName][
"FieldType"];
86 return $this->FieldInfo[$FieldName][
"Weight"];
96 return $this->FieldInfo[$FieldName][
"InKeywordSearch"];
109 # ---- search functions
127 function Search($SearchString, $StartingResult = 0, $NumberOfResults = 10,
128 $SortByField = NULL, $SortDescending = TRUE)
130 # pass off the request to grouped search (for now) if appropriate
134 $NumberOfResults, $SortByField, $SortDescending);
137 # interpret and filter out magic debugging keyword (if any)
138 $SearchString = $this->SetDebugLevel($SearchString);
139 $this->
DMsg(0,
"In Search() with search string \"".$SearchString.
"\"");
141 # save start time to use in calculating search time
142 $StartTime = microtime(TRUE);
145 $this->InclusiveTermCount = 0;
146 $this->RequiredTermCount = 0;
147 $this->ExcludedTermCount = 0;
149 # parse search string into terms
150 $Words = $this->ParseSearchStringForWords($SearchString);
151 $this->
DMsg(1,
"Found ".count($Words).
" words");
153 # parse search string for phrases
154 $Phrases = $this->ParseSearchStringForPhrases($SearchString);
155 $this->
DMsg(1,
"Found ".count($Phrases).
" phrases");
157 # if only excluded terms specified
158 if ($this->ExcludedTermCount && !$this->InclusiveTermCount)
161 $this->
DMsg(1,
"Loading all records");
162 $Scores = $this->LoadScoresForAllRecords();
167 $Scores = $this->SearchForWords($Words);
168 $this->
DMsg(1,
"Found ".count($Scores).
" results after word search");
169 $Scores = $this->SearchForPhrases($Phrases, $Scores);
170 $this->
DMsg(1,
"Found ".count($Scores).
" results after phrase search");
173 # if search results found
174 if (count($Scores) > 0)
176 # handle any excluded words
177 $Scores = $this->FilterOnExcludedWords($Words, $Scores);
179 # strip off any results that don't contain required words
180 $Scores = $this->FilterOnRequiredWords($Scores);
183 # count, sort, and trim search result scores list
184 $Scores = $this->CleanScores($Scores, $StartingResult, $NumberOfResults,
185 $SortByField, $SortDescending);
188 $this->LastSearchTime = microtime(TRUE) - $StartTime;
190 # return list of items to caller
191 $this->
DMsg(0,
"Ended up with ".$this->NumberOfResultsAvailable.
" results");
214 function FieldedSearch($SearchStrings, $StartingResult = 0, $NumberOfResults = 10,
215 $SortByField = NULL, $SortDescending = TRUE)
217 # pass off the request to grouped search (for now) if appropriate
221 $NumberOfResults, $SortByField, $SortDescending);
224 # interpret and filter out magic debugging keyword (if any)
225 $SearchStrings = $this->SetDebugLevel($SearchStrings);
226 $this->
DMsg(0,
"In FieldedSearch() with "
227 .count($SearchStrings).
" search strings");
229 # save start time to use in calculating search time
230 $StartTime = microtime(TRUE);
233 $Scores = $this->SearchAcrossFields($SearchStrings);
234 $Scores = ($Scores === NULL) ? array() : $Scores;
236 # count, sort, and trim search result scores list
237 $Scores = $this->CleanScores($Scores, $StartingResult, $NumberOfResults,
238 $SortByField, $SortDescending);
241 $this->LastSearchTime = microtime(TRUE) - $StartTime;
243 # return list of items to caller
244 $this->
DMsg(0,
"Ended up with ".$this->NumberOfResultsAvailable.
" results");
267 function GroupedSearch($SearchGroups, $StartingResult = 0, $NumberOfResults = 10,
268 $SortByField = NULL, $SortDescending = TRUE)
270 # if search parameter set was passed in
273 # convert search groups to legacy format
274 $SearchGroups = $SearchGroups->GetAsLegacyArray();
276 # retrieve base logic from legacy format
277 $BaseLogic = $SearchGroups[
"Logic"];
278 unset($SearchGroups[
"Logic"]);
282 # use default base logic
286 # interpret and filter out magic debugging keyword (if any)
287 foreach ($SearchGroups as $Index => $Groups)
289 if (isset($SearchGroups[$Index][
"SearchStrings"]))
291 $SearchGroups[$Index][
"SearchStrings"] =
292 $this->SetDebugLevel($SearchGroups[$Index][
"SearchStrings"]);
295 $this->
DMsg(0,
"In GroupedSearch() with "
296 .count($SearchGroups).
" search groups");
298 # save start time to use in calculating search time
299 $StartTime = microtime(TRUE);
301 # start with no results
304 # save AND/OR search setting
307 # for each search group
309 foreach ($SearchGroups as $Group)
311 $this->
DMsg(0,
"----- GROUP ---------------------------");
313 # if group has AND/OR setting specified
314 if (isset($Group[
"Logic"]))
316 # use specified AND/OR setting
321 # use saved AND/OR setting
324 $this->
DMsg(2,
"Logic is "
327 # if we have search strings for this group
328 if (isset($Group[
"SearchStrings"]))
331 $GroupScores = $this->SearchAcrossFields($Group[
"SearchStrings"]);
333 # if search was conducted
334 if ($GroupScores !== NULL)
336 # if base AND/OR setting is OR or this is first search
337 if (($BaseLogic == self::LOGIC_OR) || $FirstSearch)
339 # add search results to result list
340 foreach ($GroupScores as $ItemId => $Score)
342 if (isset($Scores[$ItemId]))
344 $Scores[$ItemId] += $Score;
348 $Scores[$ItemId] = $Score;
352 # (reset flag indicating first search)
353 $FirstSearch = FALSE;
357 # AND search results with previous results
358 $OldScores = $Scores;
360 foreach ($GroupScores as $ItemId => $Score)
362 if (isset($OldScores[$ItemId]))
364 $Scores[$ItemId] = $OldScores[$ItemId] + $Score;
372 # restore AND/OR search setting
375 # count, sort, and trim search result scores list
376 $Scores = $this->CleanScores($Scores, $StartingResult, $NumberOfResults,
377 $SortByField, $SortDescending);
380 $this->LastSearchTime = microtime(TRUE) - $StartTime;
382 # return search results to caller
383 $this->
DMsg(0,
"Ended up with ".$this->NumberOfResultsAvailable.
" results");
393 # save filter function name
394 $this->FilterFuncs[] = $FunctionName;
404 if ($NewSetting != NULL)
436 return $this->SearchTermList;
458 $IncludedKeywordSearch = FALSE;
459 foreach ($SearchStrings as $FieldName => $SearchStringArray)
461 if ($FieldName ==
"XXXKeywordXXX")
463 $IncludedKeywordSearch = TRUE;
467 if (array_key_exists($FieldName, $this->FieldInfo))
469 $Weight += $this->FieldInfo[$FieldName][
"Weight"];
473 if ($IncludedKeywordSearch)
475 foreach ($this->FieldInfo as $FieldName => $Info)
477 if ($Info[
"InKeywordSearch"])
479 $Weight += $Info[
"Weight"];
487 # ---- search database update functions
496 # clear word count added flags for this item
497 unset($this->WordCountAdded);
499 # delete any existing info for this item
500 $this->DB->Query(
"DELETE FROM SearchWordCounts WHERE ItemId = ".$ItemId);
501 $this->DB->Query(
"DELETE FROM SearchItemTypes WHERE ItemId = ".$ItemId);
504 $this->DB->Query(
"INSERT INTO SearchItemTypes (ItemId, ItemType)"
505 .
" VALUES (".intval($ItemId).
", ".intval($ItemType).
")");
507 # for each metadata field
508 foreach ($this->FieldInfo as $FieldName => $Info)
510 # if search weight for field is positive
511 if ($Info[
"Weight"] > 0)
513 # retrieve text for field
519 # for each text string in array
520 foreach ($Text as $String)
522 # record search info for text
523 $this->RecordSearchInfoForText($ItemId, $FieldName,
524 $Info[
"Weight"], $String,
525 $Info[
"InKeywordSearch"]);
530 # record search info for text
531 $this->RecordSearchInfoForText($ItemId, $FieldName,
532 $Info[
"Weight"], $Text,
533 $Info[
"InKeywordSearch"]);
547 # retrieve IDs for specified number of items starting at specified ID
548 $this->DB->Query(
"SELECT ".$this->ItemIdFieldName.
" FROM ".$this->ItemTableName
549 .
" WHERE ".$this->ItemIdFieldName.
" >= ".$StartingItemId
550 .
" ORDER BY ".$this->ItemIdFieldName.
" LIMIT ".$NumberOfItems);
551 $ItemIds = $this->DB->FetchColumn($this->ItemIdFieldName);
553 # for each retrieved item ID
554 foreach ($ItemIds as $ItemId)
556 # update search info for item
560 # return ID of last item updated to caller
570 # drop all entries pertaining to item from word count table
571 $this->DB->Query(
"DELETE FROM SearchWordCounts WHERE ItemId = ".$ItemId);
572 $this->DB->Query(
"DELETE FROM SearchItemTypes WHERE ItemId = ".$ItemId);
581 # retrieve our ID for field
582 $FieldId = $this->DB->Query(
"SELECT FieldId FROM SearchFields "
583 .
"WHERE FieldName = '".addslashes($FieldName).
"'",
"FieldId");
585 # drop all entries pertaining to field from word counts table
586 $this->DB->Query(
"DELETE FROM SearchWordCounts WHERE FieldId = \'".$FieldId.
"\'");
588 # drop field from our fields table
589 $this->DB->Query(
"DELETE FROM SearchFields WHERE FieldId = \'".$FieldId.
"\'");
598 return $this->DB->Query(
"SELECT COUNT(*) AS TermCount"
599 .
" FROM SearchWords",
"TermCount");
608 return $this->DB->Query(
"SELECT COUNT(DISTINCT ItemId) AS ItemCount"
609 .
" FROM SearchWordCounts",
"ItemCount");
621 # asssume no synonyms will be added
625 $WordId = $this->GetWordId($Word, TRUE);
627 # for each synonym passed in
628 foreach ($Synonyms as $Synonym)
631 $SynonymId = $this->GetWordId($Synonym, TRUE);
633 # if synonym is not already in database
634 $this->DB->Query(
"SELECT * FROM SearchWordSynonyms"
635 .
" WHERE (WordIdA = ".$WordId
636 .
" AND WordIdB = ".$SynonymId.
")"
637 .
" OR (WordIdB = ".$WordId
638 .
" AND WordIdA = ".$SynonymId.
")");
639 if ($this->DB->NumRowsSelected() == 0)
641 # add synonym entry to database
642 $this->DB->Query(
"INSERT INTO SearchWordSynonyms"
643 .
" (WordIdA, WordIdB)"
644 .
" VALUES (".$WordId.
", ".$SynonymId.
")");
649 # report to caller number of new synonyms added
662 $WordId = $this->GetWordId($Word);
665 if ($WordId !== NULL)
667 # if no specific synonyms provided
668 if ($Synonyms === NULL)
670 # remove all synonyms for word
671 $this->DB->Query(
"DELETE FROM SearchWordSynonyms"
672 .
" WHERE WordIdA = '".$WordId.
"'"
673 .
" OR WordIdB = '".$WordId.
"'");
677 # for each specified synonym
678 foreach ($Synonyms as $Synonym)
680 # look up ID for synonym
681 $SynonymId = $this->GetWordId($Synonym);
683 # if synonym ID was found
684 if ($SynonymId !== NULL)
686 # delete synonym entry
687 $this->DB->Query(
"DELETE FROM SearchWordSynonyms"
688 .
" WHERE (WordIdA = '".$WordId.
"'"
689 .
" AND WordIdB = '".$SynonymId.
"')"
690 .
" OR (WordIdB = '".$WordId.
"'"
691 .
" AND WordIdA = '".$SynonymId.
"')");
703 $this->DB->Query(
"DELETE FROM SearchWordSynonyms");
713 # assume no synonyms will be found
716 # look up ID for word
717 $WordId = $this->GetWordId($Word);
719 # if word ID was found
720 if ($WordId !== NULL)
722 # look up IDs of all synonyms for this word
723 $this->DB->Query(
"SELECT WordIdA, WordIdB FROM SearchWordSynonyms"
724 .
" WHERE WordIdA = ".$WordId
725 .
" OR WordIdB = ".$WordId);
726 $SynonymIds = array();
727 while ($Record = $this->DB->FetchRow)
729 $SynonymIds[] = ($Record[
"WordIdA"] == $WordId)
730 ? $Record[
"WordIdB"] : $Record[
"WordIdA"];
733 # for each synonym ID
734 foreach ($SynonymIds as $SynonymId)
736 # look up synonym word and add to synonym list
737 $Synonyms[] = $this->GetWord($SynonymId);
741 # return synonyms to caller
751 # assume no synonyms will be found
752 $SynonymList = array();
754 # for each synonym ID pair
756 $OurDB->Query(
"SELECT WordIdA, WordIdB FROM SearchWordSynonyms");
757 while ($Record = $OurDB->FetchRow())
760 $Word = $this->GetWord($Record[
"WordIdA"]);
761 $Synonym = $this->GetWord($Record[
"WordIdB"]);
763 # if we do not already have an entry for the word
764 # or synonym is not listed for this word
765 if (!isset($SynonymList[$Word])
766 || !in_array($Synonym, $SynonymList[$Word]))
768 # add entry for synonym
769 $SynonymList[$Word][] = $Synonym;
772 # if we do not already have an entry for the synonym
773 # or word is not listed for this synonym
774 if (!isset($SynonymList[$Synonym])
775 || !in_array($Word, $SynonymList[$Synonym]))
778 $SynonymList[$Synonym][] = $Word;
783 # (this loop removes reciprocal duplicates)
784 foreach ($SynonymList as $Word => $Synonyms)
786 # for each synonym for that word
787 foreach ($Synonyms as $Synonym)
789 # if synonym has synonyms and word is one of them
790 if (isset($SynonymList[$Synonym])
791 && isset($SynonymList[$Word])
792 && in_array($Word, $SynonymList[$Synonym])
793 && in_array($Synonym, $SynonymList[$Word]))
795 # if word has less synonyms than synonym
796 if (count($SynonymList[$Word])
797 < count($SynonymList[$Synonym]))
799 # remove synonym from synonym list for word
800 $SynonymList[$Word] = array_diff(
801 $SynonymList[$Word], array($Synonym));
803 # if no synonyms left for word
804 if (!count($SynonymList[$Word]))
806 # remove empty synonym list for word
807 unset($SynonymList[$Word]);
812 # remove word from synonym list for synonym
813 $SynonymList[$Synonym] = array_diff(
814 $SynonymList[$Synonym], array($Word));
816 # if no synonyms left for word
817 if (!count($SynonymList[$Synonym]))
819 # remove empty synonym list for word
820 unset($SynonymList[$Synonym]);
827 # sort array alphabetically (just for convenience)
828 foreach ($SynonymList as $Word => $Synonyms)
830 asort($SynonymList[$Word]);
834 # return 2D array of synonyms to caller
845 # remove all existing synonyms
848 # for each synonym entry passed in
849 foreach ($SynonymList as $Word => $Synonyms)
851 # add synonyms for word
866 # asssume no synonyms will be added
869 # read in contents of file
870 $Lines = file($FileName, FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
872 # if file contained lines
875 # for each line of file
876 foreach ($Lines as $Line)
878 # if line is not a comment
879 if (!preg_match(
"/[\s]*#/", $Line))
881 # split line into words
882 $Words = preg_split(
"/[\s,]+/", $Line);
885 if (count($Words) > 1)
887 # separate out word and synonyms
888 $Word = array_shift($Words);
897 # return count of synonyms added to caller
902 # ---- PRIVATE INTERFACE -------------------------------------------------
915 private $WordCountAdded;
918 private $RequiredTermCount;
919 private $RequiredTermCounts;
920 private $InclusiveTermCount;
921 private $ExcludedTermCount;
922 private $SearchTermList;
927 # ---- common private functions (used in both searching and DB build)
938 private function ParseSearchStringForWords($SearchString, $IgnorePhrases = FALSE)
940 # strip off any surrounding whitespace
941 $Text = trim($SearchString);
943 # set up normalization replacement strings
945 "/'s[^a-z0-9\\-+~]+/i", #
get rid of possessive plurals
946 "/'/", #
get rid of single quotes / apostrophes
947 "/\"[^\"]*\"/", #
get rid of phrases (NOTE: HARD-CODED
949 "/\\([^)]*\\)/
", # get rid of groups (NOTE: HARD-CODED
951 "/[^a-z0-9\\-+~]+/i
", # convert non-alphanumerics
952 # / non-minus/plus to a space
953 "/([^\\s])-+/i
", # convert minus preceded by anything
954 # but whitespace to a space
955 "/([^\\s])\\++/i
", # convert plus preceded by anything
956 # but whitespace to a space
957 "/-\\s/i
", # convert minus followed by whitespace to a space
958 "/\\+\\s/i
", # convert plus followed by whitespace to a space
959 "/~\\s/i
", # convert tilde followed by whitespace to a space
960 "/[ ]+/
" # convert multiple spaces to one space
962 $Replacements = array(
976 # if we are supposed to ignore phrases and groups (series of words
977 # in quotes or surrounded by parens)
980 # switch phrase removal to double quote removal (HARD-CODED
981 # INDEX INTO PATTERN LIST!!)
982 $Patterns[2] = "/\
"/";
984 # switch group removal to paren removal (HARD-CODED INDEX
985 # INTO PATTERN LIST!!)
986 $Patterns[3] =
"/[\(\)]+/";
989 # remove punctuation from text and normalize whitespace
990 $Text = preg_replace($Patterns, $Replacements, $Text);
991 $this->
DMsg(2,
"Normalized search string is '".$Text.
"'");
993 # convert text to lower case
994 $Text = strtolower($Text);
996 # strip off any extraneous whitespace
999 # start with an empty array
1002 # if we have no words left after parsing
1003 if (strlen($Text) != 0)
1006 foreach (explode(
" ", $Text) as $Word)
1008 # grab first character of word
1009 $FirstChar = substr($Word, 0, 1);
1011 # strip off option characters and set flags appropriately
1012 $Flags = self::WORD_PRESENT;
1013 if ($FirstChar ==
"-")
1015 $Word = substr($Word, 1);
1016 $Flags |= self::WORD_EXCLUDED;
1017 if (!isset($Words[$Word]))
1019 $this->ExcludedTermCount++;
1024 if ($FirstChar ==
"~")
1026 $Word = substr($Word, 1);
1029 || ($FirstChar ==
"+"))
1031 if ($FirstChar ==
"+")
1033 $Word = substr($Word, 1);
1035 $Flags |= self::WORD_REQUIRED;
1036 if (!isset($Words[$Word]))
1038 $this->RequiredTermCount++;
1041 if (!isset($Words[$Word]))
1043 $this->InclusiveTermCount++;
1044 $this->SearchTermList[] = $Word;
1048 # store flags to indicate word found
1049 $Words[$Word] = $Flags;
1050 $this->
DMsg(3,
"Word identified (".$Word.
")");
1054 # return normalized words to caller
1064 private function GetFieldId($FieldName)
1066 # if field ID is not in cache
1067 if (!isset($this->FieldIds[$FieldName]))
1069 # look up field info in database
1070 $this->DB->Query(
"SELECT FieldId FROM SearchFields "
1071 .
"WHERE FieldName = '".addslashes($FieldName).
"'");
1073 # if field was found
1074 if ($Record = $this->DB->FetchRow())
1076 # load info from DB record
1077 $FieldId = $Record[
"FieldId"];
1081 # add field to database
1082 $this->DB->Query(
"INSERT INTO SearchFields (FieldName) "
1083 .
"VALUES ('".addslashes($FieldName).
"')");
1085 # retrieve ID for newly added field
1086 $FieldId = $this->DB->LastInsertId();
1090 $this->FieldIds[$FieldName] = $FieldId;
1093 # return cached ID to caller
1094 return $this->FieldIds[$FieldName];
1104 private function GetWordId($Word, $AddIfNotFound = FALSE)
1106 static $WordIdCache;
1108 # if word was in ID cache
1109 if (isset($WordIdCache[$Word]))
1112 $WordId = $WordIdCache[$Word];
1116 # look up ID in database
1117 $WordId = $this->DB->Query(
"SELECT WordId"
1118 .
" FROM SearchWords"
1119 .
" WHERE WordText='".addslashes($Word).
"'",
1122 # if ID was not found and caller requested it be added
1123 if (($WordId === NULL) && $AddIfNotFound)
1125 # add word to database
1126 $this->DB->Query(
"INSERT INTO SearchWords (WordText)"
1127 .
" VALUES ('".addslashes(strtolower($Word)).
"')");
1129 # get ID for newly added word
1130 $WordId = $this->DB->LastInsertId();
1134 $WordIdCache[$Word] = $WordId;
1137 # return ID to caller
1148 private function GetStemId($Stem, $AddIfNotFound = FALSE)
1150 static $StemIdCache;
1152 # if stem was in ID cache
1153 if (isset($StemIdCache[$Stem]))
1156 $StemId = $StemIdCache[$Stem];
1160 # look up ID in database
1161 $StemId = $this->DB->Query(
"SELECT WordId"
1162 .
" FROM SearchStems"
1163 .
" WHERE WordText='".addslashes($Stem).
"'",
1166 # if ID was not found and caller requested it be added
1167 if (($StemId === NULL) && $AddIfNotFound)
1169 # add stem to database
1170 $this->DB->Query(
"INSERT INTO SearchStems (WordText)"
1171 .
" VALUES ('".addslashes(strtolower($Stem)).
"')");
1173 # get ID for newly added stem
1174 $StemId = $this->DB->LastInsertId();
1177 # adjust from DB ID value to stem ID value
1178 $StemId += self::STEM_ID_OFFSET;
1181 $StemIdCache[$Stem] = $StemId;
1184 # return ID to caller
1193 private function GetWord($WordId)
1197 # if word was in cache
1198 if (isset($WordCache[$WordId]))
1200 # use word from cache
1201 $Word = $WordCache[$WordId];
1205 # adjust search location and word ID if word is stem
1206 $TableName =
"SearchWords";
1207 if ($WordId >= self::STEM_ID_OFFSET)
1209 $TableName =
"SearchStems";
1210 $WordId -= self::STEM_ID_OFFSET;
1213 # look up word in database
1214 $Word = $this->DB->Query(
"SELECT WordText"
1215 .
" FROM ".$TableName
1216 .
" WHERE WordId='".$WordId.
"'",
1219 # save word to cache
1220 $WordCache[$WordId] = $Word;
1223 # return word to caller
1228 # ---- private functions used in searching
1238 private function SearchAcrossFields($SearchStrings)
1240 # start by assuming no search will be done
1244 $this->InclusiveTermCount = 0;
1245 $this->RequiredTermCount = 0;
1246 $this->ExcludedTermCount = 0;
1249 $NeedComparisonSearch = FALSE;
1250 foreach ($SearchStrings as $FieldName => $SearchStringArray)
1252 # convert search string to array if needed
1253 if (!is_array($SearchStringArray))
1255 $SearchStringArray = array($SearchStringArray);
1258 # for each search string for this field
1259 foreach ($SearchStringArray as $SearchString)
1261 # if field is keyword or field is text and does not look
1262 # like comparison match
1263 $NotComparisonSearch = !preg_match(
"/^[><!]=./", $SearchString)
1264 && !preg_match(
"/^[><=]./", $SearchString);
1265 if (($FieldName ==
"XXXKeywordXXX")
1266 || (isset($this->FieldInfo[$FieldName])
1267 && ($this->FieldInfo[$FieldName][
"FieldType"]
1268 == self::FIELDTYPE_TEXT)
1269 && $NotComparisonSearch))
1271 $this->DMsg(0,
"Searching text field \""
1272 .$FieldName.
"\" for string \"$SearchString\"");
1274 # normalize text and split into words
1275 $Words[$FieldName] =
1276 $this->ParseSearchStringForWords($SearchString);
1278 # calculate scores for matching items
1279 if (count($Words[$FieldName]))
1281 $Scores = $this->SearchForWords(
1282 $Words[$FieldName], $FieldName, $Scores);
1283 $this->DMsg(3,
"Have "
1284 .count($Scores).
" results after word search");
1287 # split into phrases
1288 $Phrases[$FieldName] =
1289 $this->ParseSearchStringForPhrases($SearchString);
1291 # handle any phrases
1292 if (count($Phrases[$FieldName]))
1294 $Scores = $this->SearchForPhrases(
1295 $Phrases[$FieldName], $Scores, $FieldName, TRUE, FALSE);
1296 $this->DMsg(3,
"Have "
1297 .count($Scores).
" results after phrase search");
1302 # set flag to indicate possible comparison search candidate found
1303 $NeedComparisonSearch = TRUE;
1308 # perform comparison searches
1309 if ($NeedComparisonSearch)
1311 $Scores = $this->SearchForComparisonMatches($SearchStrings, $Scores);
1312 $this->DMsg(3,
"Have ".count($Scores).
" results after comparison search");
1315 # if no results found and exclusions specified
1316 if (!count($Scores) && $this->ExcludedTermCount)
1319 $Scores = $this->LoadScoresForAllRecords();
1322 # if search results found
1325 # for each search text string
1326 foreach ($SearchStrings as $FieldName => $SearchStringArray)
1328 # convert search string to array if needed
1329 if (!is_array($SearchStringArray))
1331 $SearchStringArray = array($SearchStringArray);
1334 # for each search string for this field
1335 foreach ($SearchStringArray as $SearchString)
1338 if (($FieldName ==
"XXXKeywordXXX")
1339 || (isset($this->FieldInfo[$FieldName])
1340 && ($this->FieldInfo[$FieldName][
"FieldType"]
1341 == self::FIELDTYPE_TEXT)))
1343 # if there are words in search text
1344 if (isset($Words[$FieldName]))
1346 # handle any excluded words
1347 $Scores = $this->FilterOnExcludedWords(
1348 $Words[$FieldName], $Scores, $FieldName);
1351 # handle any excluded phrases
1352 if (isset($Phrases[$FieldName]))
1354 $Scores = $this->SearchForPhrases(
1355 $Phrases[$FieldName], $Scores,
1356 $FieldName, FALSE, TRUE);
1362 # strip off any results that don't contain required words
1363 $Scores = $this->FilterOnRequiredWords($Scores);
1366 # return search result scores to caller
1380 private function SearchForWords(
1381 $Words, $FieldName =
"XXXKeywordXXX", $Scores = NULL)
1385 # start with empty search result scores list if none passed in
1386 if ($Scores == NULL)
1392 $FieldId = $this->GetFieldId($FieldName);
1395 foreach ($Words as $Word => $Flags)
1398 $this->DMsg(2,
"Searching for word '${Word}' in field ".$FieldName);
1400 # if word is not excluded
1401 if (!($Flags & self::WORD_EXCLUDED))
1403 # look up record ID for word
1404 $this->DMsg(2,
"Looking up word \"".$Word.
"\"");
1405 $WordId = $this->GetWordId($Word);
1408 if ($WordId !== NULL)
1410 # look up counts for word
1411 $DB->Query(
"SELECT ItemId,Count FROM SearchWordCounts "
1412 .
"WHERE WordId = ".$WordId
1413 .
" AND FieldId = ".$FieldId);
1414 $Counts = $DB->FetchColumn(
"Count",
"ItemId");
1416 # if synonym support is enabled
1417 if ($this->SynonymsEnabled)
1419 # look for any synonyms
1420 $DB->Query(
"SELECT WordIdA, WordIdB"
1421 .
" FROM SearchWordSynonyms"
1422 .
" WHERE WordIdA = ".$WordId
1423 .
" OR WordIdB = ".$WordId);
1425 # if synonyms were found
1426 if ($DB->NumRowsSelected())
1428 # retrieve synonym IDs
1429 $SynonymIds = array();
1430 while ($Record = $DB->FetchRow())
1432 $SynonymIds[] = ($Record[
"WordIdA"] == $WordId)
1433 ? $Record[
"WordIdB"]
1434 : $Record[
"WordIdA"];
1438 foreach ($SynonymIds as $SynonymId)
1440 # retrieve counts for synonym
1441 $DB->Query(
"SELECT ItemId,Count"
1442 .
" FROM SearchWordCounts"
1443 .
" WHERE WordId = ".$SynonymId
1444 .
" AND FieldId = ".$FieldId);
1445 $SynonymCounts = $DB->FetchColumn(
"Count",
"ItemId");
1448 foreach ($SynonymCounts as $ItemId => $Count)
1450 # adjust count because it's a synonym
1451 $AdjustedCount = ceil($Count / 2);
1453 # add count to existing counts
1454 if (isset($Counts[$ItemId]))
1456 $Counts[$ItemId] += $AdjustedCount;
1460 $Counts[$ItemId] = $AdjustedCount;
1468 # if stemming is enabled
1469 if ($this->StemmingEnabled)
1472 $Stem = PorterStemmer::Stem($Word);
1473 $this->DMsg(2,
"Looking up stem \"".$Stem.
"\"");
1474 $StemId = $this->GetStemId($Stem);
1476 # if ID found for stem
1477 if ($StemId !== NULL)
1479 # retrieve counts for stem
1480 $DB->Query(
"SELECT ItemId,Count"
1481 .
" FROM SearchWordCounts"
1482 .
" WHERE WordId = ".$StemId
1483 .
" AND FieldId = ".$FieldId);
1484 $StemCounts = $DB->FetchColumn(
"Count",
"ItemId");
1487 foreach ($StemCounts as $ItemId => $Count)
1489 # adjust count because it's a stem
1490 $AdjustedCount = ceil($Count / 2);
1492 # add count to existing counts
1493 if (isset($Counts[$ItemId]))
1495 $Counts[$ItemId] += $AdjustedCount;
1499 $Counts[$ItemId] = $AdjustedCount;
1505 # if counts were found
1509 foreach ($Counts as $ItemId => $Count)
1511 # if word flagged as required
1512 if ($Flags & self::WORD_REQUIRED)
1514 # increment required word count for record
1515 if (isset($this->RequiredTermCounts[$ItemId]))
1517 $this->RequiredTermCounts[$ItemId]++;
1521 $this->RequiredTermCounts[$ItemId] = 1;
1525 # add to item record score
1526 if (isset($Scores[$ItemId]))
1528 $Scores[$ItemId] += $Count;
1532 $Scores[$ItemId] = $Count;
1539 # return basic scores to caller
1549 private function ParseSearchStringForPhrases($SearchString)
1551 # split into chunks delimited by double quote marks
1552 $Pieces = explode(
"\"", $SearchString); #
"
1554 # for each pair of chunks
1557 while ($Index < count($Pieces))
1559 # grab phrase from chunk
1560 $Phrase = trim(addslashes($Pieces[$Index - 1]));
1561 $Flags = self::WORD_PRESENT;
1563 # grab first character of phrase
1564 $FirstChar = substr($Pieces[$Index - 2], -1);
1566 # set flags to reflect any option characters
1567 if ($FirstChar == "-
")
1569 $Flags |= self::WORD_EXCLUDED;
1570 if (!isset($Phrases[$Phrase]))
1572 $this->ExcludedTermCount++;
1577 if ((($this->DefaultSearchLogic == self::LOGIC_AND)
1578 && ($FirstChar != "~
"))
1579 || ($FirstChar == "+
"))
1581 $Flags |= self::WORD_REQUIRED;
1582 if (!isset($Phrases[$Phrase]))
1584 $this->RequiredTermCount++;
1587 if (!isset($Phrases[$Phrase]))
1589 $this->InclusiveTermCount++;
1590 $this->SearchTermList[] = $Phrase;
1593 $Phrases[$Phrase] = $Flags;
1595 # move to next pair of chunks
1599 # return phrases to caller
1603 protected function SearchFieldForPhrases($FieldName, $Phrase)
1606 exit("<br>SE - ERROR: SearchFieldForPhrases() not implemented<br>\n");
1609 private function SearchForPhrases($Phrases, $Scores, $FieldName = "XXXKeywordXXX",
1610 $ProcessNonExcluded = TRUE, $ProcessExcluded = TRUE)
1612 # if phrases are found
1613 if (count($Phrases) > 0)
1615 # if this is a keyword search
1616 if ($FieldName ==
"XXXKeywordXXX")
1619 foreach ($this->FieldInfo as $KFieldName => $Info)
1621 # if field is marked to be included in keyword searches
1622 if ($Info[
"InKeywordSearch"])
1624 # call ourself with that field
1625 $Scores = $this->SearchForPhrases(
1626 $Phrases, $Scores, $KFieldName,
1627 $ProcessNonExcluded, $ProcessExcluded);
1634 foreach ($Phrases as $Phrase => $Flags)
1636 $this->DMsg(2,
"Searching for phrase '".$Phrase
1637 .
"' in field ".$FieldName);
1639 # if phrase flagged as excluded and we are doing excluded
1640 # phrases or phrase flagged as non-excluded and we
1641 # are doing non-excluded phrases
1642 if (($ProcessExcluded && ($Flags & self::WORD_EXCLUDED))
1643 || ($ProcessNonExcluded && !($Flags & self::WORD_EXCLUDED)))
1645 # initialize score list if necessary
1646 if ($Scores === NULL) { $Scores = array(); }
1648 # retrieve list of items that contain phrase
1649 $ItemIds = $this->SearchFieldForPhrases(
1650 $FieldName, $Phrase);
1652 # for each item that contains phrase
1653 foreach ($ItemIds as $ItemId)
1655 # if we are doing excluded phrases and phrase
1656 # flagged as excluded
1657 if ($ProcessExcluded && ($Flags & self::WORD_EXCLUDED))
1659 # knock item off of list
1660 unset($Scores[$ItemId]);
1662 elseif ($ProcessNonExcluded)
1664 # calculate phrase value based on number of
1665 # words and field weight
1666 $PhraseScore = count(preg_split(
"/[\s]+/",
1667 $Phrase, -1, PREG_SPLIT_NO_EMPTY))
1668 * $this->FieldInfo[$FieldName][
"Weight"];
1669 $this->DMsg(2,
"Phrase score is ".$PhraseScore);
1671 # bump up item record score
1672 if (isset($Scores[$ItemId]))
1674 $Scores[$ItemId] += $PhraseScore;
1678 $Scores[$ItemId] = $PhraseScore;
1681 # if phrase flagged as required
1682 if ($Flags & self::WORD_REQUIRED)
1684 # increment required word count for record
1685 if (isset($this->RequiredTermCounts[$ItemId]))
1687 $this->RequiredTermCounts[$ItemId]++;
1691 $this->RequiredTermCounts[$ItemId] = 1;
1701 # return updated scores to caller
1705 private function FilterOnExcludedWords($Words, $Scores, $FieldName =
"XXXKeywordXXX")
1710 $FieldId = $this->GetFieldId($FieldName);
1713 foreach ($Words as $Word => $Flags)
1715 # if word flagged as excluded
1716 if ($Flags & self::WORD_EXCLUDED)
1718 # look up record ID for word
1719 $WordId = $this->GetWordId($Word);
1722 if ($WordId !== NULL)
1724 # look up counts for word
1725 $DB->Query(
"SELECT ItemId FROM SearchWordCounts "
1726 .
"WHERE WordId=${WordId} AND FieldId=${FieldId}");
1729 while ($Record = $DB->FetchRow())
1731 # if item record is in score list
1732 $ItemId = $Record[
"ItemId"];
1733 if (isset($Scores[$ItemId]))
1735 # remove item record from score list
1736 $this->DMsg(3,
"Filtering out item ".$ItemId
1737 .
" because it contained word \"".$Word.
"\"");
1738 unset($Scores[$ItemId]);
1745 # returned filtered score list to caller
1749 private function FilterOnRequiredWords($Scores)
1751 # if there were required words
1752 if ($this->RequiredTermCount > 0)
1755 foreach ($Scores as $ItemId => $Score)
1757 # if item does not meet required word count
1758 if (!isset($this->RequiredTermCounts[$ItemId])
1759 || ($this->RequiredTermCounts[$ItemId]
1760 < $this->RequiredTermCount))
1763 $this->DMsg(4,
"Filtering out item ".$ItemId
1764 .
" because it didn't have required word count of "
1765 .$this->RequiredTermCount
1766 .(isset($this->RequiredTermCounts[$ItemId])
1768 .$this->RequiredTermCounts[$ItemId]
1771 unset($Scores[$ItemId]);
1776 # return filtered list to caller
1780 # count, sort, and trim search result scores list
1781 private function CleanScores($Scores, $StartingResult, $NumberOfResults,
1782 $SortByField, $SortDescending)
1784 # perform any requested filtering
1785 $this->DMsg(0,
"Have ".count($Scores).
" results before filter callbacks");
1786 $Scores = $this->FilterOnSuppliedFunctions($Scores);
1788 # save total number of results available
1789 $this->NumberOfResultsAvailable = count($Scores);
1791 # if no sorting field specified
1792 if ($SortByField === NULL)
1794 # sort result list by score
1795 if ($SortDescending)
1797 arsort($Scores, SORT_NUMERIC);
1801 asort($Scores, SORT_NUMERIC);
1806 # get list of item IDs in sorted order
1807 $SortedIds = $this->GetItemIdsSortedByField(
1808 $SortByField, $SortDescending);
1810 # if we have sorted item IDs
1811 if (count($SortedIds) && count($Scores))
1813 # strip sorted ID list down to those that appear in search results
1814 $SortedIds = array_intersect($SortedIds, array_keys($Scores));
1816 # rebuild score list in sorted order
1817 foreach ($SortedIds as $Id)
1819 $NewScores[$Id] = $Scores[$Id];
1821 $Scores = $NewScores;
1825 # sort result list by score
1826 arsort($Scores, SORT_NUMERIC);
1830 # trim result list to match range requested by caller
1831 $ScoresKeys = array_slice(
1832 array_keys($Scores), $StartingResult, $NumberOfResults);
1833 $TrimmedScores = array();
1834 foreach ($ScoresKeys as $Key) { $TrimmedScores[$Key] = $Scores[$Key]; }
1836 # returned cleaned search result scores list to caller
1837 return $TrimmedScores;
1842 # if filter functions have been set
1843 if (isset($this->FilterFuncs))
1846 foreach ($Scores as $ItemId => $Score)
1848 # for each filter function
1849 foreach ($this->FilterFuncs as $FuncName)
1851 # if filter function return TRUE for item
1852 if (call_user_func($FuncName, $ItemId))
1855 $this->DMsg(2,
"Filter callback <i>".$FuncName
1856 .
"</i> rejected item ".$ItemId);
1857 unset($Scores[$ItemId]);
1859 # bail out of filter func loop
1866 # return filtered list to caller
1870 private function SearchForComparisonMatches($SearchStrings, $Scores)
1874 foreach ($SearchStrings as $SearchFieldName => $SearchStringArray)
1876 # if field is not keyword
1877 if ($SearchFieldName !=
"XXXKeywordXXX")
1879 # convert search string to array if needed
1880 if (!is_array($SearchStringArray))
1882 $SearchStringArray = array($SearchStringArray);
1885 # for each search string for this field
1886 foreach ($SearchStringArray as $SearchString)
1888 # if search string looks like comparison search
1889 $FoundOperator = preg_match(
"/^[><!]=./", $SearchString)
1890 || preg_match(
"/^[><=]./", $SearchString);
1892 || (isset($this->FieldInfo[$SearchFieldName][
"FieldType"])
1893 && ($this->FieldInfo[$SearchFieldName][
"FieldType"]
1894 != self::FIELDTYPE_TEXT)))
1897 $Patterns = array(
"/^[><!]=/",
"/^[><=]/");
1898 $Replacements = array(
"",
"");
1899 $Value = trim(preg_replace(
1900 $Patterns, $Replacements, $SearchString));
1902 # determine and save operator
1903 if (!$FoundOperator)
1905 $Operators[$Index] =
"=";
1909 $Term = trim($SearchString);
1910 $FirstChar = $Term{0};
1911 $FirstTwoChars = $FirstChar.$Term{1};
1912 if ($FirstTwoChars ==
">=")
1913 { $Operators[$Index] =
">="; }
1914 elseif ($FirstTwoChars ==
"<=")
1915 { $Operators[$Index] =
"<="; }
1916 elseif ($FirstTwoChars ==
"!=")
1917 { $Operators[$Index] =
"!="; }
1918 elseif ($FirstChar ==
">")
1919 { $Operators[$Index] =
">"; }
1920 elseif ($FirstChar ==
"<")
1921 { $Operators[$Index] =
"<"; }
1922 elseif ($FirstChar ==
"=")
1923 { $Operators[$Index] =
"="; }
1926 # if operator was found
1927 if (isset($Operators[$Index]))
1930 $Values[$Index] = $Value;
1933 $FieldNames[$Index] = $SearchFieldName;
1934 $this->DMsg(3,
"Added comparison (field = <i>"
1935 .$FieldNames[$Index].
"</i> op = <i>"
1936 .$Operators[$Index].
"</i> val = <i>"
1937 .$Values[$Index].
"</i>)");
1939 # move to next comparison array entry
1947 # if comparisons found
1948 if (isset($Operators))
1950 # perform comparisons on fields and gather results
1951 $Results = $this->SearchFieldsForComparisonMatches(
1952 $FieldNames, $Operators, $Values);
1954 # if search logic is set to AND
1955 if ($this->DefaultSearchLogic == self::LOGIC_AND)
1957 # if results were found
1958 if (count($Results))
1960 # if there were no prior results and no terms for keyword search
1961 if ((count($Scores) == 0) && ($this->InclusiveTermCount == 0))
1963 # add all results to scores
1964 foreach ($Results as $ItemId)
1966 $Scores[$ItemId] = 1;
1971 # remove anything from scores that is not part of results
1972 foreach ($Scores as $ItemId => $Score)
1974 if (in_array($ItemId, $Results) == FALSE)
1976 unset($Scores[$ItemId]);
1989 # add result items to scores
1990 if ($Scores === NULL) { $Scores = array(); }
1991 foreach ($Results as $ItemId)
1993 if (isset($Scores[$ItemId]))
1995 $Scores[$ItemId] += 1;
1999 $Scores[$ItemId] = 1;
2005 # return results to caller
2009 private function SetDebugLevel($SearchStrings)
2011 # if search info is an array
2012 if (is_array($SearchStrings))
2014 # for each array element
2015 foreach ($SearchStrings as $FieldName => $SearchStringArray)
2017 # if element is an array
2018 if (is_array($SearchStringArray))
2020 # for each array element
2021 foreach ($SearchStringArray as $Index => $SearchString)
2023 # pull out search string if present
2024 $SearchStrings[$FieldName][$Index] =
2025 $this->ExtractDebugLevel($SearchString);
2030 # pull out search string if present
2031 $SearchStrings[$FieldName] =
2032 $this->ExtractDebugLevel($SearchStringArray);
2038 # pull out search string if present
2039 $SearchStrings = $this->ExtractDebugLevel($SearchStrings);
2042 # return new search info to caller
2043 return $SearchStrings;
2046 private function ExtractDebugLevel($SearchString)
2048 # if search string contains debug level indicator
2049 if (strstr($SearchString,
"DBUGLVL="))
2051 # remove indicator and set debug level
2052 $Level = preg_replace(
"/^\\s*DBUGLVL=([1-9]{1,2}).*/",
"\\1", $SearchString);
2055 $this->DebugLevel = $Level;
2056 $this->DMsg(0,
"Setting debug level to ".$Level);
2057 $SearchString = preg_replace(
"/DBUGLVL=${Level}/",
"", $SearchString);
2061 # return (possibly) modified search string to caller
2062 return $SearchString;
2065 # load and return search result scores array containing all possible records
2066 private function LoadScoresForAllRecords()
2068 # start with empty list
2072 $this->DB->Query(
"SELECT ".$this->ItemIdFieldName
2073 .
" FROM ".$this->ItemTableName);
2074 while ($Record = $this->DB->FetchRow())
2076 # set score for item to 1
2077 $Scores[$Record[$this->ItemIdFieldName]] = 1;
2080 # return array with all scores to caller
2085 # ---- private functions used in building search database
2094 private function UpdateWordCount($Word, $ItemId, $FieldId, $Weight = 1)
2096 # retrieve ID for word
2097 $WordIds[] = $this->GetWordId($Word, TRUE);
2099 # if stemming is enabled
2100 if ($this->StemmingEnabled)
2102 # retrieve ID for stem of word
2103 $Stem = PorterStemmer::Stem($Word, TRUE);
2104 $WordIds[] = $this->GetStemId($Stem, TRUE);
2107 # for word and stem of word
2108 foreach ($WordIds as $WordId)
2110 # if word count already added to database
2111 if (isset($this->WordCountAdded[$WordId][$FieldId]))
2114 $this->DB->Query(
"UPDATE SearchWordCounts SET Count=Count+".$Weight
2115 .
" WHERE WordId=".$WordId
2116 .
" AND ItemId=".$ItemId
2117 .
" AND FieldId=".$FieldId);
2121 # add word count to DB
2122 $this->DB->Query(
"INSERT INTO SearchWordCounts"
2123 .
" (WordId, ItemId, FieldId, Count) VALUES"
2124 .
" (".$WordId.
", ".$ItemId.
", ".$FieldId.
", ".$Weight.
")");
2126 # remember that we added count for this word
2127 $this->WordCountAdded[$WordId][$FieldId] = TRUE;
2130 # decrease weight for stem
2131 $Weight = ceil($Weight / 2);
2138 exit(
"<br>SE - ERROR: GetFieldContent() not implemented<br>\n");
2141 private function RecordSearchInfoForText(
2142 $ItemId, $FieldName, $Weight, $Text, $IncludeInKeyword)
2145 $Words = $this->ParseSearchStringForWords($Text, TRUE);
2147 # if there was text left after parsing
2148 if (count($Words) > 0)
2151 $FieldId = $this->GetFieldId($FieldName);
2153 # if text should be included in keyword searches
2154 if ($IncludeInKeyword)
2156 # get ID for keyword field
2157 $KeywordFieldId = $this->GetFieldId(
"XXXKeywordXXX");
2161 foreach ($Words as $Word => $Flags)
2163 # update count for word
2164 $this->UpdateWordCount($Word, $ItemId, $FieldId);
2166 # if text should be included in keyword searches
2167 if ($IncludeInKeyword)
2169 # update keyword field count for word
2170 $this->UpdateWordCount(
2171 $Word, $ItemId, $KeywordFieldId, $Weight);
2177 # print debug message if level set high enough
2178 protected function DMsg($Level, $Msg)
2180 if ($this->DebugLevel > $Level)
2182 print(
"SE: ".$Msg.
"<br>\n");
2186 # ---- BACKWARD COMPATIBILITY --------------------------------------------
2188 # possible types of logical operators
2189 const SEARCHLOGIC_AND = 1;
2190 const SEARCHLOGIC_OR = 2;
SearchTermCount()
Get total number of search terms indexed by search engine.
SetAllSynonyms($SynonymList)
Set all synonyms.
DropItem($ItemId)
Drop all data pertaining to item from search database.
DropField($FieldName)
Drop all data pertaining to field from search database.
RemoveSynonyms($Word, $Synonyms=NULL)
Remove synonym(s).
NumberOfResults()
Get number of results found by most recent search.
LoadSynonymsFromFile($FileName)
Load synonyms from a file.
Set of parameters used to perform a search.
__construct($ItemTableName, $ItemIdFieldName)
Object constructor.
SQL database abstraction object with smart query caching.
AddField($FieldName, $FieldType, $Weight, $UsedInKeywordSearch)
Add field to include in searching.
Search($SearchString, $StartingResult=0, $NumberOfResults=10, $SortByField=NULL, $SortDescending=TRUE)
Perform search.
GetAllSynonyms()
Get all synonyms.
SearchTermsRequiredByDefault($NewSetting=TRUE)
Set default search logic.
FilterOnSuppliedFunctions($Scores)
AddSynonyms($Word, $Synonyms)
Add synonyms.
const FIELDTYPE_DATERANGE
UpdateForItem($ItemId, $ItemType=0)
Update search database for the specified item.
SearchTerms()
Get normalized list of search terms.
GroupedSearch($SearchGroups, $StartingResult=0, $NumberOfResults=10, $SortByField=NULL, $SortDescending=TRUE)
Perform search with logical groups of fielded searches.
ItemCount()
Get total number of items indexed by search engine.
FieldedSearch($SearchStrings, $StartingResult=0, $NumberOfResults=10, $SortByField=NULL, $SortDescending=TRUE)
Perform search across multiple fields, with different values or comparisons specified for each field...
FieldWeight($FieldName)
Get search weight for specified field.
FieldInKeywordSearch($FieldName)
Get whether specified field is included in keyword searches.
RemoveAllSynonyms()
Remove all synonyms.
Core metadata archive search engine class.
FieldedSearchWeightScale($SearchStrings)
Get total of weights for all fields involved in search, useful for assessing scale of scores in searc...
$NumberOfResultsAvailable
DefaultSearchLogic($NewSetting=NULL)
Get/set default search logic (LOGIC_AND or LOGIC_OR).
FieldType($FieldName)
Get type of specified field (text/numeric/date/daterange).
DebugLevel($NewValue)
Set debug output level.
UpdateForItems($StartingItemId, $NumberOfItems)
Update search database for the specified range of items.
GetFieldContent($ItemId, $FieldName)
AddResultFilterFunction($FunctionName)
Add function that will be called to filter search results.
SearchTime()
Get time that last search took, in seconds.
GetSynonyms($Word)
Get synonyms for word.