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 $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]++;
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 if (is_numeric($Value))
451 # add subquery for specific resource ID 452 $Queries[$QueryIndex] .=
"(RI.DstResourceId ".$Operator.
" '" 453 .addslashes($Value).
"')";
457 # iterate over all the schemas this field can reference, 458 # gluing together an array of subqueries for the mapped 459 # title field of each as we go 460 $SchemaIds = $Field->ReferenceableSchemaIds();
462 # if no referenceable schemas configured, fall back to 463 # searching all schemas 464 if (count($SchemaIds)==0)
469 $Subqueries = array();
470 foreach ($SchemaIds as $SchemaId)
473 $MappedTitle = $Schema->GetFieldByMappedName(
"Title");
475 $Subqueries[]= $this->GetTextComparisonSql(
476 $MappedTitle->DBFieldName(), $Operator, $Value,
"R");
479 # OR together all the subqueries, add it to the query 481 $Queries[$QueryIndex] .=
482 "((".implode(
" OR ", $Subqueries).
")" 483 .
" AND R.ResourceId = RI.DstResourceId)";
487 case "Modification Comparison":
488 # if we have an SQL conditional 489 $TimestampConditional = $this->GetTimeComparisonSql(
490 $Field, $Operator, $Value);
491 if ($TimestampConditional)
494 $QueryConditions[
"ResourceFieldTimestamps"][] =
495 $TimestampConditional;
500 throw new Exception(
"Search of unknown field type (" 501 .$ProcessingType.
").");
506 # if query conditions found 507 if (isset($QueryConditions))
509 # for each query condition group 510 foreach ($QueryConditions as $TargetTable => $Conditions)
512 # add entry with conditions to query list 513 if (isset($Queries[$TargetTable]))
515 $Queries[$TargetTable] .= $CombineWord
516 .implode($CombineWord, $Conditions);
520 $Queries[$TargetTable] =
"SELECT DISTINCT ResourceId" 521 .
" FROM ".$TargetTable.
" WHERE " 522 .implode($CombineWord, $Conditions);
530 # for each assembled query 531 foreach ($Queries as $QueryIndex => $Query)
533 # if query has multiple parts 534 if (is_array($Query))
536 # for each part of query 537 $ResourceIds = array();
538 foreach ($Query as $PartIndex => $PartQuery)
540 # add closing paren if query was flagged to be closed 541 if (isset($CloseQuery[$QueryIndex][$PartIndex]))
544 if (($Logic ==
"AND")
545 && ($ComparisonCount[$QueryIndex][$PartIndex] > 1))
547 $PartQuery .=
"GROUP BY ResourceId" 548 .
" HAVING COUNT(DISTINCT " 549 .$ComparisonCountField[$QueryIndex][$PartIndex]
551 .$ComparisonCount[$QueryIndex][$PartIndex];
555 # perform query and retrieve IDs 556 $this->
DMsg(5,
"Performing comparison query <i>" 558 $this->DB->Query($PartQuery);
559 $ResourceIds = $ResourceIds
560 + $this->DB->FetchColumn(
"ResourceId");
561 $this->
DMsg(5,
"Comparison query produced <i>" 562 .count($ResourceIds).
"</i> results");
567 # add closing paren if query was flagged to be closed 568 if (isset($CloseQuery[$QueryIndex]))
571 if (($Logic ==
"Logic")
572 && ($ComparisonCount[$QueryIndex] > 1))
574 $Query .=
"GROUP BY ResourceId" 575 .
" HAVING COUNT(DISTINCT " 576 .$ComparisonCountField[$QueryIndex]
578 .$ComparisonCount[$QueryIndex];
582 # perform query and retrieve IDs 583 $this->
DMsg(5,
"Performing comparison query <i>".$Query.
"</i>");
584 $this->DB->Query($Query);
585 $ResourceIds = $this->DB->FetchColumn(
"ResourceId");
586 $this->
DMsg(5,
"Comparison query produced <i>" 587 .count($ResourceIds).
"</i> results");
590 # if we already have some results 593 # if search logic is set to AND 596 # remove anything from results that was not returned from query 597 $Results = array_intersect($Results, $ResourceIds);
601 # add values returned from query to results 602 $Results = array_unique(array_merge($Results, $ResourceIds));
607 # set results to values returned from query 608 $Results = $ResourceIds;
614 # initialize results to empty list 618 # return results to caller 631 $ItemType, $FieldId, $SortDescending)
634 return $RFactory->GetResourceIdsSortedBy($FieldId, !$SortDescending);
645 if (is_numeric($ItemOrItemId))
647 $ItemId = $ItemOrItemId;
652 $Item = $ItemOrItemId;
653 $ItemId = $Item->Id();
656 # if no priority was provided, use the default 657 if ($TaskPriority === NULL)
659 $TaskPriority = self::$TaskPriority;
662 # assemble task description 663 $Title = $Item->GetMapped(
"Title");
666 $Title =
"Item #".$ItemId;
668 $TaskDescription =
"Update search data for" 669 .
" <a href=\"r".$ItemId.
"\"><i>" 673 $GLOBALS[
"AF"]->QueueUniqueTask(array(__CLASS__,
"RunUpdateForItem"),
674 array(intval($ItemId)), $TaskPriority, $TaskDescription);
683 # bail out if item no longer exists 688 catch (InvalidArgumentException $Exception)
693 # bail out if item is a temporary record 694 if ($Resource->IsTempResource()) {
return; }
696 # retrieve schema ID of item to use for item type 697 $ItemType = $Resource->SchemaId();
699 # update search data for resource 701 $SearchEngine->UpdateForItem($ItemId, $ItemType);
714 # classifications and names associated with these search results 715 $SearchClasses = array();
716 $SearchNames = array();
718 # make sure we're not faceting too many resources 719 $SearchResults = array_slice(
721 self::$NumResourcesForFacets,
724 # disable DB cache for the search suggestions process, 725 # this avoids memory exhaustion. 729 # number of resources to include in a chunk 730 # a mysql BIGINT is at most 21 characters long and the 731 # default max_packet_size is 1 MiB, so we can pack about 732 # 1 MiB / (22 bytes) = 47,663 ResourceIds into a query before 733 # we need to worry about length problems 736 if (count($SearchResults)>0)
738 foreach (array_chunk($SearchResults, $ChunkSize, TRUE) as $Chunk)
740 # pull out all the Classifications that were associated 741 # with our search results along with all their parents 742 $DB->Query(
"SELECT ResourceId,ClassificationId FROM ResourceClassInts " 743 .
"WHERE ResourceId IN " 744 .
"(".implode(
",", array_keys($Chunk)).
")");
745 $Rows =
$DB->FetchRows();
746 foreach ($Rows as $Row)
748 $CurId = $Row[
"ClassificationId"];
749 while ($CurId !== FALSE)
751 $SearchClasses[$CurId][]=$Row[
"ResourceId"] ;
752 $CurId = self::FindParentClass($CurId);
756 # also pull out controlled names 757 $DB->Query(
"SELECT ResourceId,ControlledNameId FROM ResourceNameInts " 758 .
"WHERE ResourceId in " 759 .
"(".implode(
",", array_keys($Chunk)).
")");
760 $Rows =
$DB->FetchRows();
761 foreach ($Rows as $Row)
763 $SearchNames[$Row[
"ControlledNameId"]][]= $Row[
"ResourceId"];
767 # make sure we haven't double-counted resources that have 768 # a classification and some of its children assigned 769 $TmpClasses = array();
770 foreach ($SearchClasses as $ClassId => $Resources)
772 $TmpClasses[$ClassId] = array_unique($Resources);
774 $SearchClasses = $TmpClasses;
777 # generate a map of FieldId -> Field Names for all of the generated facets: 778 $SuggestionsById = array();
780 # pull relevant Classification names out of the DB 781 if (count($SearchClasses)>0)
783 foreach (array_chunk($SearchClasses, $ChunkSize, TRUE) as $Chunk)
785 $DB->Query(
"SELECT FieldId,ClassificationId,ClassificationName" 786 .
" FROM Classifications" 787 .
" WHERE ClassificationId" 788 .
" IN (".implode(
",", array_keys($Chunk)).
")");
789 foreach (
$DB->FetchRows() as $Row)
791 $SuggestionsById[$Row[
"FieldId"]][]=
792 array(
"Id" => $Row[
"ClassificationId"],
793 "Name" => $Row[
"ClassificationName"],
795 $SearchClasses[$Row[
"ClassificationId"]]));
800 # pull relevant ControlledNames out of the DB 801 if (count($SearchNames)>0)
803 foreach (array_chunk($SearchNames, $ChunkSize, TRUE) as $Chunk)
805 $DB->Query(
"SELECT FieldId,ControlledNameId,ControlledName" 806 .
" FROM ControlledNames" 807 .
" WHERE ControlledNameId" 808 .
" IN (".implode(
",", array_keys($SearchNames)).
")");
809 foreach (
$DB->FetchRows() as $Row)
811 $SuggestionsById[$Row[
"FieldId"]][]=
812 array(
"Id" => $Row[
"ControlledNameId"],
813 "Name" => $Row[
"ControlledName"],
815 $SearchNames[$Row[
"ControlledNameId"]]));
820 # translate the suggestions that we have in terms of the 821 # FieldIds to suggestions in terms of the field names 822 $SuggestionsByFieldName = array();
824 # if we have suggestions to offer 825 if (count($SuggestionsById)>0)
827 # gill in an array that maps FieldNames to search links 828 # which would be appropriate for that field 829 foreach ($SuggestionsById as $FieldId => $FieldValues)
835 catch (Exception $Exception)
840 # bail on fields that didn't exist and on fields that the 841 # current user cannot view, and on fields that are 842 # disabled for advanced searching 843 if (is_object($ThisField) &&
845 $ThisField->IncludeInFacetedSearch() &&
846 $ThisField->Enabled() &&
847 $User->HasPriv($ThisField->ViewingPrivileges()))
849 $SuggestionsByFieldName[$ThisField->Name()] = array();
851 foreach ($FieldValues as $Value)
853 $SuggestionsByFieldName[$ThisField->Name()][$Value[
"Id"]] =
854 array(
"Name" => $Value[
"Name"],
"Count" => $Value[
"Count"] );
860 ksort($SuggestionsByFieldName);
862 return $SuggestionsByFieldName;
872 self::$TaskPriority = $NewPriority;
881 self::$NumResourcesForFacets = $NumToUse;
884 # ---- BACKWARD COMPATIBILITY -------------------------------------------- 906 $SearchGroups, $StartingResult = 0, $NumberOfResults = 10,
907 $SortByField = NULL, $SortDescending = TRUE)
911 # if search parameter set was passed in, use it directly 912 $SearchParams = $SearchGroups;
916 # otherwise, convert legacy array into SearchParameterSet 917 $SearchParams =
new SearchParameterSet();
918 $SearchParams->SetFromLegacyArray($SearchGroups);
923 $SearchParams, $StartingResult, $NumberOfResults,
924 $SortByField, $SortDescending);
926 # pull out the resoults for the Resource schema 961 # create display parameters, used to make a more user-friendly 962 # version of the search 965 # copy keyword searches as is 966 $DisplayParams->AddParameter(
967 $SearchParams->GetKeywordSearchStrings() );
969 # copy field searches as is 970 $SearchStrings = $SearchParams->GetSearchStrings();
971 foreach ($SearchStrings as $FieldId => $Params)
973 $DisplayParams->AddParameter($Params, $FieldId);
976 # iterate over the search groups, looking for the 'is or begins 977 # with' group that we add when faceting and displaying them as 978 # IS parameters rather than the literal subgroup that we 980 $Groups = $SearchParams->GetSubgroups();
981 foreach ($Groups as $Group)
983 # start off assuming that we'll just copy the group without conversion 986 # if this group uses OR logic for a single field, then it 987 # might be one of the subgroups we want to match and will require further 989 if ($Group->Logic() ==
"OR" &&
990 count($Group->GetFields()) == 1)
992 # pull out the search strings for this field 993 $SearchStrings = $Group->GetSearchStrings();
994 $FieldId = key($SearchStrings);
995 $Values = current($SearchStrings);
997 # check if there are two search strings, one an 'is' 998 # and the other a 'begins with' that both start with the 999 # same prefix, as would be added by the search facet code 1000 if ( count($Values) == 2 &&
1001 preg_match(
'/^=(.*)$/', $Values[0], $FirstMatch) &&
1002 preg_match(
'/^\\^(.*) -- $/', $Values[1], $SecondMatch) &&
1003 $FirstMatch[1] == $SecondMatch[1] )
1005 # check if this field is valid anywhere 1010 # and check if this field is a tree field 1013 # okay, this group matches the form that 1014 # the faceting code would add for an 'is or 1015 # begins with' group; convert it to just an 1016 # 'is' group for display 1017 $DisplayParams->AddParameter(
"=".$FirstMatch[1], $FieldId);
1024 # if this group didn't require conversion, attempt to copy 1030 $DisplayParams->AddSet($Group);
1032 catch (Exception $e)
1034 # if group could not be added for any reason, skip 1035 # it and move on to the next group 1040 return $DisplayParams;
1043 # ---- PRIVATE INTERFACE ------------------------------------------------- 1045 private $FieldTypes;
1048 private static $TaskPriority = ApplicationFramework::PRIORITY_BACKGROUND;
1049 private static $NumResourcesForFacets = 500;
1059 private function GetTextComparisonSql($DBField, $Operator, $Value, $Prefix=
"")
1061 # if we were given a prefix, add the necessary period so we can use it 1062 if (strlen($Prefix))
1064 $Prefix = $Prefix.
".";
1067 if ($Operator ==
"^")
1069 $EscapedValue = str_replace(
1072 addslashes($Value));
1073 $Comparison = $Prefix.
"`".$DBField.
"` LIKE '".$EscapedValue.
"%' ";
1075 elseif ($Operator ==
'$')
1077 $EscapedValue = str_replace(
1080 addslashes($Value));
1081 $Comparison = $Prefix.
"`".$DBField.
"` LIKE '%".$EscapedValue.
"' ";
1083 elseif ($Operator ==
'!=')
1086 "(".$Prefix.
"`".$DBField.
"` ".$Operator.
" '".addslashes($Value).
"'" 1087 .
" AND ".$Prefix.
"`".$DBField.
"` IS NOT NULL)";
1091 $Comparison = $Prefix.
"`".$DBField.
"` " 1092 .$Operator.
" '".addslashes($Value).
"' ";
1105 private function GetTimeComparisonSql($Field, $Operator, $Value)
1107 # check if this is a field modification comparison 1108 $ModificationComparison = ($Operator{0} ==
"@") ? TRUE : FALSE;
1110 # if value appears to have time component or text description 1111 if (strpos($Value,
":")
1112 || strstr($Value,
"day")
1113 || strstr($Value,
"week")
1114 || strstr($Value,
"month")
1115 || strstr($Value,
"year")
1116 || strstr($Value,
"hour")
1117 || strstr($Value,
"minute"))
1119 # adjust operator if necessary 1120 if ($Operator ==
"@")
1126 if ($ModificationComparison)
1128 $Operator = substr($Operator, 1);
1131 if (strstr($Value,
"ago"))
1133 $OperatorFlipMap = array(
1139 $Operator = isset($OperatorFlipMap[$Operator])
1140 ? $OperatorFlipMap[$Operator] : $Operator;
1144 # translate common words-to-numbers 1145 $WordsForNumbers = array(
1150 '/^three /i' =>
'3 ',
1151 '/^four /i' =>
'4 ',
1152 '/^five /i' =>
'5 ',
1154 '/^seven /i' =>
'7 ',
1155 '/^eight /i' =>
'8 ',
1156 '/^nine /i' =>
'9 ',
1157 '/^ten /i' =>
'10 ',
1158 '/^eleven /i' =>
'11 ',
1159 '/^twelve /i' =>
'12 ',
1160 '/^thirteen /i' =>
'13 ',
1161 '/^fourteen /i' =>
'14 ',
1162 '/^fifteen /i' =>
'15 ',
1163 '/^sixteen /i' =>
'16 ',
1164 '/^seventeen /i' =>
'17 ',
1165 '/^eighteen /i' =>
'18 ',
1166 '/^nineteen /i' =>
'19 ',
1167 '/^twenty /i' =>
'20 ',
1168 '/^thirty /i' =>
'30 ',
1169 '/^forty /i' =>
'40 ',
1170 '/^fourty /i' =>
'40 ', # (common misspelling)
1171 '/^fifty /i' =>
'50 ',
1172 '/^sixty /i' =>
'60 ',
1173 '/^seventy /i' =>
'70 ',
1174 '/^eighty /i' =>
'80 ',
1175 '/^ninety /i' =>
'90 ');
1176 $Value = preg_replace(
1177 array_keys($WordsForNumbers), $WordsForNumbers, $Value);
1179 # use strtotime method to build condition 1180 $TimestampValue = strtotime($Value);
1181 if (($TimestampValue !== FALSE) && ($TimestampValue != -1))
1183 if ((date(
"H:i:s", $TimestampValue) ==
"00:00:00")
1184 && (strpos($Value,
"00:00") === FALSE)
1185 && ($Operator ==
"<="))
1188 date(
"Y-m-d", $TimestampValue).
" 23:59:59";
1192 $NormalizedValue = date(
1193 "Y-m-d H:i:s", $TimestampValue);
1198 $NormalizedValue = addslashes($Value);
1201 # build SQL conditional 1202 if ($ModificationComparison)
1204 $Conditional =
" ( FieldId = ".$Field->Id()
1205 .
" AND Timestamp ".$Operator
1206 .
" '".$NormalizedValue.
"' ) ";
1210 $Conditional =
" ( `".$Field->DBFieldName().
"` " 1211 .$Operator.
" '".$NormalizedValue.
"' ) ";
1216 # adjust operator if necessary 1217 if ($ModificationComparison)
1219 $Operator = ($Operator ==
"@") ?
">=" 1220 : substr($Operator, 1);
1223 # use Date object method to build conditional 1224 $Date =
new Date($Value);
1225 if ($Date->Precision())
1227 if ($ModificationComparison)
1229 $Conditional =
" ( FieldId = ".$Field->Id()
1230 .
" AND ".$Date->SqlCondition(
1231 "Timestamp", NULL, $Operator).
" ) ";
1235 $Conditional =
" ( ".$Date->SqlCondition(
1236 $Field->DBFieldName(), NULL, $Operator).
" ) ";
1241 # return assembled conditional to caller 1242 return $Conditional;
1253 private function GetUserComparisonSql(
1254 $FieldId, $Operator, $UserId)
1259 return "(UserId = ".intval($UserId).
" AND FieldId = " 1260 .intval($FieldId).
")";
1264 return "(UserId != ".intval($UserId).
" AND FieldId = " 1265 .intval($FieldId).
")";
1269 throw new Exception(
1270 "Operator ".$Operator.
" is not supported for User fields");
1282 private static function FindParentClass($ClassId)
1286 # first time through, fetch the mapping of parent values we need 1287 if (!isset($ParentMap))
1291 # result here will be a parent/child mapping for all used 1292 # classifications; avoid caching it as it can be quite large 1293 $PreviousSetting =
$DB->Caching();
1294 $DB->Caching(FALSE);
1296 "SELECT ParentId, ClassificationId FROM Classifications " 1297 .
"WHERE DEPTH > 0 AND FullResourceCount > 0 " 1298 .
"AND FieldId IN (SELECT FieldId FROM MetadataFields " 1299 .
" WHERE IncludeInFacetedSearch=1)" 1301 $DB->Caching($PreviousSetting);
1303 $ParentMap =
$DB->FetchColumn(
"ParentId",
"ClassificationId");
1306 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.