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".$FieldId][] =
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 677 # bail out if item is a temporary record 678 if ($Resource->IsTempResource()) {
return; }
680 # retrieve schema ID of item to use for item type 681 $ItemType = $Resource->SchemaId();
683 # update search data for resource 685 $SearchEngine->UpdateForItem($ItemId, $ItemType);
698 # classifications and names associated with these search results 699 $SearchClasses = array();
700 $SearchNames = array();
702 # make sure we're not faceting too many resources 703 $SearchResults = array_slice(
705 self::$NumResourcesForFacets,
708 # disable DB cache for the search suggestions process, 709 # this avoids memory exhaustion. 713 # number of resources to include in a chunk 714 # a mysql BIGINT is at most 21 characters long and the 715 # default max_packet_size is 1 MiB, so we can pack about 716 # 1 MiB / (22 bytes) = 47,663 ResourceIds into a query before 717 # we need to worry about length problems 720 if (count($SearchResults)>0)
722 foreach (array_chunk($SearchResults, $ChunkSize, TRUE) as $Chunk)
724 # pull out all the Classifications that were associated 725 # with our search results along with all their parents 726 $DB->Query(
"SELECT ResourceId,ClassificationId FROM ResourceClassInts " 727 .
"WHERE ResourceId IN " 728 .
"(".implode(
",", array_keys($Chunk)).
")");
729 $Rows =
$DB->FetchRows();
730 foreach ($Rows as $Row)
732 $CurId = $Row[
"ClassificationId"];
733 while ($CurId !== FALSE)
735 $SearchClasses[$CurId][]=$Row[
"ResourceId"] ;
736 $CurId = self::FindParentClass($CurId);
740 # also pull out controlled names 741 $DB->Query(
"SELECT ResourceId,ControlledNameId FROM ResourceNameInts " 742 .
"WHERE ResourceId in " 743 .
"(".implode(
",", array_keys($Chunk)).
")");
744 $Rows =
$DB->FetchRows();
745 foreach ($Rows as $Row)
747 $SearchNames[$Row[
"ControlledNameId"]][]= $Row[
"ResourceId"];
751 # make sure we haven't double-counted resources that have 752 # a classification and some of its children assigned 753 $TmpClasses = array();
754 foreach ($SearchClasses as $ClassId => $Resources)
756 $TmpClasses[$ClassId] = array_unique($Resources);
758 $SearchClasses = $TmpClasses;
761 # generate a map of FieldId -> Field Names for all of the generated facets: 762 $SuggestionsById = array();
764 # pull relevant Classification names out of the DB 765 if (count($SearchClasses)>0)
767 foreach (array_chunk($SearchClasses, $ChunkSize, TRUE) as $Chunk)
769 $DB->Query(
"SELECT FieldId,ClassificationId,ClassificationName" 770 .
" FROM Classifications" 771 .
" WHERE ClassificationId" 772 .
" IN (".implode(
",", array_keys($Chunk)).
")");
773 foreach (
$DB->FetchRows() as $Row)
775 $SuggestionsById[$Row[
"FieldId"]][]=
776 array(
"Id" => $Row[
"ClassificationId"],
777 "Name" => $Row[
"ClassificationName"],
779 $SearchClasses[$Row[
"ClassificationId"]]));
784 # pull relevant ControlledNames out of the DB 785 if (count($SearchNames)>0)
787 foreach (array_chunk($SearchNames, $ChunkSize, TRUE) as $Chunk)
789 $DB->Query(
"SELECT FieldId,ControlledNameId,ControlledName" 790 .
" FROM ControlledNames" 791 .
" WHERE ControlledNameId" 792 .
" IN (".implode(
",", array_keys($SearchNames)).
")");
793 foreach (
$DB->FetchRows() as $Row)
795 $SuggestionsById[$Row[
"FieldId"]][]=
796 array(
"Id" => $Row[
"ControlledNameId"],
797 "Name" => $Row[
"ControlledName"],
799 $SearchNames[$Row[
"ControlledNameId"]]));
804 # translate the suggestions that we have in terms of the 805 # FieldIds to suggestions in terms of the field names 806 $SuggestionsByFieldName = array();
808 # if we have suggestions to offer 809 if (count($SuggestionsById)>0)
811 # gill in an array that maps FieldNames to search links 812 # which would be appropriate for that field 813 foreach ($SuggestionsById as $FieldId => $FieldValues)
819 catch (Exception $Exception)
824 # bail on fields that didn't exist and on fields that the 825 # current user cannot view, and on fields that are 826 # disabled for advanced searching 827 if (is_object($ThisField) &&
829 $ThisField->IncludeInFacetedSearch() &&
830 $ThisField->Enabled() &&
831 $User->HasPriv($ThisField->ViewingPrivileges()))
833 $SuggestionsByFieldName[$ThisField->Name()] = array();
835 foreach ($FieldValues as $Value)
837 $SuggestionsByFieldName[$ThisField->Name()][$Value[
"Id"]] =
838 array(
"Name" => $Value[
"Name"],
"Count" => $Value[
"Count"] );
844 ksort($SuggestionsByFieldName);
846 return $SuggestionsByFieldName;
856 self::$TaskPriority = $NewPriority;
865 self::$NumResourcesForFacets = $NumToUse;
868 # ---- BACKWARD COMPATIBILITY -------------------------------------------- 890 $SearchGroups, $StartingResult = 0, $NumberOfResults = 10,
891 $SortByField = NULL, $SortDescending = TRUE)
895 # if search parameter set was passed in, use it directly 896 $SearchParams = $SearchGroups;
900 # otherwise, convert legacy array into SearchParameterSet 901 $SearchParams =
new SearchParameterSet();
902 $SearchParams->SetFromLegacyArray($SearchGroups);
907 $SearchParams, $StartingResult, $NumberOfResults,
908 $SortByField, $SortDescending);
910 # pull out the resoults for the Resource schema 945 # create display parameters, used to make a more user-friendly 946 # version of the search 949 # copy keyword searches as is 950 $DisplayParams->AddParameter(
951 $SearchParams->GetKeywordSearchStrings() );
953 # copy field searches as is 954 $SearchStrings = $SearchParams->GetSearchStrings();
955 foreach ($SearchStrings as $FieldId => $Params)
957 $DisplayParams->AddParameter($Params, $FieldId);
960 # iterate over the search groups, looking for the 'is or begins 961 # with' group that we add when faceting and displaying them as 962 # IS parameters rather than the literal subgroup that we 964 $Groups = $SearchParams->GetSubgroups();
965 foreach ($Groups as $Group)
967 # start off assuming that we'll just copy the group without conversion 970 # if this group uses OR logic for a single field, then it 971 # might be one of the subgroups we want to match and will require further 973 if ($Group->Logic() ==
"OR" &&
974 count($Group->GetFields()) == 1)
976 # pull out the search strings for this field 977 $SearchStrings = $Group->GetSearchStrings();
978 $FieldId = key($SearchStrings);
979 $Values = current($SearchStrings);
981 # check if there are two search strings, one an 'is' 982 # and the other a 'begins with' that both start with the 983 # same prefix, as would be added by the search facet code 984 if ( count($Values) == 2 &&
985 preg_match(
'/^=(.*)$/', $Values[0], $FirstMatch) &&
986 preg_match(
'/^\\^(.*) -- $/', $Values[1], $SecondMatch) &&
987 $FirstMatch[1] == $SecondMatch[1] )
989 # check if this field is valid anywhere 994 # and check if this field is a tree field 997 # okay, this group matches the form that 998 # the faceting code would add for an 'is or 999 # begins with' group; convert it to just an 1000 # 'is' group for display 1001 $DisplayParams->AddParameter(
"=".$FirstMatch[1], $FieldId);
1008 # if this group didn't require conversion, attempt to copy 1014 $DisplayParams->AddSet($Group);
1016 catch (Exception $e)
1018 # if group could not be added for any reason, skip 1019 # it and move on to the next group 1024 return $DisplayParams;
1027 # ---- PRIVATE INTERFACE ------------------------------------------------- 1029 private $FieldTypes;
1033 private static $NumResourcesForFacets = 500;
1043 private function GetTextComparisonSql($DBField, $Operator, $Value, $Prefix=
"")
1045 # if we were given a prefix, add the necessary period so we can use it 1046 if (strlen($Prefix))
1048 $Prefix = $Prefix.
".";
1051 if ($Operator ==
"^")
1053 $EscapedValue = str_replace(
1056 addslashes($Value));
1057 $Comparison = $Prefix.
"`".$DBField.
"` LIKE '".$EscapedValue.
"%' ";
1059 elseif ($Operator ==
'$')
1061 $EscapedValue = str_replace(
1064 addslashes($Value));
1065 $Comparison = $Prefix.
"`".$DBField.
"` LIKE '%".$EscapedValue.
"' ";
1067 elseif ($Operator ==
'!=')
1070 "(".$Prefix.
"`".$DBField.
"` ".$Operator.
" '".addslashes($Value).
"'" 1071 .
" AND ".$Prefix.
"`".$DBField.
"` IS NOT NULL)";
1075 $Comparison = $Prefix.
"`".$DBField.
"` " 1076 .$Operator.
" '".addslashes($Value).
"' ";
1089 private function GetTimeComparisonSql($Field, $Operator, $Value)
1091 # check if this is a field modification comparison 1092 $ModificationComparison = ($Operator{0} ==
"@") ? TRUE : FALSE;
1094 # if value appears to have time component or text description 1095 if (strpos($Value,
":")
1096 || strstr($Value,
"day")
1097 || strstr($Value,
"week")
1098 || strstr($Value,
"month")
1099 || strstr($Value,
"year")
1100 || strstr($Value,
"hour")
1101 || strstr($Value,
"minute"))
1103 # adjust operator if necessary 1104 if ($Operator ==
"@")
1110 if ($ModificationComparison)
1112 $Operator = substr($Operator, 1);
1115 if (strstr($Value,
"ago"))
1117 $OperatorFlipMap = array(
1123 $Operator = isset($OperatorFlipMap[$Operator])
1124 ? $OperatorFlipMap[$Operator] : $Operator;
1128 # translate common words-to-numbers 1129 $WordsForNumbers = array(
1134 '/^three /i' =>
'3 ',
1135 '/^four /i' =>
'4 ',
1136 '/^five /i' =>
'5 ',
1138 '/^seven /i' =>
'7 ',
1139 '/^eight /i' =>
'8 ',
1140 '/^nine /i' =>
'9 ',
1141 '/^ten /i' =>
'10 ',
1142 '/^eleven /i' =>
'11 ',
1143 '/^twelve /i' =>
'12 ',
1144 '/^thirteen /i' =>
'13 ',
1145 '/^fourteen /i' =>
'14 ',
1146 '/^fifteen /i' =>
'15 ',
1147 '/^sixteen /i' =>
'16 ',
1148 '/^seventeen /i' =>
'17 ',
1149 '/^eighteen /i' =>
'18 ',
1150 '/^nineteen /i' =>
'19 ',
1151 '/^twenty /i' =>
'20 ',
1152 '/^thirty /i' =>
'30 ',
1153 '/^forty /i' =>
'40 ',
1154 '/^fourty /i' =>
'40 ', # (common misspelling)
1155 '/^fifty /i' =>
'50 ',
1156 '/^sixty /i' =>
'60 ',
1157 '/^seventy /i' =>
'70 ',
1158 '/^eighty /i' =>
'80 ',
1159 '/^ninety /i' =>
'90 ');
1160 $Value = preg_replace(
1161 array_keys($WordsForNumbers), $WordsForNumbers, $Value);
1163 # use strtotime method to build condition 1164 $TimestampValue = strtotime($Value);
1165 if (($TimestampValue !== FALSE) && ($TimestampValue != -1))
1167 if ((date(
"H:i:s", $TimestampValue) ==
"00:00:00")
1168 && (strpos($Value,
"00:00") === FALSE)
1169 && ($Operator ==
"<="))
1172 date(
"Y-m-d", $TimestampValue).
" 23:59:59";
1176 $NormalizedValue = date(
1177 "Y-m-d H:i:s", $TimestampValue);
1182 $NormalizedValue = addslashes($Value);
1185 # build SQL conditional 1186 if ($ModificationComparison)
1188 $Conditional =
" ( FieldId = ".$Field->Id()
1189 .
" AND Timestamp ".$Operator
1190 .
" '".$NormalizedValue.
"' ) ";
1194 $Conditional =
" ( `".$Field->DBFieldName().
"` " 1195 .$Operator.
" '".$NormalizedValue.
"' ) ";
1200 # adjust operator if necessary 1201 if ($ModificationComparison)
1203 $Operator = ($Operator ==
"@") ?
">=" 1204 : substr($Operator, 1);
1207 # use Date object method to build conditional 1208 $Date =
new Date($Value);
1209 if ($Date->Precision())
1211 if ($ModificationComparison)
1213 $Conditional =
" ( FieldId = ".$Field->Id()
1214 .
" AND ".$Date->SqlCondition(
1215 "Timestamp", NULL, $Operator).
" ) ";
1219 $Conditional =
" ( ".$Date->SqlCondition(
1220 $Field->DBFieldName(), NULL, $Operator).
" ) ";
1225 # return assembled conditional to caller 1226 return $Conditional;
1236 private static function FindParentClass($ClassId)
1240 # first time through, fetch the mapping of parent values we need 1241 if (!isset($ParentMap))
1245 # result here will be a parent/child mapping for all used 1246 # classifications; avoid caching it as it can be quite large 1247 $PreviousSetting =
$DB->Caching();
1248 $DB->Caching(FALSE);
1250 "SELECT ParentId, ClassificationId FROM Classifications " 1251 .
"WHERE DEPTH > 0 AND FullResourceCount > 0 " 1252 .
"AND FieldId IN (SELECT FieldId FROM MetadataFields " 1253 .
" WHERE IncludeInFacetedSearch=1)" 1255 $DB->Caching($PreviousSetting);
1257 $ParentMap =
$DB->FetchColumn(
"ParentId",
"ClassificationId");
1260 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.
const PRIORITY_BACKGROUND
Lowest priority.
static QueueUpdateForItem($ItemOrItemId, $TaskPriority=NULL)
Queue background update for an item.