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 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 "(UserId = ".$User->Id().
" AND FieldId = " 305 .intval($FieldId).
")";
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]++;
407 $Queries[$QueryIndex] .= $this->GetTextComparisonSql(
408 "ClassificationName", $Operator, $Value);
412 # if we have an SQL conditional 413 $TimestampConditional = $this->GetTimeComparisonSql(
414 $Field, $Operator, $Value);
415 if ($TimestampConditional)
418 $QueryConditions[
"Resources"][] = $TimestampConditional;
423 $Date =
new Date($Value);
424 if ($Date->Precision())
426 $QueryConditions[
"Resources"][] =
427 " ( ".$Date->SqlCondition(
428 $Field->DBFieldName().
"Begin",
429 $Field->DBFieldName().
"End", $Operator).
" ) ";
434 $QueryIndex =
"ReferenceInts".$FieldId;
435 if (!isset($Queries[$QueryIndex]))
437 $Queries[$QueryIndex] =
438 "SELECT DISTINCT RI.SrcResourceId AS ResourceId" 439 .
" FROM ReferenceInts AS RI, Resources AS R " 440 .
" WHERE RI.FieldId = ".intval($FieldId)
442 $CloseQuery[$QueryIndex] = TRUE;
446 $Queries[$QueryIndex] .= $CombineWord;
449 # iterate over all the schemas this field can reference, 450 # gluing together an array of subqueries for the mapped 451 # title field of each as we go 452 $SchemaIds = $Field->ReferenceableSchemaIds();
454 # if no referenceable schemas configured, fall back to 455 # searching all schemas 456 if (count($SchemaIds)==0)
461 $Subqueries = array();
462 foreach ($SchemaIds as $SchemaId)
465 $MappedTitle = $Schema->GetFieldByMappedName(
"Title");
467 $Subqueries[]= $this->GetTextComparisonSql(
468 $MappedTitle->DBFieldName(), $Operator, $Value,
"R");
471 # OR together all the subqueries, add it to the query 473 $Queries[$QueryIndex] .=
474 "((".implode(
" OR ", $Subqueries).
")" 475 .
" AND R.ResourceId = RI.DstResourceId)";
478 case "Modification Comparison":
479 # if we have an SQL conditional 480 $TimestampConditional = $this->GetTimeComparisonSql(
481 $Field, $Operator, $Value);
482 if ($TimestampConditional)
485 $QueryConditions[
"ResourceFieldTimestamps"][] =
486 $TimestampConditional;
491 throw new Exception(
"Search of unknown field type (" 492 .$ProcessingType.
").");
497 # if query conditions found 498 if (isset($QueryConditions))
500 # for each query condition group 501 foreach ($QueryConditions as $TargetTable => $Conditions)
503 # add entry with conditions to query list 504 if (isset($Queries[$TargetTable]))
506 $Queries[$TargetTable] .= $CombineWord
507 .implode($CombineWord, $Conditions);
511 $Queries[$TargetTable] =
"SELECT DISTINCT ResourceId" 512 .
" FROM ".$TargetTable.
" WHERE " 513 .implode($CombineWord, $Conditions);
521 # for each assembled query 522 foreach ($Queries as $QueryIndex => $Query)
524 # if query has multiple parts 525 if (is_array($Query))
527 # for each part of query 528 $ResourceIds = array();
529 foreach ($Query as $PartIndex => $PartQuery)
531 # add closing paren if query was flagged to be closed 532 if (isset($CloseQuery[$QueryIndex][$PartIndex]))
535 if (($Logic ==
"AND")
536 && ($ComparisonCount[$QueryIndex][$PartIndex] > 1))
538 $PartQuery .=
"GROUP BY ResourceId" 539 .
" HAVING COUNT(DISTINCT " 540 .$ComparisonCountField[$QueryIndex][$PartIndex]
542 .$ComparisonCount[$QueryIndex][$PartIndex];
546 # perform query and retrieve IDs 547 $this->
DMsg(5,
"Performing comparison query <i>" 549 $this->DB->Query($PartQuery);
550 $ResourceIds = $ResourceIds
551 + $this->DB->FetchColumn(
"ResourceId");
552 $this->
DMsg(5,
"Comparison query produced <i>" 553 .count($ResourceIds).
"</i> results");
558 # add closing paren if query was flagged to be closed 559 if (isset($CloseQuery[$QueryIndex]))
562 if (($Logic ==
"Logic")
563 && ($ComparisonCount[$QueryIndex] > 1))
565 $Query .=
"GROUP BY ResourceId" 566 .
" HAVING COUNT(DISTINCT " 567 .$ComparisonCountField[$QueryIndex]
569 .$ComparisonCount[$QueryIndex];
573 # perform query and retrieve IDs 574 $this->
DMsg(5,
"Performing comparison query <i>".$Query.
"</i>");
575 $this->DB->Query($Query);
576 $ResourceIds = $this->DB->FetchColumn(
"ResourceId");
577 $this->
DMsg(5,
"Comparison query produced <i>" 578 .count($ResourceIds).
"</i> results");
581 # if we already have some results 584 # if search logic is set to AND 587 # remove anything from results that was not returned from query 588 $Results = array_intersect($Results, $ResourceIds);
592 # add values returned from query to results 593 $Results = array_unique(array_merge($Results, $ResourceIds));
598 # set results to values returned from query 599 $Results = $ResourceIds;
605 # initialize results to empty list 609 # return results to caller 622 $ItemType, $FieldId, $SortDescending)
625 return $RFactory->GetResourceIdsSortedBy($FieldId, !$SortDescending);
636 if (is_numeric($ItemOrItemId))
638 $ItemId = $ItemOrItemId;
643 $Item = $ItemOrItemId;
644 $ItemId = $Item->Id();
647 # if no priority was provided, use the default 648 if ($TaskPriority === NULL)
650 $TaskPriority = self::$TaskPriority;
653 # assemble task description 654 $Title = $Item->GetMapped(
"Title");
657 $Title =
"Item #".$ItemId;
659 $TaskDescription =
"Update search data for" 660 .
" <a href=\"r".$ItemId.
"\"><i>" 664 $GLOBALS[
"AF"]->QueueUniqueTask(array(__CLASS__,
"RunUpdateForItem"),
665 array(intval($ItemId)), $TaskPriority, $TaskDescription);
674 # bail out if item no longer exists 679 catch (InvalidArgumentException $Exception)
684 # bail out if item is a temporary record 685 if ($Resource->IsTempResource()) {
return; }
687 # retrieve schema ID of item to use for item type 688 $ItemType = $Resource->SchemaId();
690 # update search data for resource 692 $SearchEngine->UpdateForItem($ItemId, $ItemType);
705 # classifications and names associated with these search results 706 $SearchClasses = array();
707 $SearchNames = array();
709 # make sure we're not faceting too many resources 710 $SearchResults = array_slice(
712 self::$NumResourcesForFacets,
715 # disable DB cache for the search suggestions process, 716 # this avoids memory exhaustion. 720 # number of resources to include in a chunk 721 # a mysql BIGINT is at most 21 characters long and the 722 # default max_packet_size is 1 MiB, so we can pack about 723 # 1 MiB / (22 bytes) = 47,663 ResourceIds into a query before 724 # we need to worry about length problems 727 if (count($SearchResults)>0)
729 foreach (array_chunk($SearchResults, $ChunkSize, TRUE) as $Chunk)
731 # pull out all the Classifications that were associated 732 # with our search results along with all their parents 733 $DB->Query(
"SELECT ResourceId,ClassificationId FROM ResourceClassInts " 734 .
"WHERE ResourceId IN " 735 .
"(".implode(
",", array_keys($Chunk)).
")");
736 $Rows =
$DB->FetchRows();
737 foreach ($Rows as $Row)
739 $CurId = $Row[
"ClassificationId"];
740 while ($CurId !== FALSE)
742 $SearchClasses[$CurId][]=$Row[
"ResourceId"] ;
743 $CurId = self::FindParentClass($CurId);
747 # also pull out controlled names 748 $DB->Query(
"SELECT ResourceId,ControlledNameId FROM ResourceNameInts " 749 .
"WHERE ResourceId in " 750 .
"(".implode(
",", array_keys($Chunk)).
")");
751 $Rows =
$DB->FetchRows();
752 foreach ($Rows as $Row)
754 $SearchNames[$Row[
"ControlledNameId"]][]= $Row[
"ResourceId"];
758 # make sure we haven't double-counted resources that have 759 # a classification and some of its children assigned 760 $TmpClasses = array();
761 foreach ($SearchClasses as $ClassId => $Resources)
763 $TmpClasses[$ClassId] = array_unique($Resources);
765 $SearchClasses = $TmpClasses;
768 # generate a map of FieldId -> Field Names for all of the generated facets: 769 $SuggestionsById = array();
771 # pull relevant Classification names out of the DB 772 if (count($SearchClasses)>0)
774 foreach (array_chunk($SearchClasses, $ChunkSize, TRUE) as $Chunk)
776 $DB->Query(
"SELECT FieldId,ClassificationId,ClassificationName" 777 .
" FROM Classifications" 778 .
" WHERE ClassificationId" 779 .
" IN (".implode(
",", array_keys($Chunk)).
")");
780 foreach (
$DB->FetchRows() as $Row)
782 $SuggestionsById[$Row[
"FieldId"]][]=
783 array(
"Id" => $Row[
"ClassificationId"],
784 "Name" => $Row[
"ClassificationName"],
786 $SearchClasses[$Row[
"ClassificationId"]]));
791 # pull relevant ControlledNames out of the DB 792 if (count($SearchNames)>0)
794 foreach (array_chunk($SearchNames, $ChunkSize, TRUE) as $Chunk)
796 $DB->Query(
"SELECT FieldId,ControlledNameId,ControlledName" 797 .
" FROM ControlledNames" 798 .
" WHERE ControlledNameId" 799 .
" IN (".implode(
",", array_keys($SearchNames)).
")");
800 foreach (
$DB->FetchRows() as $Row)
802 $SuggestionsById[$Row[
"FieldId"]][]=
803 array(
"Id" => $Row[
"ControlledNameId"],
804 "Name" => $Row[
"ControlledName"],
806 $SearchNames[$Row[
"ControlledNameId"]]));
811 # translate the suggestions that we have in terms of the 812 # FieldIds to suggestions in terms of the field names 813 $SuggestionsByFieldName = array();
815 # if we have suggestions to offer 816 if (count($SuggestionsById)>0)
818 # gill in an array that maps FieldNames to search links 819 # which would be appropriate for that field 820 foreach ($SuggestionsById as $FieldId => $FieldValues)
826 catch (Exception $Exception)
831 # bail on fields that didn't exist and on fields that the 832 # current user cannot view, and on fields that are 833 # disabled for advanced searching 834 if (is_object($ThisField) &&
836 $ThisField->IncludeInFacetedSearch() &&
837 $ThisField->Enabled() &&
838 $User->HasPriv($ThisField->ViewingPrivileges()))
840 $SuggestionsByFieldName[$ThisField->Name()] = array();
842 foreach ($FieldValues as $Value)
844 $SuggestionsByFieldName[$ThisField->Name()][$Value[
"Id"]] =
845 array(
"Name" => $Value[
"Name"],
"Count" => $Value[
"Count"] );
851 ksort($SuggestionsByFieldName);
853 return $SuggestionsByFieldName;
863 self::$TaskPriority = $NewPriority;
872 self::$NumResourcesForFacets = $NumToUse;
875 # ---- BACKWARD COMPATIBILITY -------------------------------------------- 897 $SearchGroups, $StartingResult = 0, $NumberOfResults = 10,
898 $SortByField = NULL, $SortDescending = TRUE)
902 # if search parameter set was passed in, use it directly 903 $SearchParams = $SearchGroups;
907 # otherwise, convert legacy array into SearchParameterSet 908 $SearchParams =
new SearchParameterSet();
909 $SearchParams->SetFromLegacyArray($SearchGroups);
914 $SearchParams, $StartingResult, $NumberOfResults,
915 $SortByField, $SortDescending);
917 # pull out the resoults for the Resource schema 952 # create display parameters, used to make a more user-friendly 953 # version of the search 956 # copy keyword searches as is 957 $DisplayParams->AddParameter(
958 $SearchParams->GetKeywordSearchStrings() );
960 # copy field searches as is 961 $SearchStrings = $SearchParams->GetSearchStrings();
962 foreach ($SearchStrings as $FieldId => $Params)
964 $DisplayParams->AddParameter($Params, $FieldId);
967 # iterate over the search groups, looking for the 'is or begins 968 # with' group that we add when faceting and displaying them as 969 # IS parameters rather than the literal subgroup that we 971 $Groups = $SearchParams->GetSubgroups();
972 foreach ($Groups as $Group)
974 # start off assuming that we'll just copy the group without conversion 977 # if this group uses OR logic for a single field, then it 978 # might be one of the subgroups we want to match and will require further 980 if ($Group->Logic() ==
"OR" &&
981 count($Group->GetFields()) == 1)
983 # pull out the search strings for this field 984 $SearchStrings = $Group->GetSearchStrings();
985 $FieldId = key($SearchStrings);
986 $Values = current($SearchStrings);
988 # check if there are two search strings, one an 'is' 989 # and the other a 'begins with' that both start with the 990 # same prefix, as would be added by the search facet code 991 if ( count($Values) == 2 &&
992 preg_match(
'/^=(.*)$/', $Values[0], $FirstMatch) &&
993 preg_match(
'/^\\^(.*) -- $/', $Values[1], $SecondMatch) &&
994 $FirstMatch[1] == $SecondMatch[1] )
996 # check if this field is valid anywhere 1001 # and check if this field is a tree field 1004 # okay, this group matches the form that 1005 # the faceting code would add for an 'is or 1006 # begins with' group; convert it to just an 1007 # 'is' group for display 1008 $DisplayParams->AddParameter(
"=".$FirstMatch[1], $FieldId);
1015 # if this group didn't require conversion, attempt to copy 1021 $DisplayParams->AddSet($Group);
1023 catch (Exception $e)
1025 # if group could not be added for any reason, skip 1026 # it and move on to the next group 1031 return $DisplayParams;
1034 # ---- PRIVATE INTERFACE ------------------------------------------------- 1036 private $FieldTypes;
1039 private static $TaskPriority = ApplicationFramework::PRIORITY_BACKGROUND;
1040 private static $NumResourcesForFacets = 500;
1050 private function GetTextComparisonSql($DBField, $Operator, $Value, $Prefix=
"")
1052 # if we were given a prefix, add the necessary period so we can use it 1053 if (strlen($Prefix))
1055 $Prefix = $Prefix.
".";
1058 if ($Operator ==
"^")
1060 $EscapedValue = str_replace(
1063 addslashes($Value));
1064 $Comparison = $Prefix.
"`".$DBField.
"` LIKE '".$EscapedValue.
"%' ";
1066 elseif ($Operator ==
'$')
1068 $EscapedValue = str_replace(
1071 addslashes($Value));
1072 $Comparison = $Prefix.
"`".$DBField.
"` LIKE '%".$EscapedValue.
"' ";
1074 elseif ($Operator ==
'!=')
1077 "(".$Prefix.
"`".$DBField.
"` ".$Operator.
" '".addslashes($Value).
"'" 1078 .
" AND ".$Prefix.
"`".$DBField.
"` IS NOT NULL)";
1082 $Comparison = $Prefix.
"`".$DBField.
"` " 1083 .$Operator.
" '".addslashes($Value).
"' ";
1096 private function GetTimeComparisonSql($Field, $Operator, $Value)
1098 # check if this is a field modification comparison 1099 $ModificationComparison = ($Operator{0} ==
"@") ? TRUE : FALSE;
1101 # if value appears to have time component or text description 1102 if (strpos($Value,
":")
1103 || strstr($Value,
"day")
1104 || strstr($Value,
"week")
1105 || strstr($Value,
"month")
1106 || strstr($Value,
"year")
1107 || strstr($Value,
"hour")
1108 || strstr($Value,
"minute"))
1110 # adjust operator if necessary 1111 if ($Operator ==
"@")
1117 if ($ModificationComparison)
1119 $Operator = substr($Operator, 1);
1122 if (strstr($Value,
"ago"))
1124 $OperatorFlipMap = array(
1130 $Operator = isset($OperatorFlipMap[$Operator])
1131 ? $OperatorFlipMap[$Operator] : $Operator;
1135 # translate common words-to-numbers 1136 $WordsForNumbers = array(
1141 '/^three /i' =>
'3 ',
1142 '/^four /i' =>
'4 ',
1143 '/^five /i' =>
'5 ',
1145 '/^seven /i' =>
'7 ',
1146 '/^eight /i' =>
'8 ',
1147 '/^nine /i' =>
'9 ',
1148 '/^ten /i' =>
'10 ',
1149 '/^eleven /i' =>
'11 ',
1150 '/^twelve /i' =>
'12 ',
1151 '/^thirteen /i' =>
'13 ',
1152 '/^fourteen /i' =>
'14 ',
1153 '/^fifteen /i' =>
'15 ',
1154 '/^sixteen /i' =>
'16 ',
1155 '/^seventeen /i' =>
'17 ',
1156 '/^eighteen /i' =>
'18 ',
1157 '/^nineteen /i' =>
'19 ',
1158 '/^twenty /i' =>
'20 ',
1159 '/^thirty /i' =>
'30 ',
1160 '/^forty /i' =>
'40 ',
1161 '/^fourty /i' =>
'40 ', # (common misspelling)
1162 '/^fifty /i' =>
'50 ',
1163 '/^sixty /i' =>
'60 ',
1164 '/^seventy /i' =>
'70 ',
1165 '/^eighty /i' =>
'80 ',
1166 '/^ninety /i' =>
'90 ');
1167 $Value = preg_replace(
1168 array_keys($WordsForNumbers), $WordsForNumbers, $Value);
1170 # use strtotime method to build condition 1171 $TimestampValue = strtotime($Value);
1172 if (($TimestampValue !== FALSE) && ($TimestampValue != -1))
1174 if ((date(
"H:i:s", $TimestampValue) ==
"00:00:00")
1175 && (strpos($Value,
"00:00") === FALSE)
1176 && ($Operator ==
"<="))
1179 date(
"Y-m-d", $TimestampValue).
" 23:59:59";
1183 $NormalizedValue = date(
1184 "Y-m-d H:i:s", $TimestampValue);
1189 $NormalizedValue = addslashes($Value);
1192 # build SQL conditional 1193 if ($ModificationComparison)
1195 $Conditional =
" ( FieldId = ".$Field->Id()
1196 .
" AND Timestamp ".$Operator
1197 .
" '".$NormalizedValue.
"' ) ";
1201 $Conditional =
" ( `".$Field->DBFieldName().
"` " 1202 .$Operator.
" '".$NormalizedValue.
"' ) ";
1207 # adjust operator if necessary 1208 if ($ModificationComparison)
1210 $Operator = ($Operator ==
"@") ?
">=" 1211 : substr($Operator, 1);
1214 # use Date object method to build conditional 1215 $Date =
new Date($Value);
1216 if ($Date->Precision())
1218 if ($ModificationComparison)
1220 $Conditional =
" ( FieldId = ".$Field->Id()
1221 .
" AND ".$Date->SqlCondition(
1222 "Timestamp", NULL, $Operator).
" ) ";
1226 $Conditional =
" ( ".$Date->SqlCondition(
1227 $Field->DBFieldName(), NULL, $Operator).
" ) ";
1232 # return assembled conditional to caller 1233 return $Conditional;
1243 private static function FindParentClass($ClassId)
1247 # first time through, fetch the mapping of parent values we need 1248 if (!isset($ParentMap))
1252 # result here will be a parent/child mapping for all used 1253 # classifications; avoid caching it as it can be quite large 1254 $PreviousSetting =
$DB->Caching();
1255 $DB->Caching(FALSE);
1257 "SELECT ParentId, ClassificationId FROM Classifications " 1258 .
"WHERE DEPTH > 0 AND FullResourceCount > 0 " 1259 .
"AND FieldId IN (SELECT FieldId FROM MetadataFields " 1260 .
" WHERE IncludeInFacetedSearch=1)" 1262 $DB->Caching($PreviousSetting);
1264 $ParentMap =
$DB->FetchColumn(
"ParentId",
"ClassificationId");
1267 return isset($ParentMap[$ClassId]) ? $ParentMap[$ClassId] : FALSE;
AddField($FieldId, $FieldType, $ItemTypes, $Weight, $UsedInKeywordSearch)
Add field to include in searching.
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 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.
Search($SearchParams, $StartingResult=0, $NumberOfResults=PHP_INT_MAX, $SortByField=NULL, $SortDescending=TRUE)
Perform search with specified parameters.
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.
static ConvertToDisplayParameters($SearchParams)
Get a simplified SearchParameterSet for display purposes.
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.
static QueueUpdateForItem($ItemOrItemId, $TaskPriority=NULL)
Queue background update for an item.