3 # FILE: SPTSearchEngine.php 5 # Part of the Collection Workflow Integration System (CWIS) 6 # Copyright 2011-2016 Edward Almasy and Internet Scout Research Group 7 # http://scout.wisc.edu/cwis/ 17 # pass database handle and config values to real search engine object 18 parent::__construct(
"Resources",
"ResourceId",
"SchemaId");
22 foreach ($Schemas as $SchemaId => $Schema)
24 # for each field defined in schema 26 $Fields = $this->Schemas[$SchemaId]->GetFields();
27 foreach ($Fields as $FieldId => $Field)
29 # save metadata field type 30 $this->FieldTypes[$FieldId] = $Field->Type();
32 # determine field type for searching 33 switch ($Field->Type())
45 $FieldType = self::FIELDTYPE_TEXT;
50 $FieldType = self::FIELDTYPE_NUMERIC;
54 $FieldType = self::FIELDTYPE_DATERANGE;
58 $FieldType = self::FIELDTYPE_DATE;
66 throw new Exception(
"ERROR: unknown field type " 71 if ($FieldType !== NULL)
73 # add field to search engine 74 $this->
AddField($FieldId, $FieldType, $Field->SchemaId(),
75 $Field->SearchWeight(),
76 $Field->IncludeInKeywordSearch());
94 # if this is a reference field 97 # retrieve IDs of referenced items 98 $ReferredItemIds = $Resource->Get($FieldId);
100 # for each referred item 101 $ReturnValue = array();
102 foreach ($ReferredItemIds as $RefId)
104 # retrieve title value for item and add to returned values 105 $RefResource =
new Resource($RefId);
106 $ReturnValue[] = $RefResource->GetMapped(
"Title");
109 # return referred item titles to caller 114 # retrieve text (including variants) from resource object and return to caller 115 return $Resource->Get($FieldId, FALSE, TRUE);
127 # normalize and escape search phrase for use in SQL query 128 $SearchPhrase = strtolower(addslashes($Phrase));
130 # query DB for matching list based on field type 132 switch ($Field->Type())
138 $QueryString =
"SELECT DISTINCT ResourceId FROM Resources " 139 .
"WHERE POSITION('".$SearchPhrase.
"'" 140 .
" IN LOWER(`".$Field->DBFieldName().
"`)) ";
144 $QueryString =
"SELECT DISTINCT ResourceId FROM Resources " 145 .
"WHERE POSITION('".$SearchPhrase.
"'" 146 .
" IN LOWER(`".$Field->DBFieldName().
"AltText`)) ";
150 $NameTableSize = $this->DB->Query(
"SELECT COUNT(*) AS NameCount" 151 .
" FROM ControlledNames",
"NameCount");
152 $QueryString =
"SELECT DISTINCT ResourceNameInts.ResourceId " 153 .
"FROM ResourceNameInts, ControlledNames " 154 .
"WHERE POSITION('".$SearchPhrase.
"' IN LOWER(ControlledName)) " 155 .
"AND ControlledNames.ControlledNameId" 156 .
" = ResourceNameInts.ControlledNameId " 157 .
"AND ControlledNames.FieldId = ".intval($FieldId);
158 $SecondQueryString =
"SELECT DISTINCT ResourceNameInts.ResourceId " 159 .
"FROM ResourceNameInts, ControlledNames, VariantNames " 160 .
"WHERE POSITION('".$SearchPhrase.
"' IN LOWER(VariantName)) " 161 .
"AND VariantNames.ControlledNameId" 162 .
" = ResourceNameInts.ControlledNameId " 163 .
"AND ControlledNames.ControlledNameId" 164 .
" = ResourceNameInts.ControlledNameId " 165 .
"AND ControlledNames.FieldId = ".intval($FieldId);
169 $QueryString =
"SELECT DISTINCT ResourceNameInts.ResourceId " 170 .
"FROM ResourceNameInts, ControlledNames " 171 .
"WHERE POSITION('".$SearchPhrase.
"' IN LOWER(ControlledName)) " 172 .
"AND ControlledNames.ControlledNameId" 173 .
" = ResourceNameInts.ControlledNameId " 174 .
"AND ControlledNames.FieldId = ".intval($FieldId);
178 $QueryString =
"SELECT DISTINCT ResourceClassInts.ResourceId " 179 .
"FROM ResourceClassInts, Classifications " 180 .
"WHERE POSITION('".$SearchPhrase
181 .
"' IN LOWER(ClassificationName)) " 182 .
"AND Classifications.ClassificationId" 183 .
" = ResourceClassInts.ClassificationId " 184 .
"AND Classifications.FieldId = ".intval($FieldId);
188 $UserId = $this->DB->Query(
"SELECT UserId FROM APUsers " 189 .
"WHERE POSITION('".$SearchPhrase
190 .
"' IN LOWER(UserName)) " 191 .
"OR POSITION('".$SearchPhrase
192 .
"' IN LOWER(RealName))",
"UserId");
195 $QueryString =
"SELECT DISTINCT ResourceId FROM ResourceUserInts " 196 .
"WHERE UserId = ".$UserId
197 .
" AND FieldId = ".intval($FieldId);
202 if ($SearchPhrase > 0)
204 $QueryString =
"SELECT DISTINCT ResourceId FROM Resources " 205 .
"WHERE `".$Field->DBFieldName()
206 .
"` = ".(int)$SearchPhrase;
214 # (these types not yet handled by search engine for phrases) 218 # build match list based on results returned from DB 219 if (isset($QueryString))
221 $this->
DMsg(7,
"Performing phrase search query (<i>".$QueryString.
"</i>)");
222 if ($this->
DebugLevel > 9) { $StartTime = microtime(TRUE); }
223 $this->DB->Query($QueryString);
226 $EndTime = microtime(TRUE);
227 if (($StartTime - $EndTime) > 0.1)
229 printf(
"SE: Query took %.2f seconds<br>\n",
230 ($EndTime - $StartTime));
233 $MatchList = $this->DB->FetchColumn(
"ResourceId");
234 if (isset($SecondQueryString))
236 $this->
DMsg(7,
"Performing second phrase search query" 237 .
" (<i>".$SecondQueryString.
"</i>)");
238 if ($this->
DebugLevel > 9) { $StartTime = microtime(TRUE); }
239 $this->DB->Query($SecondQueryString);
242 $EndTime = microtime(TRUE);
243 if (($StartTime - $EndTime) > 0.1)
245 printf(
"SE: query took %.2f seconds<br>\n",
246 ($EndTime - $StartTime));
249 $MatchList = $MatchList + $this->DB->FetchColumn(
"ResourceId");
254 $MatchList = array();
257 # return list of matching resources to caller 270 $FieldIds, $Operators, $Values, $Logic)
272 # use SQL keyword appropriate to current search logic for combining operations 273 $CombineWord = ($Logic ==
"AND") ?
" AND " :
" OR ";
275 # for each comparison 276 foreach ($FieldIds as $Index => $FieldId)
278 # skip field if it is not valid 285 $Operator = $Operators[$Index];
286 $Value = $Values[$Index];
288 $ProcessingType = ($Operator{0} ==
"@")
289 ?
"Modification Comparison" : $Field->Type();
290 switch ($ProcessingType)
297 $QueryConditions[
"Resources"][] = $this->GetTextComparisonSql(
298 $Field->DBFieldName(), $Operator, $Value);
302 $User =
new CWUser($Value);
303 $QueryConditions[
"ResourceUserInts"][] =
304 $this->GetUserComparisonSql(
305 $FieldId, $Operator, $User->Id());
309 $QueryIndex =
"ResourceNameInts".$FieldId;
310 if (!isset($Queries[$QueryIndex][
"A"]))
312 $Queries[$QueryIndex][
"A"] =
313 "SELECT DISTINCT ResourceId" 314 .
" FROM ResourceNameInts, ControlledNames " 315 .
" WHERE ControlledNames.FieldId = " 318 $CloseQuery[$QueryIndex][
"A"] = TRUE;
319 $ComparisonCount[$QueryIndex][
"A"] = 1;
320 $ComparisonCountField[$QueryIndex][
"A"] =
"ControlledName";
324 $Queries[$QueryIndex][
"A"] .=
" OR ";
325 $ComparisonCount[$QueryIndex][
"A"]++;
327 $Queries[$QueryIndex][
"A"] .=
328 "(ResourceNameInts.ControlledNameId" 329 .
" = ControlledNames.ControlledNameId" 330 .
" AND ".$this->GetTextComparisonSql(
331 "ControlledName", $Operator, $Value)
333 if (!isset($Queries[$QueryIndex][
"B"]))
335 $Queries[$QueryIndex][
"B"] =
336 "SELECT DISTINCT ResourceId" 337 .
" FROM ResourceNameInts, ControlledNames," 339 .
" WHERE ControlledNames.FieldId = " 342 $CloseQuery[$QueryIndex][
"B"] = TRUE;
343 $ComparisonCount[$QueryIndex][
"B"] = 1;
344 $ComparisonCountField[$QueryIndex][
"B"] =
"ControlledName";
348 $Queries[$QueryIndex][
"B"] .=
" OR ";
349 $ComparisonCount[$QueryIndex][
"B"]++;
351 $Queries[$QueryIndex][
"B"] .=
352 "(ResourceNameInts.ControlledNameId" 353 .
" = ControlledNames.ControlledNameId" 354 .
" AND ResourceNameInts.ControlledNameId" 355 .
" = VariantNames.ControlledNameId" 356 .
" AND ".$this->GetTextComparisonSql(
357 "VariantName", $Operator, $Value)
362 $QueryIndex =
"ResourceNameInts".$FieldId;
363 if (!isset($Queries[$QueryIndex]))
365 $Queries[$QueryIndex] =
366 "SELECT DISTINCT ResourceId" 367 .
" FROM ResourceNameInts, ControlledNames " 368 .
" WHERE ControlledNames.FieldId = " 371 $CloseQuery[$QueryIndex] = TRUE;
372 $ComparisonCount[$QueryIndex] = 1;
373 $ComparisonCountField[$QueryIndex] =
"ControlledName";
377 $Queries[$QueryIndex] .=
" OR ";
378 $ComparisonCount[$QueryIndex]++;
380 $Queries[$QueryIndex] .=
381 "(ResourceNameInts.ControlledNameId" 382 .
" = ControlledNames.ControlledNameId" 383 .
" AND ".$this->GetTextComparisonSql(
384 "ControlledName", $Operator, $Value)
389 $QueryIndex =
"ResourceClassInts".$FieldId;
390 if (!isset($Queries[$QueryIndex]))
392 $Queries[$QueryIndex] =
"SELECT DISTINCT ResourceId" 393 .
" FROM ResourceClassInts, Classifications" 394 .
" WHERE ResourceClassInts.ClassificationId" 395 .
" = Classifications.ClassificationId" 396 .
" AND Classifications.FieldId" 397 .
" = ".intval($FieldId).
" AND ( ";
398 $CloseQuery[$QueryIndex] = TRUE;
399 $ComparisonCount[$QueryIndex] = 1;
400 $ComparisonCountField[$QueryIndex] =
"ClassificationName";
404 $Queries[$QueryIndex] .=
" OR ";
405 $ComparisonCount[$QueryIndex]++;
408 # for tree fields where the user searched "^XYZ --", treat this as 409 # "=XYZ" OR "^XYZ -- " 410 if ($Operator ==
"^" && preg_match(
"%^(.+) -- *$%", $Value, $Matches))
412 $Queries[$QueryIndex] .=
"(" 413 .$this->GetTextComparisonSql(
414 "ClassificationName",
"=", $Matches[1])
416 .$this->GetTextComparisonSql(
417 "ClassificationName",
"^", $Matches[1].
" -- ")
422 $Queries[$QueryIndex] .= $this->GetTextComparisonSql(
423 "ClassificationName", $Operator, $Value);
428 # if we have an SQL conditional 429 $TimestampConditional = $this->GetTimeComparisonSql(
430 $Field, $Operator, $Value);
431 if ($TimestampConditional)
434 $QueryConditions[
"Resources"][] = $TimestampConditional;
439 $Date =
new Date($Value);
440 if ($Date->Precision())
442 $QueryConditions[
"Resources"][] =
443 " ( ".$Date->SqlCondition(
444 $Field->DBFieldName().
"Begin",
445 $Field->DBFieldName().
"End", $Operator).
" ) ";
450 $QueryIndex =
"ReferenceInts".$FieldId;
451 if (!isset($Queries[$QueryIndex]))
453 $Queries[$QueryIndex] =
454 "SELECT DISTINCT RI.SrcResourceId AS ResourceId" 455 .
" FROM ReferenceInts AS RI, Resources AS R " 456 .
" WHERE RI.FieldId = ".intval($FieldId)
458 $CloseQuery[$QueryIndex] = TRUE;
462 $Queries[$QueryIndex] .= $CombineWord;
465 if (is_numeric($Value))
467 # add subquery for specific resource ID 468 $Queries[$QueryIndex] .=
"(RI.DstResourceId ".$Operator.
" '" 469 .addslashes($Value).
"')";
473 # iterate over all the schemas this field can reference, 474 # gluing together an array of subqueries for the mapped 475 # title field of each as we go 476 $SchemaIds = $Field->ReferenceableSchemaIds();
478 # if no referenceable schemas configured, fall back to 479 # searching all schemas 480 if (count($SchemaIds)==0)
485 $Subqueries = array();
486 foreach ($SchemaIds as $SchemaId)
489 $MappedTitle = $Schema->GetFieldByMappedName(
"Title");
491 $Subqueries[]= $this->GetTextComparisonSql(
492 $MappedTitle->DBFieldName(), $Operator, $Value,
"R");
495 # OR together all the subqueries, add it to the query 497 $Queries[$QueryIndex] .=
498 "((".implode(
" OR ", $Subqueries).
")" 499 .
" AND R.ResourceId = RI.DstResourceId)";
503 case "Modification Comparison":
504 # if we have an SQL conditional 505 $TimestampConditional = $this->GetTimeComparisonSql(
506 $Field, $Operator, $Value);
507 if ($TimestampConditional)
510 $QueryConditions[
"ResourceFieldTimestamps"][] =
511 $TimestampConditional;
516 throw new Exception(
"Search of unknown field type (" 517 .$ProcessingType.
").");
522 # if query conditions found 523 if (isset($QueryConditions))
525 # for each query condition group 526 foreach ($QueryConditions as $TargetTable => $Conditions)
528 # add entry with conditions to query list 529 if (isset($Queries[$TargetTable]))
531 $Queries[$TargetTable] .= $CombineWord
532 .implode($CombineWord, $Conditions);
536 $Queries[$TargetTable] =
"SELECT DISTINCT ResourceId" 537 .
" FROM ".$TargetTable.
" WHERE " 538 .implode($CombineWord, $Conditions);
546 # for each assembled query 547 foreach ($Queries as $QueryIndex => $Query)
549 # if query has multiple parts 550 if (is_array($Query))
552 # for each part of query 553 $ResourceIds = array();
554 foreach ($Query as $PartIndex => $PartQuery)
556 # add closing paren if query was flagged to be closed 557 if (isset($CloseQuery[$QueryIndex][$PartIndex]))
560 if (($Logic ==
"AND")
561 && ($ComparisonCount[$QueryIndex][$PartIndex] > 1))
563 $PartQuery .=
"GROUP BY ResourceId" 564 .
" HAVING COUNT(DISTINCT " 565 .$ComparisonCountField[$QueryIndex][$PartIndex]
567 .$ComparisonCount[$QueryIndex][$PartIndex];
571 # perform query and retrieve IDs 572 $this->
DMsg(5,
"Performing comparison query <i>" 574 $this->DB->Query($PartQuery);
575 $ResourceIds = $ResourceIds
576 + $this->DB->FetchColumn(
"ResourceId");
577 $this->
DMsg(5,
"Comparison query produced <i>" 578 .count($ResourceIds).
"</i> results");
583 # add closing paren if query was flagged to be closed 584 if (isset($CloseQuery[$QueryIndex]))
587 if (($Logic ==
"Logic")
588 && ($ComparisonCount[$QueryIndex] > 1))
590 $Query .=
"GROUP BY ResourceId" 591 .
" HAVING COUNT(DISTINCT " 592 .$ComparisonCountField[$QueryIndex]
594 .$ComparisonCount[$QueryIndex];
598 # perform query and retrieve IDs 599 $this->
DMsg(5,
"Performing comparison query <i>".$Query.
"</i>");
600 $this->DB->Query($Query);
601 $ResourceIds = $this->DB->FetchColumn(
"ResourceId");
602 $this->
DMsg(5,
"Comparison query produced <i>" 603 .count($ResourceIds).
"</i> results");
606 # if we already have some results 609 # if search logic is set to AND 612 # remove anything from results that was not returned from query 613 $Results = array_intersect($Results, $ResourceIds);
617 # add values returned from query to results 618 $Results = array_unique(array_merge($Results, $ResourceIds));
623 # set results to values returned from query 624 $Results = $ResourceIds;
630 # initialize results to empty list 634 # return results to caller 647 $ItemType, $FieldId, $SortDescending)
650 return $RFactory->GetResourceIdsSortedBy($FieldId, !$SortDescending);
661 if (is_numeric($ItemOrItemId))
663 $ItemId = $ItemOrItemId;
668 $Item = $ItemOrItemId;
669 $ItemId = $Item->Id();
672 # if no priority was provided, use the default 673 if ($TaskPriority === NULL)
675 $TaskPriority = self::$TaskPriority;
678 # assemble task description 679 $Title = $Item->GetMapped(
"Title");
682 $Title =
"Item #".$ItemId;
684 $TaskDescription =
"Update search data for" 685 .
" <a href=\"r".$ItemId.
"\"><i>" 689 $GLOBALS[
"AF"]->QueueUniqueTask(array(__CLASS__,
"RunUpdateForItem"),
690 array(intval($ItemId)), $TaskPriority, $TaskDescription);
699 # bail out if item no longer exists 704 catch (InvalidArgumentException $Exception)
709 # bail out if item is a temporary record 710 if ($Resource->IsTempResource()) {
return; }
712 # retrieve schema ID of item to use for item type 713 $ItemType = $Resource->SchemaId();
715 # update search data for resource 717 $SearchEngine->UpdateForItem($ItemId, $ItemType);
730 # classifications and names associated with these search results 731 $SearchClasses = array();
732 $SearchNames = array();
734 # make sure we're not faceting too many resources 735 $SearchResults = array_slice(
737 self::$NumResourcesForFacets,
740 # disable DB cache for the search suggestions process, 741 # this avoids memory exhaustion. 745 # number of resources to include in a chunk 746 # a mysql BIGINT is at most 21 characters long and the 747 # default max_packet_size is 1 MiB, so we can pack about 748 # 1 MiB / (22 bytes) = 47,663 ResourceIds into a query before 749 # we need to worry about length problems 752 if (count($SearchResults)>0)
754 foreach (array_chunk($SearchResults, $ChunkSize, TRUE) as $Chunk)
756 # pull out all the Classifications that were associated 757 # with our search results along with all their parents 758 $DB->Query(
"SELECT ResourceId,ClassificationId FROM ResourceClassInts " 759 .
"WHERE ResourceId IN " 760 .
"(".implode(
",", array_keys($Chunk)).
")");
761 $Rows =
$DB->FetchRows();
762 foreach ($Rows as $Row)
764 $CurId = $Row[
"ClassificationId"];
765 while ($CurId !== FALSE)
767 $SearchClasses[$CurId][]=$Row[
"ResourceId"] ;
768 $CurId = self::FindParentClass($CurId);
772 # also pull out controlled names 773 $DB->Query(
"SELECT ResourceId,ControlledNameId FROM ResourceNameInts " 774 .
"WHERE ResourceId in " 775 .
"(".implode(
",", array_keys($Chunk)).
")");
776 $Rows =
$DB->FetchRows();
777 foreach ($Rows as $Row)
779 $SearchNames[$Row[
"ControlledNameId"]][]= $Row[
"ResourceId"];
783 # make sure we haven't double-counted resources that have 784 # a classification and some of its children assigned 785 $TmpClasses = array();
786 foreach ($SearchClasses as $ClassId => $Resources)
788 $TmpClasses[$ClassId] = array_unique($Resources);
790 $SearchClasses = $TmpClasses;
793 # generate a map of FieldId -> Field Names for all of the generated facets: 794 $SuggestionsById = array();
796 # pull relevant Classification names out of the DB 797 if (count($SearchClasses)>0)
799 foreach (array_chunk($SearchClasses, $ChunkSize, TRUE) as $Chunk)
801 $DB->Query(
"SELECT FieldId,ClassificationId,ClassificationName" 802 .
" FROM Classifications" 803 .
" WHERE ClassificationId" 804 .
" IN (".implode(
",", array_keys($Chunk)).
")");
805 foreach (
$DB->FetchRows() as $Row)
807 $SuggestionsById[$Row[
"FieldId"]][]=
808 array(
"Id" => $Row[
"ClassificationId"],
809 "Name" => $Row[
"ClassificationName"],
811 $SearchClasses[$Row[
"ClassificationId"]]));
816 # pull relevant ControlledNames out of the DB 817 if (count($SearchNames)>0)
819 foreach (array_chunk($SearchNames, $ChunkSize, TRUE) as $Chunk)
821 $DB->Query(
"SELECT FieldId,ControlledNameId,ControlledName" 822 .
" FROM ControlledNames" 823 .
" WHERE ControlledNameId" 824 .
" IN (".implode(
",", array_keys($SearchNames)).
")");
825 foreach (
$DB->FetchRows() as $Row)
827 $SuggestionsById[$Row[
"FieldId"]][]=
828 array(
"Id" => $Row[
"ControlledNameId"],
829 "Name" => $Row[
"ControlledName"],
831 $SearchNames[$Row[
"ControlledNameId"]]));
836 # translate the suggestions that we have in terms of the 837 # FieldIds to suggestions in terms of the field names 838 $SuggestionsByFieldName = array();
840 # if we have suggestions to offer 841 if (count($SuggestionsById)>0)
843 # gill in an array that maps FieldNames to search links 844 # which would be appropriate for that field 845 foreach ($SuggestionsById as $FieldId => $FieldValues)
851 catch (Exception $Exception)
856 # bail on fields that didn't exist and on fields that the 857 # current user cannot view, and on fields that are 858 # disabled for advanced searching 859 if (is_object($ThisField) &&
861 $ThisField->IncludeInFacetedSearch() &&
862 $ThisField->Enabled() &&
863 $User->HasPriv($ThisField->ViewingPrivileges()))
865 $SuggestionsByFieldName[$ThisField->Name()] = array();
867 foreach ($FieldValues as $Value)
869 $SuggestionsByFieldName[$ThisField->Name()][$Value[
"Id"]] =
870 array(
"Name" => $Value[
"Name"],
"Count" => $Value[
"Count"] );
876 ksort($SuggestionsByFieldName);
878 return $SuggestionsByFieldName;
888 self::$TaskPriority = $NewPriority;
897 self::$NumResourcesForFacets = $NumToUse;
908 $SearchEngine =
new self();
911 $Ids = $RFactory->GetItemIds();
913 foreach ($Ids as $Id)
915 $SearchEngine->QueueUpdateForItem($Id);
931 foreach ($Schemas as $SchemaId => $Schema)
933 $ItemsQueued += self::QueueDBRebuildForSchema($SchemaId);
939 # ---- BACKWARD COMPATIBILITY -------------------------------------------- 961 $SearchGroups, $StartingResult = 0, $NumberOfResults = 10,
962 $SortByField = NULL, $SortDescending = TRUE)
964 # check for use of deprecated parameters 965 if ($StartingResult != 0)
967 throw new InvalidArgumentException(
"Deprecated StartingResult" 968 .
" parameter used with value \"".$StartingResult.
"\".");
970 elseif ($NumberOfResults != 10)
972 throw new InvalidArgumentException(
"Deprecated NumberOfResults" 973 .
" parameter used with value \"".$NumberOfResults.
"\".");
978 # if search parameter set was passed in, use it directly 979 $SearchParams = $SearchGroups;
983 # otherwise, convert legacy array into SearchParameterSet 984 $SearchParams =
new SearchParameterSet();
985 $SearchParams->SetFromLegacyArray($SearchGroups);
988 # add sort and item type parameters to search parameter set 989 $SearchParams->SortBy($SortByField);
990 $SearchParams->SortDescending($SortDescending);
994 $Results = $this->
Search($SearchParams);
1008 # translate current version of 'is under' 1009 '%([A-Z].*) begins with <i>(.*) -- ?</i>%' =>
1010 '$1 is under <i>$2</i>',
1011 # translate older subgroup version of 'is under' 1012 '%\( ?([A-Z].*) is <i>(.*)</i><br>\n( )+ or \1 is under <i>\2</i>\)%' =>
1013 '$1 is under <i>$2</i>',
1016 return preg_replace(
1017 array_keys($Patterns),
1018 array_values($Patterns),
1022 # ---- PRIVATE INTERFACE ------------------------------------------------- 1024 private $FieldTypes;
1028 private static $NumResourcesForFacets = 500;
1038 private function GetTextComparisonSql($DBField, $Operator, $Value, $Prefix=
"")
1040 # if we were given a prefix, add the necessary period so we can use it 1041 if (strlen($Prefix))
1043 $Prefix = $Prefix.
".";
1046 if ($Operator ==
"^")
1048 $EscapedValue = str_replace(
1051 addslashes($Value));
1052 $Comparison = $Prefix.
"`".$DBField.
"` LIKE '".$EscapedValue.
"%' ";
1054 elseif ($Operator ==
'$')
1056 $EscapedValue = str_replace(
1059 addslashes($Value));
1060 $Comparison = $Prefix.
"`".$DBField.
"` LIKE '%".$EscapedValue.
"' ";
1062 elseif ($Operator ==
'!=')
1065 "(".$Prefix.
"`".$DBField.
"` ".$Operator.
" '".addslashes($Value).
"'" 1066 .
" AND ".$Prefix.
"`".$DBField.
"` IS NOT NULL)";
1070 $Comparison = $Prefix.
"`".$DBField.
"` " 1071 .$Operator.
" '".addslashes($Value).
"' ";
1084 private function GetTimeComparisonSql($Field, $Operator, $Value)
1086 # check if this is a field modification comparison 1087 $ModificationComparison = ($Operator{0} ==
"@") ? TRUE : FALSE;
1089 # if value appears to have time component or text description 1090 if (strpos($Value,
":")
1091 || strstr($Value,
"day")
1092 || strstr($Value,
"week")
1093 || strstr($Value,
"month")
1094 || strstr($Value,
"year")
1095 || strstr($Value,
"hour")
1096 || strstr($Value,
"minute"))
1098 # adjust operator if necessary 1099 if ($Operator ==
"@")
1105 if ($ModificationComparison)
1107 $Operator = substr($Operator, 1);
1110 if (strstr($Value,
"ago"))
1112 $OperatorFlipMap = array(
1118 $Operator = isset($OperatorFlipMap[$Operator])
1119 ? $OperatorFlipMap[$Operator] : $Operator;
1123 # translate common words-to-numbers 1124 $WordsForNumbers = array(
1129 '/^three /i' =>
'3 ',
1130 '/^four /i' =>
'4 ',
1131 '/^five /i' =>
'5 ',
1133 '/^seven /i' =>
'7 ',
1134 '/^eight /i' =>
'8 ',
1135 '/^nine /i' =>
'9 ',
1136 '/^ten /i' =>
'10 ',
1137 '/^eleven /i' =>
'11 ',
1138 '/^twelve /i' =>
'12 ',
1139 '/^thirteen /i' =>
'13 ',
1140 '/^fourteen /i' =>
'14 ',
1141 '/^fifteen /i' =>
'15 ',
1142 '/^sixteen /i' =>
'16 ',
1143 '/^seventeen /i' =>
'17 ',
1144 '/^eighteen /i' =>
'18 ',
1145 '/^nineteen /i' =>
'19 ',
1146 '/^twenty /i' =>
'20 ',
1147 '/^thirty /i' =>
'30 ',
1148 '/^forty /i' =>
'40 ',
1149 '/^fourty /i' =>
'40 ', # (common misspelling)
1150 '/^fifty /i' =>
'50 ',
1151 '/^sixty /i' =>
'60 ',
1152 '/^seventy /i' =>
'70 ',
1153 '/^eighty /i' =>
'80 ',
1154 '/^ninety /i' =>
'90 ');
1155 $Value = preg_replace(
1156 array_keys($WordsForNumbers), $WordsForNumbers, $Value);
1158 # use strtotime method to build condition 1159 $TimestampValue = strtotime($Value);
1160 if (($TimestampValue !== FALSE) && ($TimestampValue != -1))
1162 if ((date(
"H:i:s", $TimestampValue) ==
"00:00:00")
1163 && (strpos($Value,
"00:00") === FALSE)
1164 && ($Operator ==
"<="))
1167 date(
"Y-m-d", $TimestampValue).
" 23:59:59";
1171 $NormalizedValue = date(
1172 "Y-m-d H:i:s", $TimestampValue);
1177 $NormalizedValue = addslashes($Value);
1180 # build SQL conditional 1181 if ($ModificationComparison)
1183 $Conditional =
" ( FieldId = ".$Field->Id()
1184 .
" AND Timestamp ".$Operator
1185 .
" '".$NormalizedValue.
"' ) ";
1189 $Conditional =
" ( `".$Field->DBFieldName().
"` " 1190 .$Operator.
" '".$NormalizedValue.
"' ) ";
1195 # adjust operator if necessary 1196 if ($ModificationComparison)
1198 $Operator = ($Operator ==
"@") ?
">=" 1199 : substr($Operator, 1);
1202 # use Date object method to build conditional 1203 $Date =
new Date($Value);
1204 if ($Date->Precision())
1206 if ($ModificationComparison)
1208 $Conditional =
" ( FieldId = ".$Field->Id()
1209 .
" AND ".$Date->SqlCondition(
1210 "Timestamp", NULL, $Operator).
" ) ";
1214 $Conditional =
" ( ".$Date->SqlCondition(
1215 $Field->DBFieldName(), NULL, $Operator).
" ) ";
1220 # return assembled conditional to caller 1221 return $Conditional;
1232 private function GetUserComparisonSql(
1233 $FieldId, $Operator, $UserId)
1238 return "(UserId = ".intval($UserId).
" AND FieldId = " 1239 .intval($FieldId).
")";
1243 return "(UserId != ".intval($UserId).
" AND FieldId = " 1244 .intval($FieldId).
")";
1248 throw new Exception(
1249 "Operator ".$Operator.
" is not supported for User fields");
1261 private static function FindParentClass($ClassId)
1265 # first time through, fetch the mapping of parent values we need 1266 if (!isset($ParentMap))
1270 # result here will be a parent/child mapping for all used 1271 # classifications; avoid caching it as it can be quite large 1272 $PreviousSetting =
$DB->Caching();
1273 $DB->Caching(FALSE);
1275 "SELECT ParentId, ClassificationId FROM Classifications " 1276 .
"WHERE DEPTH > 0 AND FullResourceCount > 0 " 1277 .
"AND FieldId IN (SELECT FieldId FROM MetadataFields " 1278 .
" WHERE IncludeInFacetedSearch=1)" 1280 $DB->Caching($PreviousSetting);
1282 $ParentMap =
$DB->FetchColumn(
"ParentId",
"ClassificationId");
1285 return isset($ParentMap[$ClassId]) ? $ParentMap[$ClassId] : FALSE;
AddField($FieldId, $FieldType, $ItemTypes, $Weight, $UsedInKeywordSearch)
Add field to include in searching.
static FilterTextDisplay($ParamDesc)
Filter for text display of search parameters.
Set of parameters used to perform a search.
static SetUpdatePriority($NewPriority)
Set the default priority for background tasks.
SQL database abstraction object with smart query caching.
static SetNumResourcesForFacets($NumToUse)
Set the number of resources used for search facets.
static QueueDBRebuildForSchema($SchemaId)
Queue background rebuild of search database for all items for specified schema.
static GetItemIdsSortedByField($ItemType, $FieldId, $SortDescending)
Return item IDs sorted by a specified field.
static RunUpdateForItem($ItemId)
Update search index for an item.
GroupedSearch($SearchGroups, $StartingResult=0, $NumberOfResults=10, $SortByField=NULL, $SortDescending=TRUE)
Perform search with logical groups of fielded searches.
SearchFieldForPhrases($FieldId, $Phrase)
Perform phrase searching.
DMsg($Level, $Msg)
Print debug message if level set high enough.
Represents a "resource" in CWIS.
static GetResultFacets($SearchResults, $User)
Generate a list of suggested additional search terms that can be used for faceted searching...
GetFieldContent($ItemId, $FieldId)
Overloaded version of method to retrieve text from DB.
Core metadata archive search engine class.
Search($SearchParams)
Perform search with specified parameters, returning results in a flat array indexed by item ID...
SearchFieldsForComparisonMatches($FieldIds, $Operators, $Values, $Logic)
Perform comparison searches.
DebugLevel($NewValue)
Set debug output level.
Factory for Resource objects.
CWIS-specific user class.
__construct()
Class constructor.
const PRIORITY_BACKGROUND
Lowest priority.
static QueueUpdateForItem($ItemOrItemId, $TaskPriority=NULL)
Queue background update for an item.
static QueueDBRebuildForAllSchemas()
Queue background rebuild of search database for all items for all schemas.