Commit 80b375de4845fe5875fc92c52f727c1e7ffab991
1 parent
5bb8b475
KTS-673
"The search algorithm needs some work" Implemented. Committed By: Conrad Vermeulen Reviewed By: Kevin Fourie git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@7144 c91229c3-7414-0410-bfa2-8a42b809f60b
Showing
66 changed files
with
8082 additions
and
0 deletions
search2/ajax/ajax.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +require_once('../../config/dmsDefaults.php'); | |
| 4 | +session_start(); | |
| 5 | + | |
| 6 | +require_once(KT_DIR . '/search2/search/search.inc.php'); | |
| 7 | + | |
| 8 | + | |
| 9 | +class AjaxSearchHelper | |
| 10 | +{ | |
| 11 | + const STATUS_SUCCESS = 0; | |
| 12 | + const STATUS_MISSING_QUERY = 1; | |
| 13 | + const STATUS_SESSION = 3; | |
| 14 | + const STATUS_MISSING_WHAT = 4; | |
| 15 | + const STATUS_MISSING_FIELDSET = 5; | |
| 16 | + const STATUS_INTERNAL = 99; | |
| 17 | + const STATUS_PARSE_PROBLEM = 20; | |
| 18 | + const STATUS_MISSING_SAVED = 6; | |
| 19 | + const STATUS_MISSING_NAME = 7; | |
| 20 | + const STATUS_SAVED_SEARCH_EXISTS = 30; | |
| 21 | + const STATUS_MISSING_DOCUMENT_TYPE = 8; | |
| 22 | + const STATUS_MISSING_FOLDER = 9; | |
| 23 | + | |
| 24 | + | |
| 25 | + public static function checkVar($var, $code, $message) | |
| 26 | + { | |
| 27 | + if (empty($var)) | |
| 28 | + { | |
| 29 | + AjaxSearchHelper::createResponse($code,$message); | |
| 30 | + } | |
| 31 | + return $var; | |
| 32 | + } | |
| 33 | + | |
| 34 | + public static function checkPOST($var, $code, $message) | |
| 35 | + { | |
| 36 | + return AjaxSearchHelper::checkVar($_GET[$var], $code, $message); | |
| 37 | + } | |
| 38 | + | |
| 39 | + public static function checkSESSION($var, $code, $message) | |
| 40 | + { | |
| 41 | + return AjaxSearchHelper::checkVar($_SESSION[$var], $code, $message); | |
| 42 | + } | |
| 43 | + | |
| 44 | + public static function getSessionUser() | |
| 45 | + { | |
| 46 | + return AjaxSearchHelper::checkSESSION('userID', AjaxSearchHelper::STATUS_SESSION , _kt('Session has expired.')); | |
| 47 | + } | |
| 48 | + | |
| 49 | + public static function checkGET($var, $code, $message) | |
| 50 | + { | |
| 51 | + return AjaxSearchHelper::checkVar($_GET[$var], $code, $message); | |
| 52 | + } | |
| 53 | + | |
| 54 | + | |
| 55 | + public static function createResponse($status, $message=null, $rsName=null,$rs=null) | |
| 56 | + { | |
| 57 | + $resp = array('status'=>$status); | |
| 58 | + if (isset($message)) | |
| 59 | + { | |
| 60 | + $resp['message'] = $message; | |
| 61 | + } | |
| 62 | + if (isset($rsName)) | |
| 63 | + { | |
| 64 | + $resp[$rsName] = $rs; | |
| 65 | + } | |
| 66 | + print json_encode($resp); | |
| 67 | + exit; | |
| 68 | + } | |
| 69 | + | |
| 70 | + public static function parseQuery($txtQuery, $exitOnSuccess=true) | |
| 71 | + { | |
| 72 | + try | |
| 73 | + { | |
| 74 | + $expr = parseExpression($txtQuery); | |
| 75 | + if ($exitOnSuccess) | |
| 76 | + { | |
| 77 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS ); | |
| 78 | + } | |
| 79 | + return $expr; | |
| 80 | + } | |
| 81 | + catch(Exception $e) | |
| 82 | + { | |
| 83 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_PARSE_PROBLEM , $e->getMessage()); | |
| 84 | + } | |
| 85 | + } | |
| 86 | + | |
| 87 | + public static function updateQuery($iSavedId,$txtQuery, $userID) | |
| 88 | + { | |
| 89 | + $txtQuery = sanitizeForSQL($txtQuery); | |
| 90 | + $iSavedId = sanitizeForSQL($iSavedId); | |
| 91 | + | |
| 92 | + $sql = "UPDATE search_saved SET expression='$txtQuery' WHERE id=$iSavedId"; | |
| 93 | + if (!Permission::userIsSystemAdministrator($userID)) | |
| 94 | + { | |
| 95 | + $sql .= " AND user_id = $userID"; | |
| 96 | + } | |
| 97 | + $result = DBUtil::runQuery($sql); | |
| 98 | + if (PEAR::isError($result)) | |
| 99 | + { | |
| 100 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_INTERNAL ); | |
| 101 | + } | |
| 102 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS ); | |
| 103 | + } | |
| 104 | + | |
| 105 | + | |
| 106 | + public static function saveQuery($txtName,$txtQuery, $userID) | |
| 107 | + { | |
| 108 | + $lookup = sanitizeForSQL($txtName); | |
| 109 | + $sql = "select 1 from search_saved where name='$lookup'"; | |
| 110 | + $result = DBUtil::getResultArray($sql); | |
| 111 | + if (PEAR::isError($result)) | |
| 112 | + { | |
| 113 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_INTERNAL ); | |
| 114 | + } | |
| 115 | + if (count($result) > 0) | |
| 116 | + { | |
| 117 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SAVED_SEARCH_EXISTS, _kt('Search with this name already exists') ); | |
| 118 | + } | |
| 119 | + | |
| 120 | + // autoInsert does escaping... | |
| 121 | + $values = array( | |
| 122 | + 'name'=>$txtName, | |
| 123 | + 'expression'=>$txtQuery, | |
| 124 | + 'type'=>'S', | |
| 125 | + 'shared'=>0, | |
| 126 | + 'user_id' => $userID | |
| 127 | + ); | |
| 128 | + | |
| 129 | + $result = DBUtil::autoInsert('search_saved', $values); | |
| 130 | + | |
| 131 | + if (PEAR::isError($result)) | |
| 132 | + { | |
| 133 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_INTERNAL ); | |
| 134 | + } | |
| 135 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS ); | |
| 136 | + } | |
| 137 | + | |
| 138 | + public static function getSavedSearches($userID) | |
| 139 | + { | |
| 140 | + $rs = SearchHelper::getSavedSearches($userID); | |
| 141 | + if (PEAR::isError($rs)) | |
| 142 | + { | |
| 143 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_INTERNAL ); | |
| 144 | + } | |
| 145 | + | |
| 146 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS , null, 'searches', $rs); | |
| 147 | + } | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + public static function getDocumentTypes() | |
| 152 | + { | |
| 153 | + $rs = SearchHelper::getDocumentTypes(); | |
| 154 | + if (PEAR::isError($rs)) | |
| 155 | + { | |
| 156 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_INTERNAL, $rs->getMessage() ); | |
| 157 | + } | |
| 158 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS , null, 'documenttypes', $rs); | |
| 159 | + } | |
| 160 | + | |
| 161 | + public static function getDocumentTypeFieldsets($documentTypeID) | |
| 162 | + { | |
| 163 | + $rs = SearchHelper::getDocumentTypeFieldsets($documentTypeID); | |
| 164 | + | |
| 165 | + if (PEAR::isError($rs)) | |
| 166 | + { | |
| 167 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_INTERNAL, $rs->getMessage() ); | |
| 168 | + } | |
| 169 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS , null, 'fieldsets', $rs); | |
| 170 | + } | |
| 171 | + | |
| 172 | + | |
| 173 | + public static function getFieldsets() | |
| 174 | + { | |
| 175 | + $rs = SearchHelper::getFieldsets(); | |
| 176 | + if (PEAR::isError($rs)) | |
| 177 | + { | |
| 178 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_INTERNAL, $rs->getMessage() ); | |
| 179 | + } | |
| 180 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS , null, 'fieldsets', $rs); | |
| 181 | + } | |
| 182 | + | |
| 183 | + public static function getFields($fieldsetID) | |
| 184 | + { | |
| 185 | + $result = SearchHelper::getFields($fieldsetID); | |
| 186 | + | |
| 187 | + if (PEAR::isError($result)) | |
| 188 | + { | |
| 189 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_INTERNAL, $result->getMessage() ); | |
| 190 | + } | |
| 191 | + | |
| 192 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS , null, 'fields', $result); | |
| 193 | + } | |
| 194 | + | |
| 195 | + | |
| 196 | + public static function getFolder($folderID) | |
| 197 | + { | |
| 198 | + $userid = AjaxSearchHelper::getSessionUser(); | |
| 199 | + | |
| 200 | + $folders = SearchHelper::getFolder($folderID, $userid); | |
| 201 | + if (PEAR::isError($folders)) | |
| 202 | + { | |
| 203 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_MISSING_FOLDER, $folders->getMessage() ); | |
| 204 | + } | |
| 205 | + | |
| 206 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS , null, 'folders', $folders); | |
| 207 | + | |
| 208 | + } | |
| 209 | + | |
| 210 | + public static function getSearchFields() | |
| 211 | + { | |
| 212 | + $results = SearchHelper::getSearchFields(); | |
| 213 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_SUCCESS , null, 'fields', $results); | |
| 214 | + } | |
| 215 | + | |
| 216 | +} | |
| 217 | + | |
| 218 | +?> | |
| 0 | 219 | \ No newline at end of file | ... | ... |
search2/ajax/metadata.php
0 → 100755
| 1 | +<?php | |
| 2 | +require_once('ajax.inc.php'); | |
| 3 | + | |
| 4 | +$what = AjaxSearchHelper::checkGET('what', AjaxSearchHelper::STATUS_MISSING_WHAT , _kt('What is required? fieldsets or fields?')); | |
| 5 | + | |
| 6 | +switch ($what) | |
| 7 | +{ | |
| 8 | + case 'documenttypes': | |
| 9 | + AjaxSearchHelper::getDocumentTypes(); | |
| 10 | + case 'documenttypefieldsets': | |
| 11 | + $documentTypeID = AjaxSearchHelper::checkGET('documenttypeid', AjaxSearchHelper::STATUS_MISSING_DOCUMENT_TYPE, _kt('Document type id is not specified.')); | |
| 12 | + AjaxSearchHelper::getDocumentTypeFieldsets($documentTypeID); | |
| 13 | + case 'fieldsets': | |
| 14 | + AjaxSearchHelper::getFieldsets(); | |
| 15 | + case 'fields': | |
| 16 | + $fieldsetID = AjaxSearchHelper::checkGET('fieldsetid', AjaxSearchHelper::STATUS_MISSING_FIELDSET, _kt('Field set id is not specified.')); | |
| 17 | + AjaxSearchHelper::getFields($fieldsetID); | |
| 18 | + default: | |
| 19 | + AjaxSearchHelper::createResponse(AjaxSearchHelper::STATUS_INTERNAL , _kt('Nothing else is available')); | |
| 20 | +} | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | +?> | |
| 0 | 25 | \ No newline at end of file | ... | ... |
search2/ajax/parseExpr.php
0 → 100755
search2/ajax/saveExpr.php
0 → 100755
| 1 | +<?php | |
| 2 | +require_once('ajax.inc.php'); | |
| 3 | + | |
| 4 | +$userID = AjaxSearchHelper::getSessionUser(); | |
| 5 | +$txtQuery = AjaxSearchHelper::checkGET('txtQuery',AjaxSearchHelper::STATUS_MISSING_QUERY ,_kt('Query is empty')); | |
| 6 | + | |
| 7 | +AjaxSearchHelper::parseQuery($txtQuery, false); | |
| 8 | + | |
| 9 | +if (array_key_exists('iSavedId',$_GET)) | |
| 10 | +{ | |
| 11 | + $iSavedId = AjaxSearchHelper::checkGET('iSavedId', AjaxSearchHelper::STATUS_MISSING_SAVED, _kt('Saved search ID is missing')); | |
| 12 | + | |
| 13 | + if (!is_numeric($iSavedId)) | |
| 14 | + { | |
| 15 | + AjaxHelper::ajaxResponse(AjaxSearchHelper::STATUS_MISSING_SAVED, _kt('Saved search ID is not numeric') ); | |
| 16 | + } | |
| 17 | + | |
| 18 | + AjaxSearchHelper::updateQuery($iSavedId, $txtQuery, $userID); | |
| 19 | + | |
| 20 | +} | |
| 21 | +else | |
| 22 | +{ | |
| 23 | + $txtName = AjaxSearchHelper::checkGET('txtName',AjaxSearchHelper::STATUS_MISSING_NAME ,_kt('Query name is empty')); | |
| 24 | + | |
| 25 | + AjaxSearchHelper::saveQuery($txtName, $txtQuery, $userID); | |
| 26 | + | |
| 27 | +} | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | +?> | |
| 0 | 41 | \ No newline at end of file | ... | ... |
search2/ajax/savedSearches.php
0 → 100755
search2/ajax/searchFields.php
0 → 100755
search2/ajax/treeNodes.php
0 → 100755
search2/images/kn.png
0 → 100755
863 Bytes
search2/images/o-red.png
0 → 100755
595 Bytes
search2/images/o-yellow.png
0 → 100755
489 Bytes
search2/images/wledgetree.png
0 → 100755
2.96 KB
search2/indexing/bin/cronIndexer.php
0 → 100755
search2/indexing/bin/diagnose.php
0 → 100755
search2/indexing/bin/optimise.php
0 → 100755
search2/indexing/bin/recreateIndex.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +if (true) | |
| 4 | +{ | |
| 5 | + die('are you sure?'); | |
| 6 | +} | |
| 7 | + | |
| 8 | +require_once(realpath('../../../config/dmsDefaults.php')); | |
| 9 | +require_once('indexing/indexerCore.inc.php'); | |
| 10 | +require_once('indexing/indexers/PHPLuceneIndexer.inc.php'); | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | +// this is a function specific to PHP | |
| 15 | +PHPLuceneIndexer::createIndex(); | |
| 16 | +PHPLuceneIndexer::indexAll(); | |
| 17 | + | |
| 18 | +print "The lucene index has been deleted. All documents are now in the queue.\n"; | |
| 19 | + | |
| 20 | +?> | |
| 0 | 21 | \ No newline at end of file | ... | ... |
search2/indexing/bin/registerTypes.php
0 → 100755
search2/indexing/extractorCore.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * DocumentExtractor is the base class for all text extractors. | |
| 5 | + * | |
| 6 | + */ | |
| 7 | +abstract class DocumentExtractor | |
| 8 | +{ | |
| 9 | + /** | |
| 10 | + * The source filename from which to extract text. | |
| 11 | + * | |
| 12 | + * @var string | |
| 13 | + */ | |
| 14 | + protected $sourcefile; | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * The target filename, where the extracted text must be stored. | |
| 18 | + * | |
| 19 | + * @var string | |
| 20 | + */ | |
| 21 | + protected $targetfile; | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * The mime type of the source file. | |
| 25 | + * | |
| 26 | + * @var string | |
| 27 | + */ | |
| 28 | + protected $mimetype; | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * The extension of the source file. | |
| 32 | + * | |
| 33 | + * @var string | |
| 34 | + */ | |
| 35 | + protected $extension; | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * Reference to the document being indexed. | |
| 39 | + * | |
| 40 | + * @var Document | |
| 41 | + */ | |
| 42 | + protected $document; | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * Indicates if the extractor needs an intermediate file or not. | |
| 46 | + * Generally the source file will be a file within the respository itself. Some extractors may | |
| 47 | + * require the source file to have the correct extension. Setting this to true will result in | |
| 48 | + * a file being created with the extension of the file. It is ideal to disable this if possible. | |
| 49 | + * | |
| 50 | + * @var boolean | |
| 51 | + */ | |
| 52 | + protected $needsIntermediate; | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * The status of the extraction. If null, the extraction has not been done yet. | |
| 56 | + * | |
| 57 | + * @var boolean | |
| 58 | + */ | |
| 59 | + protected $extractionStatus; | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * The status of the indexing. If null, the indexing has not been done yet. | |
| 63 | + * | |
| 64 | + * @var boolean | |
| 65 | + */ | |
| 66 | + protected $indexStatus; | |
| 67 | + | |
| 68 | + | |
| 69 | + public function __construct() | |
| 70 | + { | |
| 71 | + $this->needsIntermediate=false; | |
| 72 | + $this->extractionStatus = null; | |
| 73 | + $this->indexStatus = null; | |
| 74 | + } | |
| 75 | + | |
| 76 | + /** | |
| 77 | + * Sets the status of the indexing. | |
| 78 | + * | |
| 79 | + * @param unknown_type $status | |
| 80 | + */ | |
| 81 | + public function setIndexingStatus($status) | |
| 82 | + { | |
| 83 | + $this->indexStatus = $status; | |
| 84 | + } | |
| 85 | + /** | |
| 86 | + * Returns the indexing status. | |
| 87 | + * | |
| 88 | + * @return boolean | |
| 89 | + */ | |
| 90 | + public function getIndexingStatus() | |
| 91 | + { | |
| 92 | + return $this->indexStatus; | |
| 93 | + } | |
| 94 | + | |
| 95 | + /** | |
| 96 | + * Sets the extraction status. | |
| 97 | + * | |
| 98 | + * @param boolean $status | |
| 99 | + */ | |
| 100 | + public function setExtractionStatus($status) | |
| 101 | + { | |
| 102 | + $this->extractionStatus = $status; | |
| 103 | + } | |
| 104 | + /** | |
| 105 | + * Return the extraction status. | |
| 106 | + * | |
| 107 | + * @return boolean | |
| 108 | + */ | |
| 109 | + public function getExtractionStatus() | |
| 110 | + { | |
| 111 | + return $this->extractionStatus; | |
| 112 | + } | |
| 113 | + | |
| 114 | + /** | |
| 115 | + * This associates all the mime types associated with the extractor class. | |
| 116 | + * | |
| 117 | + */ | |
| 118 | + public function registerMimeTypes() | |
| 119 | + { | |
| 120 | + $types = $this->getSupportedMimeTypes(); | |
| 121 | + if (empty($types)) | |
| 122 | + { | |
| 123 | + return; | |
| 124 | + } | |
| 125 | + $classname=get_class($this); | |
| 126 | + | |
| 127 | + foreach($types as $type) | |
| 128 | + { | |
| 129 | + $sql = "update mime_types set extractor='$classname' where mimetypes='$type' and extractor is null"; | |
| 130 | + DBUtil::runQuery($sql); | |
| 131 | + } | |
| 132 | + } | |
| 133 | + | |
| 134 | + /** | |
| 135 | + * Indicates if an intermediate file is required. | |
| 136 | + * | |
| 137 | + * @param $value boolean Optional. If set, we set the value. | |
| 138 | + * @return boolean | |
| 139 | + */ | |
| 140 | + public function needsIntermediateSourceFile($value = null) | |
| 141 | + { | |
| 142 | + if (!is_null($value)) | |
| 143 | + { | |
| 144 | + $this->needsIntermediate = $value; | |
| 145 | + } | |
| 146 | + return $this->needsIntermediate; | |
| 147 | + } | |
| 148 | + | |
| 149 | + /** | |
| 150 | + * Sets the source filename for the document extractor. | |
| 151 | + * | |
| 152 | + * @param string $sourcefile | |
| 153 | + */ | |
| 154 | + public function setSourceFile($sourcefile) | |
| 155 | + { | |
| 156 | + $this->sourcefile=$sourcefile; | |
| 157 | + } | |
| 158 | + | |
| 159 | + /** | |
| 160 | + * Returns the source file name. | |
| 161 | + * | |
| 162 | + * @return string | |
| 163 | + */ | |
| 164 | + public function getSourceFile() { return $this->sourcefile; } | |
| 165 | + | |
| 166 | + /** | |
| 167 | + * Sets the source file's mime type. | |
| 168 | + * | |
| 169 | + * @param string $mimetype | |
| 170 | + */ | |
| 171 | + public function setMimeType($mimetype) | |
| 172 | + { | |
| 173 | + $this->mimetype=$mimetype; | |
| 174 | + } | |
| 175 | + /** | |
| 176 | + * Returns the mime type for the source file. | |
| 177 | + * | |
| 178 | + * @return string | |
| 179 | + */ | |
| 180 | + public function getMimeType() { return $this->mimetype; } | |
| 181 | + | |
| 182 | + /** | |
| 183 | + * Indicates the extension for the source file. | |
| 184 | + * | |
| 185 | + * @param string $extension | |
| 186 | + */ | |
| 187 | + public function setExtension($extension) | |
| 188 | + { | |
| 189 | + $this->extension=$extension; | |
| 190 | + } | |
| 191 | + /** | |
| 192 | + * Returns the extension of the source file. | |
| 193 | + * | |
| 194 | + * @return string | |
| 195 | + */ | |
| 196 | + public function getExtension() { return $this->extension; } | |
| 197 | + | |
| 198 | + /** | |
| 199 | + * Sets the file name of the target text file. | |
| 200 | + * | |
| 201 | + * @param string $targetfile | |
| 202 | + */ | |
| 203 | + public function setTargetFile($targetfile) | |
| 204 | + { | |
| 205 | + $this->targetfile=$targetfile; | |
| 206 | + } | |
| 207 | + | |
| 208 | + /** | |
| 209 | + * Gets the file name of the target text file containing the extracted text. | |
| 210 | + * | |
| 211 | + * @return unknown | |
| 212 | + */ | |
| 213 | + public function getTargetFile() { return $this->targetfile; } | |
| 214 | + | |
| 215 | + /** | |
| 216 | + * Filter function that may be applied after extraction. This may be overridden. | |
| 217 | + * | |
| 218 | + * @param string $text | |
| 219 | + * @return string | |
| 220 | + */ | |
| 221 | + protected function filter($text) | |
| 222 | + { | |
| 223 | + return $text; | |
| 224 | + } | |
| 225 | + | |
| 226 | + /** | |
| 227 | + * Set the document that will be indexed. | |
| 228 | + * | |
| 229 | + * @param Document $document | |
| 230 | + */ | |
| 231 | + public function setDocument($document) | |
| 232 | + { | |
| 233 | + $this->document = $document; | |
| 234 | + } | |
| 235 | + | |
| 236 | + /** | |
| 237 | + * Returns a reference to the document. | |
| 238 | + * | |
| 239 | + * @return string | |
| 240 | + */ | |
| 241 | + public function getDocument() | |
| 242 | + { | |
| 243 | + return $this->document; | |
| 244 | + } | |
| 245 | + | |
| 246 | + /** | |
| 247 | + * Returns an array of supported mime types. | |
| 248 | + * e.g. return array('plain/text'); | |
| 249 | + * | |
| 250 | + * | |
| 251 | + * @return array | |
| 252 | + * | |
| 253 | + */ | |
| 254 | + public abstract function getSupportedMimeTypes(); | |
| 255 | + | |
| 256 | + /** | |
| 257 | + * Extracts the content from the source file. | |
| 258 | + * | |
| 259 | + * @return boolean | |
| 260 | + */ | |
| 261 | + public abstract function extractTextContent(); | |
| 262 | + | |
| 263 | + /** | |
| 264 | + * Returns a friendly name for the document text extractor. | |
| 265 | + * | |
| 266 | + * @return string | |
| 267 | + */ | |
| 268 | + public abstract function getDisplayName(); | |
| 269 | + | |
| 270 | + /** | |
| 271 | + * Attempts to diagnose any problems with the indexing process. | |
| 272 | + * | |
| 273 | + * @return string | |
| 274 | + */ | |
| 275 | + public abstract function diagnose(); | |
| 276 | + | |
| 277 | +} | |
| 278 | + | |
| 279 | +/** | |
| 280 | + * This class extends the document extractor to execute some command line application. | |
| 281 | + * The getCommandLine() method needs to be overridden. | |
| 282 | + * | |
| 283 | + */ | |
| 284 | +abstract class ExternalDocumentExtractor extends DocumentExtractor | |
| 285 | +{ | |
| 286 | + /** | |
| 287 | + * Initialise the extractor. | |
| 288 | + * | |
| 289 | + */ | |
| 290 | + public function __construct() | |
| 291 | + { | |
| 292 | + parent::__construct(); | |
| 293 | + putenv('LANG=en_US.UTF-8'); | |
| 294 | + } | |
| 295 | + | |
| 296 | + /** | |
| 297 | + * Executes a command. Returns true if successful. | |
| 298 | + * | |
| 299 | + * @param string $cmd A command line instruction. | |
| 300 | + * @return boolean | |
| 301 | + */ | |
| 302 | + protected function exec($cmd) | |
| 303 | + { | |
| 304 | + $aRet = KTUtil::pexec($cmd); | |
| 305 | + return $aRet['ret'] == 0; | |
| 306 | + } | |
| 307 | + | |
| 308 | + /** | |
| 309 | + * Returns the command line string to be executed. | |
| 310 | + * The command returned should include the target filename. | |
| 311 | + * | |
| 312 | + * @return string | |
| 313 | + */ | |
| 314 | + protected function getCommandLine() | |
| 315 | + { | |
| 316 | + throw new Exception('getCommandLine is not implemented'); | |
| 317 | + } | |
| 318 | + | |
| 319 | + /** | |
| 320 | + * Executes the command that executes the command. | |
| 321 | + * Returns true if success. | |
| 322 | + * | |
| 323 | + * @return boolean | |
| 324 | + */ | |
| 325 | + public function extractTextContent() | |
| 326 | + { | |
| 327 | + global $default; | |
| 328 | + | |
| 329 | + $cmdline = $this->getCommandLine(); | |
| 330 | + | |
| 331 | + $class = get_class($this); | |
| 332 | + $default->log->debug("$class: " . $cmdline); | |
| 333 | + | |
| 334 | + return $this->exec($cmdline); | |
| 335 | + } | |
| 336 | + | |
| 337 | +} | |
| 338 | + | |
| 339 | +/** | |
| 340 | + * An extension to the extenal document extractor. A derived class simply needs | |
| 341 | + * to implement a constructor and getSupportedMimeTypes(). | |
| 342 | + * | |
| 343 | + */ | |
| 344 | +abstract class ApplicationExtractor extends ExternalDocumentExtractor | |
| 345 | +{ | |
| 346 | + /** | |
| 347 | + * The full path to the application that will be run. This will be resolved from | |
| 348 | + * the path or using the config file. | |
| 349 | + * | |
| 350 | + * @var string | |
| 351 | + */ | |
| 352 | + private $application; | |
| 353 | + /** | |
| 354 | + * The command name of the application that can be run. | |
| 355 | + * | |
| 356 | + * @var string | |
| 357 | + */ | |
| 358 | + private $command; | |
| 359 | + /** | |
| 360 | + * This is the friendly name for the extractor. | |
| 361 | + * | |
| 362 | + * @var string | |
| 363 | + */ | |
| 364 | + private $displayname; | |
| 365 | + /** | |
| 366 | + * The command line parameters for the application. | |
| 367 | + * This may include {source} and {target} where substitutions will be done. | |
| 368 | + * | |
| 369 | + * @var string | |
| 370 | + */ | |
| 371 | + private $params; | |
| 372 | + | |
| 373 | + /** | |
| 374 | + * Initialise the extractor. | |
| 375 | + * | |
| 376 | + * @param string $section The section in the config file. | |
| 377 | + * @param string $appname The application name in the config file. | |
| 378 | + * @param string $command The command that can be run. | |
| 379 | + * @param string $displayname | |
| 380 | + * @param string $params | |
| 381 | + */ | |
| 382 | + public function __construct($section, $appname, $command, $displayname, $params) | |
| 383 | + { | |
| 384 | + parent::__construct(); | |
| 385 | + | |
| 386 | + $this->application = KTUtil::findCommand("$section/$appname", $command); | |
| 387 | + $this->command = $command; | |
| 388 | + $this->displayname = $displayname; | |
| 389 | + $this->params = $params; | |
| 390 | + } | |
| 391 | + | |
| 392 | + /** | |
| 393 | + * Return the display name. | |
| 394 | + * | |
| 395 | + * @return string | |
| 396 | + */ | |
| 397 | + public function getDisplayName() | |
| 398 | + { | |
| 399 | + return _kt($this->displayname); | |
| 400 | + } | |
| 401 | + | |
| 402 | + /** | |
| 403 | + * Returns the command line after performing substitutions. | |
| 404 | + * | |
| 405 | + * @return unknown | |
| 406 | + */ | |
| 407 | + protected function getCommandLine() | |
| 408 | + { | |
| 409 | + $sources = array('{source}','{target}'); | |
| 410 | + $target = array($this->sourcefile, $this->targetfile); | |
| 411 | + $cmdline = $this->command . ' ' . str_replace($sources,$target, $params); | |
| 412 | + | |
| 413 | + return $cmdline; | |
| 414 | + } | |
| 415 | + | |
| 416 | + /** | |
| 417 | + * Identifies if there are any circumstances why the command can not run that could result in the text extraction process | |
| 418 | + * failing. | |
| 419 | + * | |
| 420 | + * @return mixed Returns string if there is a problem, null otherwise. | |
| 421 | + */ | |
| 422 | + public function diagnose() | |
| 423 | + { | |
| 424 | + if (false === $this->application) | |
| 425 | + { | |
| 426 | + return _kt("Cannot locate binary for $this->displayname ($this->command)."); | |
| 427 | + } | |
| 428 | + | |
| 429 | + return null; | |
| 430 | + } | |
| 431 | +} | |
| 432 | + | |
| 433 | +abstract class TextExtractor extends DocumentExtractor | |
| 434 | +{ | |
| 435 | + /** | |
| 436 | + * This extracts the text from the document. | |
| 437 | + * | |
| 438 | + * @return boolean | |
| 439 | + */ | |
| 440 | + public function extractTextContent() | |
| 441 | + { | |
| 442 | + $content = file_get_contents($this->sourcefile); | |
| 443 | + if (false === $content) | |
| 444 | + { | |
| 445 | + return false; | |
| 446 | + } | |
| 447 | + | |
| 448 | + $result = file_put_contents($this->targetfile, $this->filter($content)); | |
| 449 | + | |
| 450 | + return false !== $result; | |
| 451 | + } | |
| 452 | + | |
| 453 | + /** | |
| 454 | + * There are no external dependancies to diagnose. | |
| 455 | + * | |
| 456 | + * @return null | |
| 457 | + */ | |
| 458 | + public function diagnose() | |
| 459 | + { | |
| 460 | + return null; | |
| 461 | + } | |
| 462 | + | |
| 463 | +} | |
| 464 | + | |
| 465 | +/** | |
| 466 | + * The composite extractor implies that a conversion is done to an intermediate form before another extractor is run. | |
| 467 | + * | |
| 468 | + */ | |
| 469 | +abstract class CompositeExtractor extends DocumentExtractor | |
| 470 | +{ | |
| 471 | + /** | |
| 472 | + * The initial extractor | |
| 473 | + * | |
| 474 | + * @var DocumentExtractor | |
| 475 | + */ | |
| 476 | + private $sourceExtractor; | |
| 477 | + /** | |
| 478 | + * The text extractor | |
| 479 | + * | |
| 480 | + * @var DocumentExtractor | |
| 481 | + */ | |
| 482 | + private $targetExtractor; | |
| 483 | + /** | |
| 484 | + * The extension for the initial extraction | |
| 485 | + * | |
| 486 | + * @var string | |
| 487 | + */ | |
| 488 | + private $targetExtension; | |
| 489 | + /** | |
| 490 | + * The mime type of the initial extraction. | |
| 491 | + * | |
| 492 | + * @var string | |
| 493 | + */ | |
| 494 | + private $targetMimeType; | |
| 495 | + | |
| 496 | + public function __construct($sourceExtractor, $targetExtension, $targetMimeType, $targetExtractor, $needsIntermediate) | |
| 497 | + { | |
| 498 | + $this->sourceExtractor = $sourceExtractor; | |
| 499 | + $this->targetExtractor = $targetExtractor; | |
| 500 | + $this->targetExtension = $targetExtension; | |
| 501 | + $this->targetMimeType = $targetMimeType; | |
| 502 | + $this->needsIntermediateSourceFile($needsIntermediate); | |
| 503 | + } | |
| 504 | + | |
| 505 | + /** | |
| 506 | + * Extracts the content of the document | |
| 507 | + * | |
| 508 | + * @return string | |
| 509 | + */ | |
| 510 | + public function extractTextContent() | |
| 511 | + { | |
| 512 | + $intermediateFile = $this->targetfile . '.' . $this->targetExtension; | |
| 513 | + | |
| 514 | + $this->sourceExtractor->setSourceFile($this->sourcefile); | |
| 515 | + $this->sourceExtractor->setTargetFile($intermediateFile); | |
| 516 | + $this->sourceExtractor->setMimeType($this->mimetype); | |
| 517 | + $this->sourceExtractor->setExtension($this->extension); | |
| 518 | + if ($this->sourceExtractor->extractTextContent()) | |
| 519 | + { | |
| 520 | + return false; | |
| 521 | + } | |
| 522 | + | |
| 523 | + $this->targetExtractor->setSourceFile($intermediateFile); | |
| 524 | + $this->targetExtractor->setTargetFile($this->targetfile); | |
| 525 | + $this->targetExtractor->setMimeType($this->targetMimeType); | |
| 526 | + $this->targetExtractor->setExtension($this->targetExtension); | |
| 527 | + $result = $this->targetExtractor->extractTextContent(); | |
| 528 | + | |
| 529 | + unlink(@$intermediateFile); | |
| 530 | + | |
| 531 | + return $result; | |
| 532 | + } | |
| 533 | + | |
| 534 | + /** | |
| 535 | + * Diagnose the extractors | |
| 536 | + * | |
| 537 | + * @return mixed | |
| 538 | + */ | |
| 539 | + public function diagnose() | |
| 540 | + { | |
| 541 | + $diagnosis = $this->sourceExtractor->diagnose(); | |
| 542 | + if (!empty($diagnosis)) | |
| 543 | + { | |
| 544 | + return $diagnosis; | |
| 545 | + } | |
| 546 | + | |
| 547 | + $diagnosis = $this->targetExtractor->diagnose(); | |
| 548 | + if (!empty($diagnosis)) | |
| 549 | + { | |
| 550 | + return $diagnosis; | |
| 551 | + } | |
| 552 | + | |
| 553 | + return null; | |
| 554 | + } | |
| 555 | +} | |
| 556 | + | |
| 557 | + | |
| 558 | +/** | |
| 559 | + * The purpose of an extractor hook is to effect the | |
| 560 | + * | |
| 561 | + */ | |
| 562 | +abstract class ExtractorHook | |
| 563 | +{ | |
| 564 | + /** | |
| 565 | + * Returns an array of supported mime types. | |
| 566 | + * e.g. return array('plain/text'); | |
| 567 | + * | |
| 568 | + * | |
| 569 | + * @return array | |
| 570 | + * | |
| 571 | + */ | |
| 572 | + public abstract function getSupportedMimeTypes(); | |
| 573 | + | |
| 574 | + /** | |
| 575 | + * Returns the friendly name for the hook. | |
| 576 | + * | |
| 577 | + * @return string | |
| 578 | + */ | |
| 579 | + public abstract function getDisplayName(); | |
| 580 | + | |
| 581 | + /** | |
| 582 | + * This does a basic diagnosis on the hook. | |
| 583 | + * | |
| 584 | + * @return string | |
| 585 | + */ | |
| 586 | + public function diagnose() | |
| 587 | + { | |
| 588 | + return null; | |
| 589 | + } | |
| 590 | + | |
| 591 | + /** | |
| 592 | + * Perform any pre extraction activities. | |
| 593 | + * | |
| 594 | + * @param DocumentExtractor $extractor | |
| 595 | + */ | |
| 596 | + public function pre_extract($extractor) | |
| 597 | + { | |
| 598 | + } | |
| 599 | + | |
| 600 | + /** | |
| 601 | + * Perform any post extraction activities. | |
| 602 | + * | |
| 603 | + * @param DocumentExtractor $extractor | |
| 604 | + */ | |
| 605 | + public function post_extract($extractor) | |
| 606 | + { | |
| 607 | + | |
| 608 | + } | |
| 609 | + | |
| 610 | + /** | |
| 611 | + * Perform any pre indexing activities. | |
| 612 | + * | |
| 613 | + * @param DocumentExtractor $extractor | |
| 614 | + */ | |
| 615 | + public function pre_index($extractor) | |
| 616 | + { | |
| 617 | + | |
| 618 | + } | |
| 619 | + | |
| 620 | + /** | |
| 621 | + * Perform any post indexing activities. | |
| 622 | + * | |
| 623 | + * @param DocumentExtractor $extractor | |
| 624 | + */ | |
| 625 | + public function post_index($extractor) | |
| 626 | + { | |
| 627 | + | |
| 628 | + } | |
| 629 | +} | |
| 630 | + | |
| 631 | +?> | |
| 0 | 632 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/MailMimeExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class MailMimeExtractor extends TextExtractor | |
| 4 | +{ | |
| 5 | + public function getDisplayName() | |
| 6 | + { | |
| 7 | + return _kt('Mail Mime Extractor'); | |
| 8 | + } | |
| 9 | + | |
| 10 | + public function getSupportedMimeTypes() | |
| 11 | + { | |
| 12 | + return array('text/msg'); | |
| 13 | + } | |
| 14 | + | |
| 15 | +} | |
| 16 | + | |
| 17 | +?> | |
| 0 | 18 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/OOPDFTextExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +require_once('PDFExtractor.inc.php'); | |
| 4 | +require_once('OOTextExtractor.inc.php'); | |
| 5 | + | |
| 6 | +class OOPDFTextExtractor extends CompositeExtractor | |
| 7 | +{ | |
| 8 | + public function __construct() | |
| 9 | + { | |
| 10 | + parent::__construct(new OOTextExtractor('application/pdf'),'pdf','application/pdf',new PDFExtractor(), true); | |
| 11 | + } | |
| 12 | + | |
| 13 | + public function getSupportedMimeTypes() | |
| 14 | + { | |
| 15 | + // we provide this so diagnose doesn't fail | |
| 16 | + return array(); | |
| 17 | + } | |
| 18 | + | |
| 19 | + public function getDisplayName() | |
| 20 | + { | |
| 21 | + // we provide this so diagnose doesn't fail | |
| 22 | + throw new Exception('This should be overriden'); | |
| 23 | + } | |
| 24 | + | |
| 25 | +} | |
| 26 | + | |
| 27 | +/* | |
| 28 | +class OOPDFTextExtractor extends DocumentExtractor | |
| 29 | +{ | |
| 30 | + | |
| 31 | + private $pdf2txt; | |
| 32 | + | |
| 33 | + | |
| 34 | + private $text2pdf; | |
| 35 | + | |
| 36 | + public function __construct() | |
| 37 | + { | |
| 38 | + $this->pdf2txt = new PDFExtractor(); | |
| 39 | + $this->text2pdf = new OOTextExtractor(); | |
| 40 | + } | |
| 41 | + | |
| 42 | + public function needsIntermediateSourceFile() | |
| 43 | + { | |
| 44 | + // we need the intermediate file because it | |
| 45 | + // has the correct extension. jodconverter uses the extension to determine mimetype | |
| 46 | + return true; | |
| 47 | + } | |
| 48 | + | |
| 49 | + public function getDisplayName() | |
| 50 | + { | |
| 51 | + throw new Exception('This should be overriden'); | |
| 52 | + } | |
| 53 | + | |
| 54 | + public function getSupportedMimeTypes() | |
| 55 | + { | |
| 56 | + return array(); | |
| 57 | + } | |
| 58 | + | |
| 59 | + public function extractTextContent() | |
| 60 | + { | |
| 61 | + $pdffile = $this->targetfile . '.pdf'; | |
| 62 | + | |
| 63 | + $this->text2pdf->setSourceFile($this->sourcefile); | |
| 64 | + $this->text2pdf->setTargetFile($pdffile); | |
| 65 | + $this->text2pdf->setMimeType($this->mimetype); | |
| 66 | + $this->text2pdf->setExtension($this->extension); | |
| 67 | + if ($this->extractTextContent()) | |
| 68 | + { | |
| 69 | + return false; | |
| 70 | + } | |
| 71 | + | |
| 72 | + $this->pdf2txt->setSourceFile($pdffile); | |
| 73 | + $this->pdf2txt->setTargetFile($this->targetfile); | |
| 74 | + $this->pdf2txt->setMimeType('application/pdf'); | |
| 75 | + $this->pdf2txt->setExtension('pdf'); | |
| 76 | + $result = $this->pdf2txt->extractTextContent(); | |
| 77 | + | |
| 78 | + unlink(@$pdffile); | |
| 79 | + | |
| 80 | + return $result; | |
| 81 | + } | |
| 82 | + | |
| 83 | + public function diagnose() | |
| 84 | + { | |
| 85 | + $diagnosis = $this->pdf2txt->diagnose(); | |
| 86 | + if (!empty($diagnosis)) | |
| 87 | + { | |
| 88 | + return $diagnosis; | |
| 89 | + } | |
| 90 | + | |
| 91 | + $diagnosis = $this->text2pdf->diagnose(); | |
| 92 | + if (!empty($diagnosis)) | |
| 93 | + { | |
| 94 | + return $diagnosis; | |
| 95 | + } | |
| 96 | + | |
| 97 | + return null; | |
| 98 | + } | |
| 99 | +} */ | |
| 100 | + | |
| 101 | +?> | |
| 0 | 102 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/OOPresentationExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +require_once('OOPDFTextExtractor.inc.php'); | |
| 4 | + | |
| 5 | +class OOPresentationExtractor extends OOPDFTextExtractor | |
| 6 | +{ | |
| 7 | + public function getDisplayName() | |
| 8 | + { | |
| 9 | + return _kt('OpenOffice Presentation Extractor'); | |
| 10 | + } | |
| 11 | + | |
| 12 | + public function getSupportedMimeTypes() | |
| 13 | + { | |
| 14 | + return array( | |
| 15 | + 'application/vnd.oasis.opendocument.presentation', | |
| 16 | + 'application/vnd.oasis.opendocument.presentation-template', | |
| 17 | + ); | |
| 18 | + } | |
| 19 | +} | |
| 20 | + | |
| 21 | +?> | |
| 0 | 22 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/OOSpreadsheetExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +require_once('OOPDFTextExtractor.inc.php'); | |
| 4 | + | |
| 5 | +class OOSpreadsheetExtractor extends OOPDFTextExtractor | |
| 6 | +{ | |
| 7 | + public function getDisplayName() | |
| 8 | + { | |
| 9 | + return _kt('OpenOffice Spreadsheet Extractor'); | |
| 10 | + } | |
| 11 | + | |
| 12 | + public function getSupportedMimeTypes() | |
| 13 | + { | |
| 14 | + return array( | |
| 15 | + 'application/vnd.ms-excel', | |
| 16 | + 'application/vnd.oasis.opendocument.spreadsheet', | |
| 17 | + 'application/vnd.oasis.opendocument.spreadsheet-template', | |
| 18 | + 'application/vnd.sun.xml.calc', | |
| 19 | + 'application/vnd.sun.xml.calc.template' | |
| 20 | + ); | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | + | |
| 25 | +?> | |
| 0 | 26 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/OOTextExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class OOTextExtractor extends ExternalDocumentExtractor | |
| 4 | +{ | |
| 5 | + private $converter; | |
| 6 | + private $javaPath; | |
| 7 | + private $ooHost; | |
| 8 | + private $ooPort; | |
| 9 | + private $targetMimeType; | |
| 10 | + | |
| 11 | + public function __construct($targetMimeType='plain/text') | |
| 12 | + { | |
| 13 | + parent::__construct(); | |
| 14 | + $config =& KTConfig::getSingleton(); | |
| 15 | + | |
| 16 | + $this->converter = KTUtil::findCommand('extractors/jodconverter', 'jodconverter'); | |
| 17 | + $this->javaPath = KTUtil::findCommand('extractors/java', 'java'); | |
| 18 | + $this->ooHost = $config->get('openoffice/host', 'localhost'); | |
| 19 | + $this->ooPort = $config->get('openoffice/port', 8100); | |
| 20 | + $this->targetMimeType = $targetMimeType; | |
| 21 | + } | |
| 22 | + | |
| 23 | + public function getDisplayName() | |
| 24 | + { | |
| 25 | + return _kt('OpenOffice Text Extractor'); | |
| 26 | + } | |
| 27 | + | |
| 28 | + public function getSupportedMimeTypes() | |
| 29 | + { | |
| 30 | + return array( | |
| 31 | + 'text/rtf', | |
| 32 | + 'application/vnd.oasis.opendocument.text', | |
| 33 | + 'application/vnd.oasis.opendocument.text-template', | |
| 34 | + 'application/vnd.oasis.opendocument.text-web', | |
| 35 | + 'application/vnd.oasis.opendocument.text-master', | |
| 36 | + 'application/vnd.sun.xml.writer', | |
| 37 | + 'application/vnd.sun.xml.writer.template', | |
| 38 | + 'application/vnd.sun.xml.writer.global', | |
| 39 | + ); | |
| 40 | + } | |
| 41 | + | |
| 42 | + public function needsIntermediateSourceFile() | |
| 43 | + { | |
| 44 | + // we need the intermediate file because it | |
| 45 | + // has the correct extension. jodconverter uses the extension to determine mimetype | |
| 46 | + return true; | |
| 47 | + } | |
| 48 | + | |
| 49 | + protected function getCommandLine() | |
| 50 | + { | |
| 51 | + $cmdline = "$this->javaPath -jar $this->converter $this->sourcefile $this->mimetype $this->targetfile $this->targetMimeType $this->ooHost $this->ooPort"; | |
| 52 | + return $cmdline; | |
| 53 | + } | |
| 54 | + | |
| 55 | + public function diagnose() | |
| 56 | + { | |
| 57 | + if (false === $this->converter) | |
| 58 | + { | |
| 59 | + return _kt('Cannot locate jodconverter'); | |
| 60 | + } | |
| 61 | + | |
| 62 | + if (false === $this->javaPath) | |
| 63 | + { | |
| 64 | + return _kt('Cannot locate java'); | |
| 65 | + } | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + $connection = @fsockopen($this->ooHost, $this->ooPort,$errno, $errstr,5 ); | |
| 70 | + if (false === $connection) | |
| 71 | + { | |
| 72 | + return _kt('Cannot connect to openoffice host'); | |
| 73 | + } | |
| 74 | + fclose($connection); | |
| 75 | + | |
| 76 | + | |
| 77 | + return null; | |
| 78 | + } | |
| 79 | +} | |
| 80 | + | |
| 81 | +?> | |
| 0 | 82 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/PDFExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class PDFExtractor extends ApplicationExtractor | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('extractors','pdftotext','pdftotext','PDF Text Extractor','-nopgbrk -enc UTF-8 {source} {target}'); | |
| 8 | + } | |
| 9 | + | |
| 10 | + public function getSupportedMimeTypes() | |
| 11 | + { | |
| 12 | + return array('application/pdf'); | |
| 13 | + } | |
| 14 | +} | |
| 15 | + | |
| 16 | +?> | |
| 0 | 17 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/PSExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class PSExtractor extends ApplicationExtractor | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('extractors','pstotext','pstotext','PostScript Text Extractor','-nopgbrk -enc UTF-8 {source} {target}'); | |
| 8 | + } | |
| 9 | + | |
| 10 | + public function getSupportedMimeTypes() | |
| 11 | + { | |
| 12 | + return array('application/postscript'); | |
| 13 | + } | |
| 14 | +} | |
| 15 | + | |
| 16 | +?> | |
| 0 | 17 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/PlainTextExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class PlainTextExtractor extends TextExtractor | |
| 4 | +{ | |
| 5 | + public function getDisplayName() | |
| 6 | + { | |
| 7 | + return _kt('Plain Text Extractor'); | |
| 8 | + } | |
| 9 | + | |
| 10 | + public function getSupportedMimeTypes() | |
| 11 | + { | |
| 12 | + return array('text/plain','text/csv'); | |
| 13 | + } | |
| 14 | + | |
| 15 | +} | |
| 16 | + | |
| 17 | +?> | |
| 0 | 18 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/ScriptExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class ScriptExtractor extends TextExtractor | |
| 4 | +{ | |
| 5 | + public function getDisplayName() | |
| 6 | + { | |
| 7 | + return _kt('Script Extractor'); | |
| 8 | + } | |
| 9 | + | |
| 10 | + public function getSupportedMimeTypes() | |
| 11 | + { | |
| 12 | + return array('application/x-shellscript','application/javascript'); | |
| 13 | + } | |
| 14 | + | |
| 15 | +} | |
| 16 | + | |
| 17 | +?> | |
| 0 | 18 | \ No newline at end of file | ... | ... |
search2/indexing/extractors/XMLExtractor.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class XMLExtractor extends TextExtractor | |
| 4 | +{ | |
| 5 | + public function getDisplayName() | |
| 6 | + { | |
| 7 | + return _kt('XML Text Extractor'); | |
| 8 | + } | |
| 9 | + | |
| 10 | + public function getSupportedMimeTypes() | |
| 11 | + { | |
| 12 | + return array('text/xml','application/xml','text/html'); | |
| 13 | + } | |
| 14 | + | |
| 15 | + protected function filter($text) | |
| 16 | + { | |
| 17 | + return preg_replace ("@(</?[^>]*>)+@", " ", $text); | |
| 18 | + } | |
| 19 | +} | |
| 20 | + | |
| 21 | +?> | |
| 0 | 22 | \ No newline at end of file | ... | ... |
search2/indexing/indexerCore.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +require_once('indexing/extractorCore.inc.php'); | |
| 4 | + | |
| 5 | + | |
| 6 | +class MatchResult | |
| 7 | +{ | |
| 8 | + protected $document_id; | |
| 9 | + protected $title; | |
| 10 | + protected $rank; | |
| 11 | + protected $text; | |
| 12 | + protected $filesize; | |
| 13 | + protected $fullpath; | |
| 14 | + protected $live; | |
| 15 | + protected $version; | |
| 16 | + protected $filename; | |
| 17 | + protected $thumbnail; // TODO: if not null, gui can display a thumbnail | |
| 18 | + protected $viewer; // TODO: if not null, a viewer can be used to view the document | |
| 19 | + protected $document; | |
| 20 | + protected $checkoutuser; | |
| 21 | + protected $workflowstate; | |
| 22 | + protected $workflow; | |
| 23 | + | |
| 24 | + public function __construct($document_id, $rank, $title, $text) | |
| 25 | + { | |
| 26 | + $this->document_id=$document_id; | |
| 27 | + $this->rank= $rank; | |
| 28 | + $this->title=$title; | |
| 29 | + $this->text = $text; | |
| 30 | + $this->loadDocumentInfo(); | |
| 31 | + } | |
| 32 | + | |
| 33 | + protected function __isset($property) | |
| 34 | + { | |
| 35 | + switch($property) | |
| 36 | + { | |
| 37 | + case 'DocumentID': return isset($this->document_id); | |
| 38 | + case 'Rank': return isset($this->rank); | |
| 39 | + case 'Text': return isset($this->text); | |
| 40 | + case 'Title': return isset($this->title); | |
| 41 | + case null: break; | |
| 42 | + default: | |
| 43 | + throw new Exception("Unknown property '$property' to get on MatchResult"); | |
| 44 | + } | |
| 45 | + } | |
| 46 | + | |
| 47 | + private function loadDocumentInfo() | |
| 48 | + { | |
| 49 | + $sql = "SELECT | |
| 50 | + f.full_path, f.name, dcv.size as filesize, dcv.major_version, | |
| 51 | + dcv.minor_version, dcv.filename, cou.name as checkoutuser, w.human_name as workflow, ws.human_name as workflowstate | |
| 52 | + | |
| 53 | + FROM | |
| 54 | + documents d | |
| 55 | + INNER JOIN document_metadata_version dmv ON d.metadata_version_id = dmv.id | |
| 56 | + INNER JOIN document_content_version dcv ON dmv.content_version_id = dcv.id | |
| 57 | + LEFT JOIN folders f ON f.id=d.folder_id | |
| 58 | + LEFT JOIN users cou ON d.checked_out_user_id=cou.id | |
| 59 | + LEFT JOIN workflows w ON dmv.workflow_id=w.id | |
| 60 | + LEFT JOIN workflow_states ws ON dmv.workflow_state_id = ws.id | |
| 61 | + WHERE | |
| 62 | + d.id=$this->document_id"; | |
| 63 | + | |
| 64 | + $result = DBUtil::getOneResult($sql); | |
| 65 | + | |
| 66 | + if (PEAR::isError($result) || empty($result)) | |
| 67 | + { | |
| 68 | + $this->live = false; | |
| 69 | + return; | |
| 70 | + } | |
| 71 | + | |
| 72 | + $this->live = true; | |
| 73 | + if (is_null($result['name'])) | |
| 74 | + { | |
| 75 | + $this->fullpath = '(orphaned)'; | |
| 76 | + } | |
| 77 | + else | |
| 78 | + { | |
| 79 | + $this->fullpath = $result['full_path'] . '/' . $result['name']; | |
| 80 | + if (substr($this->fullpath,0,1) == '/') $this->fullpath = substr($this->fullpath,1); | |
| 81 | + } | |
| 82 | + | |
| 83 | + | |
| 84 | + $this->filesize = $result['filesize'] + 0; | |
| 85 | + | |
| 86 | + if ($this->filesize > 1024 * 1024 * 1024) | |
| 87 | + { | |
| 88 | + $this->filesize = floor($this->filesize / (1024 * 1024 * 1024)) . 'g'; | |
| 89 | + } | |
| 90 | + elseif ($this->filesize > 1024 * 1024) | |
| 91 | + { | |
| 92 | + $this->filesize = floor($this->filesize / (1024 * 1024)) . 'm'; | |
| 93 | + } | |
| 94 | + elseif ($this->filesize > 1024) | |
| 95 | + { | |
| 96 | + $this->filesize = floor($this->filesize / (1024)) . 'k'; | |
| 97 | + } | |
| 98 | + else | |
| 99 | + { | |
| 100 | + $this->filesize .= 'b'; | |
| 101 | + } | |
| 102 | + | |
| 103 | + $this->version = $result['major_version'] . '.' . $result['minor_version']; | |
| 104 | + $this->filename=$result['filename']; | |
| 105 | + $this->checkoutuser = $result['checkoutuser']; | |
| 106 | + $this->workflow = $result['workflow']; | |
| 107 | + $this->workflowstate = $result['workflowstate']; | |
| 108 | + | |
| 109 | + } | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + protected function __get($property) | |
| 114 | + { | |
| 115 | + switch($property) | |
| 116 | + { | |
| 117 | + case 'DocumentID': return $this->document_id; | |
| 118 | + case 'Rank': return $this->rank; | |
| 119 | + case 'Text': return $this->text; | |
| 120 | + case 'Title': return $this->title; | |
| 121 | + case 'FullPath': return $this->fullpath; | |
| 122 | + case 'IsLive': return $this->live; | |
| 123 | + case 'Filesize': return $this->filesize; | |
| 124 | + case 'Version': return $this->version; | |
| 125 | + case 'Filename': return $this->filename; | |
| 126 | + case 'Document': | |
| 127 | + if (is_null($this->document)) | |
| 128 | + $this->document = Document::get($this->document_id); | |
| 129 | + return $this->document; | |
| 130 | + case 'IsAvailable': | |
| 131 | + return $this->Document->isLive(); | |
| 132 | + | |
| 133 | + case 'CheckedOutUser': | |
| 134 | + return $this->checkoutuser; | |
| 135 | + case 'Workflow': | |
| 136 | + if (is_null($this->workflow)) | |
| 137 | + { | |
| 138 | + return ''; | |
| 139 | + } | |
| 140 | + return "$this->workflow - $this->workflowstate"; | |
| 141 | + case null: break; | |
| 142 | + default: | |
| 143 | + throw new Exception("Unknown property '$property' to get on MatchResult"); | |
| 144 | + } | |
| 145 | + } | |
| 146 | + | |
| 147 | + protected function __set($property, $value) | |
| 148 | + { | |
| 149 | + switch($property) | |
| 150 | + { | |
| 151 | + case 'Rank': $this->rank = number_format($value,2,'.',','); break; | |
| 152 | + case 'Text': $this->text = $value; break; | |
| 153 | + default: | |
| 154 | + throw new Exception("Unknown property '$property' to set on MatchResult"); | |
| 155 | + } | |
| 156 | + } | |
| 157 | +} | |
| 158 | + | |
| 159 | +function MatchResultCompare($a, $b) | |
| 160 | +{ | |
| 161 | + if ($a->Rank == $b->Rank) { | |
| 162 | + return 0; | |
| 163 | + } | |
| 164 | + return ($a->Rank < $b->Rank) ? -1 : 1; | |
| 165 | +} | |
| 166 | + | |
| 167 | +class QueryResultItem extends MatchResult | |
| 168 | +{ | |
| 169 | + protected $discussion; | |
| 170 | + | |
| 171 | + public function __construct($document_id, $rank, $title, $text, $discussion) | |
| 172 | + { | |
| 173 | + parent::__construct($document_id, $rank, $title, $text); | |
| 174 | + $this->discussion=$discussion; | |
| 175 | + } | |
| 176 | + | |
| 177 | + protected function __isset($property) | |
| 178 | + { | |
| 179 | + switch($property) | |
| 180 | + { | |
| 181 | + case 'Discussion': return isset($this->discussion); | |
| 182 | + default: return parent::__isset($property); | |
| 183 | + } | |
| 184 | + } | |
| 185 | + | |
| 186 | + protected function __get($property) | |
| 187 | + { | |
| 188 | + switch($property) | |
| 189 | + { | |
| 190 | + case 'Discussion': return $this->discussion; | |
| 191 | + default: return parent::__get($property); | |
| 192 | + } | |
| 193 | + } | |
| 194 | +} | |
| 195 | + | |
| 196 | +abstract class Indexer | |
| 197 | +{ | |
| 198 | + /** | |
| 199 | + * Cache of extractors | |
| 200 | + * | |
| 201 | + * @var array | |
| 202 | + */ | |
| 203 | + private $extractorCache; | |
| 204 | + | |
| 205 | + /** | |
| 206 | + * Indicates if the indexer will do logging. | |
| 207 | + * | |
| 208 | + * @var boolean | |
| 209 | + */ | |
| 210 | + private $debug; | |
| 211 | + /** | |
| 212 | + * Cache on mime related hooks | |
| 213 | + * | |
| 214 | + * @var unknown_type | |
| 215 | + */ | |
| 216 | + private $mimeHookCache; | |
| 217 | + /** | |
| 218 | + * Cache on general hooks. | |
| 219 | + * | |
| 220 | + * @var array | |
| 221 | + */ | |
| 222 | + private $generalHookCache; | |
| 223 | + | |
| 224 | + /** | |
| 225 | + * This is a path to the extractors. | |
| 226 | + * | |
| 227 | + * @var string | |
| 228 | + */ | |
| 229 | + private $extractorPath; | |
| 230 | + /** | |
| 231 | + * This is a path to the hooks. | |
| 232 | + * | |
| 233 | + * @var string | |
| 234 | + */ | |
| 235 | + private $hookPath; | |
| 236 | + | |
| 237 | + /** | |
| 238 | + * Initialise the indexer | |
| 239 | + * | |
| 240 | + */ | |
| 241 | + protected function __construct() | |
| 242 | + { | |
| 243 | + $this->extractorCache=array(); | |
| 244 | + $this->debug=true; | |
| 245 | + $this->hookCache = array(); | |
| 246 | + $this->generalHookCache = array(); | |
| 247 | + | |
| 248 | + $config = KTConfig::getSingleton(); | |
| 249 | + | |
| 250 | + $this->extractorPath = $config->get('indexer/extractorPath', 'extractors'); | |
| 251 | + $this->hookPath = $config->get('indexer/extractorHookPath','extractorHooks'); | |
| 252 | + } | |
| 253 | + | |
| 254 | + /** | |
| 255 | + * Returns a reference to the main class | |
| 256 | + * | |
| 257 | + * @return Indexer | |
| 258 | + */ | |
| 259 | + public static function get() | |
| 260 | + { | |
| 261 | + static $singleton = null; | |
| 262 | + | |
| 263 | + if (is_null($singleton)) | |
| 264 | + { | |
| 265 | + $config = KTConfig::getSingleton(); | |
| 266 | + $classname = $config->get('indexer/coreClass'); | |
| 267 | + | |
| 268 | + require_once('indexing/indexers/' . $classname . '.inc.php'); | |
| 269 | + | |
| 270 | + if (!class_exists($classname)) | |
| 271 | + { | |
| 272 | + throw new Exception("Class '$classname' does not exist."); | |
| 273 | + } | |
| 274 | + | |
| 275 | + $singleton = new $classname; | |
| 276 | + } | |
| 277 | + | |
| 278 | + return $singleton; | |
| 279 | + } | |
| 280 | + | |
| 281 | + public abstract function deleteDocument($docid); | |
| 282 | + | |
| 283 | + /** | |
| 284 | + * Remove the association of all extractors to mime types on the database. | |
| 285 | + * | |
| 286 | + */ | |
| 287 | + public function clearExtractors() | |
| 288 | + { | |
| 289 | + global $default; | |
| 290 | + $sql = "update mime_types set extractor=null"; | |
| 291 | + DBUtil::runQuery($sql); | |
| 292 | + | |
| 293 | + $default->log->debug('clearExtractors'); | |
| 294 | + } | |
| 295 | + | |
| 296 | + /** | |
| 297 | + * lookup the name of the extractor class based on the mime type. | |
| 298 | + * | |
| 299 | + * @param string $type | |
| 300 | + * @return string | |
| 301 | + */ | |
| 302 | + public static function resolveExtractor($type) | |
| 303 | + { | |
| 304 | + global $default; | |
| 305 | + $sql = "select extractor from mime_types where filetypes='$type'"; | |
| 306 | + $class = DBUtil::getOneResultKey($sql,'extractor'); | |
| 307 | + if (PEAR::isError($class)) | |
| 308 | + { | |
| 309 | + $default->log->error("resolveExtractor: cannot resolve $type"); | |
| 310 | + return $class; | |
| 311 | + } | |
| 312 | + if ($this->debug) $default->log->debug("resolveExtractor: Resolved '$class' from mime type '$type'."); | |
| 313 | + return $class; | |
| 314 | + } | |
| 315 | + | |
| 316 | + /** | |
| 317 | + * Return all the discussion text. | |
| 318 | + * | |
| 319 | + * @param int $docid | |
| 320 | + * @return string | |
| 321 | + */ | |
| 322 | + public static function getDiscussionText($docid) | |
| 323 | + { | |
| 324 | + $sql = "SELECT | |
| 325 | + dc.subject, dc.body | |
| 326 | + FROM | |
| 327 | + discussion_threads dt | |
| 328 | + INNER JOIN discussion_comments dc ON dc.thread_id=dt.id AND dc.id BETWEEN dt.first_comment_id AND dt.last_comment_id | |
| 329 | + WHERE | |
| 330 | + dt.document_id=$docid"; | |
| 331 | + $result = DBUtil::getResultArray($sql); | |
| 332 | + $text = ''; | |
| 333 | + | |
| 334 | + foreach($result as $record) | |
| 335 | + { | |
| 336 | + $text .= $record['subject'] . "\n" . $record['body'] . "\n"; | |
| 337 | + } | |
| 338 | + | |
| 339 | + return $text; | |
| 340 | + } | |
| 341 | + | |
| 342 | + /** | |
| 343 | + * Schedule the indexing of a document. | |
| 344 | + * | |
| 345 | + * @param string $document | |
| 346 | + * @param string $what | |
| 347 | + */ | |
| 348 | + public static function index($document, $what='C') | |
| 349 | + { | |
| 350 | + global $default; | |
| 351 | + | |
| 352 | + $document_id = $document->getId(); | |
| 353 | + $userid=$_SESSION['userID']; | |
| 354 | + if (empty($userid)) $userid=1; | |
| 355 | + | |
| 356 | + // we dequeue the document so that there are no issues when enqueuing | |
| 357 | + Indexer::unqueueDocument($document_id); | |
| 358 | + | |
| 359 | + // enqueue item | |
| 360 | + $sql = "INSERT INTO index_files(document_id, user_id, what) VALUES($document_id, $userid, '$what')"; | |
| 361 | + DBUtil::runQuery($sql); | |
| 362 | + | |
| 363 | +// if ($this->debug) $default->log->debug("index: Queuing indexing of $document_id"); | |
| 364 | + } | |
| 365 | + | |
| 366 | + | |
| 367 | + public static function indexAll() | |
| 368 | + { | |
| 369 | + $userid=$_SESSION['userID']; | |
| 370 | + if (empty($userid)) $userid=1; | |
| 371 | + $sql = "INSERT INTO index_files(document_id, user_id, what) SELECT id, $userid, 'C' FROM documents WHERE status_id=1"; | |
| 372 | + DBUtil::runQuery($sql); | |
| 373 | + } | |
| 374 | + | |
| 375 | + /** | |
| 376 | + * Clearout the scheduling of documents that no longer exist. | |
| 377 | + * | |
| 378 | + */ | |
| 379 | + public static function clearoutDeleted() | |
| 380 | + { | |
| 381 | + global $default; | |
| 382 | + | |
| 383 | + $sql = 'DELETE FROM | |
| 384 | + index_files AS iff USING index_files AS iff, documents | |
| 385 | + WHERE | |
| 386 | + NOT EXISTS( | |
| 387 | + SELECT | |
| 388 | + d.id | |
| 389 | + FROM | |
| 390 | + documents AS d | |
| 391 | + INNER JOIN document_metadata_version dmv ON d.metadata_version_id=dmv.id | |
| 392 | + WHERE | |
| 393 | + iff.document_id = d.id OR dmv.status_id=3 | |
| 394 | + );'; | |
| 395 | + DBUtil::runQuery($sql); | |
| 396 | + | |
| 397 | + // if ($this->debug) $default->log->debug("clearoutDeleted: remove documents"); | |
| 398 | + } | |
| 399 | + | |
| 400 | + | |
| 401 | + /** | |
| 402 | + * Check if a document is scheduled to be indexed | |
| 403 | + * | |
| 404 | + * @param mixed $document This may be a document or document id | |
| 405 | + * @return boolean | |
| 406 | + */ | |
| 407 | + public static function isDocumentScheduled($document) | |
| 408 | + { | |
| 409 | + if (is_numeric($document)) | |
| 410 | + { | |
| 411 | + $docid = $document; | |
| 412 | + } | |
| 413 | + else if ($document instanceof Document) | |
| 414 | + { | |
| 415 | + $docid = $document->getId(); | |
| 416 | + } | |
| 417 | + else | |
| 418 | + { | |
| 419 | + return false; | |
| 420 | + } | |
| 421 | + $sql = "SELECT 1 FROM index_files WHERE document_id=$docid"; | |
| 422 | + $result = DBUtil::getResultArray($sql); | |
| 423 | + return count($result) > 0; | |
| 424 | + } | |
| 425 | + | |
| 426 | + /** | |
| 427 | + * Filters text removing redundant characters such as continuous newlines and spaces. | |
| 428 | + * | |
| 429 | + * @param string $filename | |
| 430 | + */ | |
| 431 | + private function filterText($filename) | |
| 432 | + { | |
| 433 | + $content = file_get_contents($filename); | |
| 434 | + | |
| 435 | + $src = array("([\r\n])","([\n][\n])","([\n])","([\t])",'([ ][ ])'); | |
| 436 | + $tgt = array("\n","\n",' ',' ',' '); | |
| 437 | + | |
| 438 | + // shrink what is being stored. | |
| 439 | + do | |
| 440 | + { | |
| 441 | + $orig = $content; | |
| 442 | + $content = preg_replace($src, $tgt, $content); | |
| 443 | + } while ($content != $orig); | |
| 444 | + | |
| 445 | + return file_put_contents($filename, $content); | |
| 446 | + } | |
| 447 | + | |
| 448 | + /** | |
| 449 | + * Load hooks for text extraction process. | |
| 450 | + * | |
| 451 | + */ | |
| 452 | + private function loadExtractorHooks() | |
| 453 | + { | |
| 454 | + $this->generalHookCache = array(); | |
| 455 | + $this->mimeHookCache = array(); | |
| 456 | + | |
| 457 | + $dir = opendir($this->hookPath); | |
| 458 | + while (($file = readdir($dir)) !== false) | |
| 459 | + { | |
| 460 | + if (substr($file,-12) == 'Hook.inc.php') | |
| 461 | + { | |
| 462 | + require_once($this->hookPath . '/' . $file); | |
| 463 | + $class = substr($file, 0, -8); | |
| 464 | + | |
| 465 | + if (!class_exists($class)) | |
| 466 | + { | |
| 467 | + continue; | |
| 468 | + } | |
| 469 | + | |
| 470 | + $hook = new $class; | |
| 471 | + if (!($class instanceof ExtractorHook)) | |
| 472 | + { | |
| 473 | + continue; | |
| 474 | + } | |
| 475 | + | |
| 476 | + $mimeTypes = $hook->registerMimeTypes(); | |
| 477 | + if (is_null($mimeTypes)) | |
| 478 | + { | |
| 479 | + $this->generalHookCache[] = & $hook; | |
| 480 | + } | |
| 481 | + else | |
| 482 | + { | |
| 483 | + foreach($mimeTypes as $type) | |
| 484 | + { | |
| 485 | + $this->mimeHookCache[$type][] = & $hook; | |
| 486 | + } | |
| 487 | + } | |
| 488 | + | |
| 489 | + } | |
| 490 | + } | |
| 491 | + closedir($dir); | |
| 492 | + } | |
| 493 | + | |
| 494 | + /** | |
| 495 | + * This is a refactored function to execute the hooks. | |
| 496 | + * | |
| 497 | + * @param DocumentExtractor $extractor | |
| 498 | + * @param string $phase | |
| 499 | + * @param string $mimeType Optional. If set, indicates which hooks must be used, else assume general. | |
| 500 | + */ | |
| 501 | + private function executeHook($extractor, $phase, $mimeType = null) | |
| 502 | + { | |
| 503 | + $hooks = array(); | |
| 504 | + if (is_null($mimeType)) | |
| 505 | + { | |
| 506 | + $hooks = $this->generalHookCache; | |
| 507 | + } | |
| 508 | + else | |
| 509 | + { | |
| 510 | + if (array_key_exists($mimeType, $this->mimeHookCache)) | |
| 511 | + { | |
| 512 | + $hooks = $this->mimeHookCache[$mimeType]; | |
| 513 | + } | |
| 514 | + } | |
| 515 | + if (empty($hooks)) | |
| 516 | + { | |
| 517 | + return; | |
| 518 | + } | |
| 519 | + | |
| 520 | + foreach($hooks as $hook) | |
| 521 | + { | |
| 522 | + $hook->$phase($extractor); | |
| 523 | + } | |
| 524 | + } | |
| 525 | + | |
| 526 | + /** | |
| 527 | + * The main function that may be called repeatedly to index documents. | |
| 528 | + * | |
| 529 | + * @param int $max Default 20 | |
| 530 | + */ | |
| 531 | + public function indexDocuments($max=null) | |
| 532 | + { | |
| 533 | + global $default; | |
| 534 | + | |
| 535 | + $config =& KTConfig::getSingleton(); | |
| 536 | + | |
| 537 | + if (is_null($max)) | |
| 538 | + { | |
| 539 | + $max = $config->get('indexer/batchDocuments',20); | |
| 540 | + } | |
| 541 | + | |
| 542 | + $this->loadExtractorHooks(); | |
| 543 | + | |
| 544 | + Indexer::clearoutDeleted(); | |
| 545 | + | |
| 546 | + // identify the indexers that must run | |
| 547 | + // mysql specific limit! | |
| 548 | + $sql = "SELECT | |
| 549 | + iff.document_id, mt.filetypes, mt.mimetypes, mt.extractor, iff.what | |
| 550 | + FROM | |
| 551 | + index_files iff | |
| 552 | + INNER JOIN documents d ON iff.document_id=d.id | |
| 553 | + INNER JOIN document_metadata_version dmv ON d.metadata_version_id=dmv.id | |
| 554 | + INNER JOIN document_content_version dcv ON dmv.content_version_id=dcv.id | |
| 555 | + INNER JOIN mime_types mt ON dcv.mime_id=mt.id | |
| 556 | + WHERE | |
| 557 | + iff.processdate IS NULL AND dmv.status_id=1 | |
| 558 | + ORDER BY indexdate | |
| 559 | + LIMIT $max"; | |
| 560 | + $result = DBUtil::getResultArray($sql); | |
| 561 | + if (PEAR::isError($result)) | |
| 562 | + { | |
| 563 | + return; | |
| 564 | + } | |
| 565 | + | |
| 566 | + // bail if no work to do | |
| 567 | + if (count($result) == 0) | |
| 568 | + { | |
| 569 | + return; | |
| 570 | + } | |
| 571 | + | |
| 572 | + // identify any documents that need indexing and mark them | |
| 573 | + // so they are not taken in a followup run | |
| 574 | + $ids = array(); | |
| 575 | + foreach($result as $docinfo) | |
| 576 | + { | |
| 577 | + $ids[] = $docinfo['document_id']; | |
| 578 | + } | |
| 579 | + | |
| 580 | + // mark the documents as being processed | |
| 581 | + $date = date('Y-m-d H:j:s'); | |
| 582 | + $ids=implode(',',$ids); | |
| 583 | + $sql = "UPDATE index_files SET processdate='$date' WHERE document_id in ($ids)"; | |
| 584 | + DBUtil::runQuery($sql); | |
| 585 | + | |
| 586 | + $extractorCache = array(); | |
| 587 | + $storageManager = KTStorageManagerUtil::getSingleton(); | |
| 588 | + | |
| 589 | + $tempPath = $config->get("urls/tmpDirectory"); | |
| 590 | + | |
| 591 | + foreach($result as $docinfo) | |
| 592 | + { | |
| 593 | + $docId=$docinfo['document_id']; | |
| 594 | + $extension=$docinfo['filetypes']; | |
| 595 | + $mimeType=$docinfo['mimetypes']; | |
| 596 | + $extractorClass=$docinfo['extractor']; | |
| 597 | + $indexDocument = in_array($docinfo['what'], array('A','C')); | |
| 598 | + $indexDiscussion = in_array($docinfo['what'], array('A','D')); | |
| 599 | + | |
| 600 | + if ($this->debug) $default->log->debug("Indexing docid: $docId extension: '$extension' mimetype: '$mimeType' extractor: '$extractorClass'"); | |
| 601 | + | |
| 602 | + if (empty($extractorClass)) | |
| 603 | + { | |
| 604 | + if ($this->debug) $default->log->debug("No extractor for docid: $docId"); | |
| 605 | + | |
| 606 | + Indexer::unqueueDocument($docId); | |
| 607 | + continue; | |
| 608 | + } | |
| 609 | + | |
| 610 | + if ($this->debug) print "Processing document $docId.\n"; | |
| 611 | + if ($indexDocument) | |
| 612 | + { | |
| 613 | + if (array_key_exists($extractorClass, $extractorCache)) | |
| 614 | + { | |
| 615 | + $extractor = $extractorCache[$extractorClass]; | |
| 616 | + } | |
| 617 | + else | |
| 618 | + { | |
| 619 | + require_once('extractors/' . $extractorClass . '.inc.php'); | |
| 620 | + | |
| 621 | + if (!class_exists($extractorClass)) | |
| 622 | + { | |
| 623 | + $default->log->error("indexDocuments: extractor '$extractorClass' does not exist."); | |
| 624 | + continue; | |
| 625 | + } | |
| 626 | + | |
| 627 | + $extractor = $extractorCache[$extractorClass] = new $extractorClass(); | |
| 628 | + } | |
| 629 | + | |
| 630 | + if (is_null($extractor)) | |
| 631 | + { | |
| 632 | + $default->log->error("indexDocuments: extractor '$extractorClass' not resolved - it is null."); | |
| 633 | + continue; | |
| 634 | + } | |
| 635 | + | |
| 636 | + if (!($extractor instanceof DocumentExtractor)) | |
| 637 | + { | |
| 638 | + $default->log->error("indexDocuments: extractor '$extractorClass' is not a document extractor class."); | |
| 639 | + continue; | |
| 640 | + } | |
| 641 | + | |
| 642 | + $document = Document::get($docId); | |
| 643 | + $sourceFile = $storageManager->temporaryFile($document); | |
| 644 | + | |
| 645 | + if (empty($sourceFile) || !is_file($sourceFile)) | |
| 646 | + { | |
| 647 | + $default->log->error("indexDocuments: source file '$sourceFile' for document $docId does not exist."); | |
| 648 | + Indexer::unqueueDocument($docId); | |
| 649 | + continue; | |
| 650 | + } | |
| 651 | + | |
| 652 | + if ($extractor->needsIntermediateSourceFile()) | |
| 653 | + { | |
| 654 | + $intermediate = $tempPath . '/'. $document->getFileName(); | |
| 655 | + $result = @copy($sourceFile, $intermediate); | |
| 656 | + if ($result === false) | |
| 657 | + { | |
| 658 | + $default->log->error("Could not create intermediate file from document $docid"); | |
| 659 | + // problem. lets try again later. probably permission related. log the issue. | |
| 660 | + continue; | |
| 661 | + } | |
| 662 | + $sourceFile = $intermediate; | |
| 663 | + } | |
| 664 | + | |
| 665 | + $targetFile = tempnam($tempPath, 'ktindexer') . '.txt'; | |
| 666 | + | |
| 667 | + $extractor->setSourceFile($sourceFile); | |
| 668 | + $extractor->setMimeType($mimeType); | |
| 669 | + $extractor->setExtension($extension); | |
| 670 | + $extractor->setTargetFile($targetFile); | |
| 671 | + $extractor->setDocument($document); | |
| 672 | + $extractor->setIndexingStatus(null); | |
| 673 | + $extractor->setExtractionStatus(null); | |
| 674 | + if ($this->debug) $default->log->debug("Extra Info docid: $docId Source File: '$sourceFile' Target File: '$targetFile'"); | |
| 675 | + | |
| 676 | + $this->executeHook($extractor, 'pre_extract'); | |
| 677 | + $this->executeHook($extractor, 'pre_extract', $mimeType); | |
| 678 | + | |
| 679 | + if ($extractor->extractTextContent()) | |
| 680 | + { | |
| 681 | + $extractor->setExtractionStatus(true); | |
| 682 | + $this->executeHook($extractor, 'pre_index'); | |
| 683 | + $this->executeHook($extractor, 'pre_index', $mimeType); | |
| 684 | + | |
| 685 | + $title = $document->getName(); | |
| 686 | + if ($indexDiscussion) | |
| 687 | + { | |
| 688 | + $indexStatus = $this->indexDocumentAndDiscussion($docId, $targetFile, $title); | |
| 689 | + | |
| 690 | + if (!$indexStatus) $default->log->error("Problem indexing document $docId"); | |
| 691 | + | |
| 692 | + $extractor->setIndexingStatus($indexStatus); | |
| 693 | + } | |
| 694 | + else | |
| 695 | + { | |
| 696 | + if (!$this->filterText($targetFile)) | |
| 697 | + { | |
| 698 | + $default->log->error("Problem filtering document $docId"); | |
| 699 | + } | |
| 700 | + else | |
| 701 | + { | |
| 702 | + $indexStatus = $this->indexDocument($docId, $targetFile, $title); | |
| 703 | + | |
| 704 | + if (!$indexStatus) $default->log->error("Problem indexing document $docId"); | |
| 705 | + | |
| 706 | + $extractor->setIndexingStatus($indexStatus); | |
| 707 | + } | |
| 708 | + } | |
| 709 | + | |
| 710 | + $this->executeHook($extractor, 'post_index', $mimeType); | |
| 711 | + $this->executeHook($extractor, 'post_index'); | |
| 712 | + } | |
| 713 | + else | |
| 714 | + { | |
| 715 | + $extractor->setExtractionStatus(false); | |
| 716 | + $default->log->error("Could not extract contents from document $docId"); | |
| 717 | + } | |
| 718 | + | |
| 719 | + $this->executeHook($extractor, 'post_extract', $mimeType); | |
| 720 | + $this->executeHook($extractor, 'post_extract'); | |
| 721 | + | |
| 722 | + if ($extractor->needsIntermediateSourceFile()) | |
| 723 | + { | |
| 724 | + @unlink($sourceFile); | |
| 725 | + } | |
| 726 | + | |
| 727 | + @unlink($targetFile); | |
| 728 | + } | |
| 729 | + else | |
| 730 | + { | |
| 731 | + $this->indexDiscussion($docId); | |
| 732 | + } | |
| 733 | + | |
| 734 | + Indexer::unqueueDocument($docId); | |
| 735 | + if ($this->debug) $default->log->debug("Done indexing docid: $docId"); | |
| 736 | + | |
| 737 | + } | |
| 738 | + if ($this->debug) print "Done.\n"; | |
| 739 | + } | |
| 740 | + | |
| 741 | + /** | |
| 742 | + * Index a document. The base class must override this function. | |
| 743 | + * | |
| 744 | + * @param int $docId | |
| 745 | + * @param string $textFile | |
| 746 | + */ | |
| 747 | + protected abstract function indexDocument($docId, $textFile, $title=''); | |
| 748 | + | |
| 749 | + /** | |
| 750 | + * Index a discussion. The base class must override this function. | |
| 751 | + * | |
| 752 | + * @param int $docId | |
| 753 | + */ | |
| 754 | + protected abstract function indexDiscussion($docId); | |
| 755 | + | |
| 756 | + /** | |
| 757 | + * Diagnose the extractors. | |
| 758 | + * | |
| 759 | + * @return array | |
| 760 | + */ | |
| 761 | + public function diagnose() | |
| 762 | + { | |
| 763 | + $diagnosis = $this->_diagnose($this->extractorPath, 'DocumentExtractor', 'Extractor.inc.php'); | |
| 764 | + $diagnosis = array_merge($diagnosis, $this->_diagnose($this->hookPath, 'Hook', 'Hook.inc.php')); | |
| 765 | + | |
| 766 | + return $diagnosis; | |
| 767 | + } | |
| 768 | + | |
| 769 | + /** | |
| 770 | + * This is a refactored diagnose function. | |
| 771 | + * | |
| 772 | + * @param string $path | |
| 773 | + * @param string $class | |
| 774 | + * @param string $extension | |
| 775 | + * @return array | |
| 776 | + */ | |
| 777 | + private function _diagnose($path, $baseclass, $extension) | |
| 778 | + { | |
| 779 | + global $default; | |
| 780 | + | |
| 781 | + $diagnoses = array(); | |
| 782 | + $dir = opendir($path); | |
| 783 | + $extlen = - strlen($extension); | |
| 784 | + while (($file = readdir($dir)) !== false) | |
| 785 | + { | |
| 786 | + if (substr($file,$extlen) != $extension) | |
| 787 | + { | |
| 788 | + $default->log->error("diagnose: '$file' does not have extension '$extension'."); | |
| 789 | + continue; | |
| 790 | + } | |
| 791 | + | |
| 792 | + require_once($path . '/' . $file); | |
| 793 | + | |
| 794 | + $class = substr($file, 0, -8); | |
| 795 | + if (!class_exists($class)) | |
| 796 | + { | |
| 797 | + $default->log->error("diagnose: class '$class' does not exist."); | |
| 798 | + continue; | |
| 799 | + } | |
| 800 | + | |
| 801 | + $extractor = new $class(); | |
| 802 | + if (!is_a($extractor, $baseclass)) | |
| 803 | + { | |
| 804 | + $default->log->error("diagnose(): '$class' is not of type DocumentExtractor"); | |
| 805 | + continue; | |
| 806 | + } | |
| 807 | + | |
| 808 | + $types = $extractor->getSupportedMimeTypes(); | |
| 809 | + if (empty($types)) | |
| 810 | + { | |
| 811 | + if ($this->debug) $default->log->debug("diagnose: class '$class' does not support any types."); | |
| 812 | + continue; | |
| 813 | + } | |
| 814 | + | |
| 815 | + $diagnosis=$extractor->diagnose(); | |
| 816 | + if (empty($diagnosis)) | |
| 817 | + { | |
| 818 | + continue; | |
| 819 | + } | |
| 820 | + $diagnoses[$class] = array( | |
| 821 | + 'name'=>$extractor->getDisplayName(), | |
| 822 | + 'diagnosis'=>$diagnosis | |
| 823 | + ); | |
| 824 | + | |
| 825 | + } | |
| 826 | + closedir($dir); | |
| 827 | + | |
| 828 | + return $diagnoses; | |
| 829 | + } | |
| 830 | + | |
| 831 | + | |
| 832 | + /** | |
| 833 | + * Register the extractor types. | |
| 834 | + * | |
| 835 | + * @param boolean $clear. Optional. Defaults to false. | |
| 836 | + */ | |
| 837 | + public function registerTypes($clear=false) | |
| 838 | + { | |
| 839 | + if ($clear) | |
| 840 | + { | |
| 841 | + $this->clearExtractors(); | |
| 842 | + } | |
| 843 | + $dir = opendir($this->extractorPath); | |
| 844 | + while (($file = readdir($dir)) !== false) | |
| 845 | + { | |
| 846 | + if (substr($file,-17) == 'Extractor.inc.php') | |
| 847 | + { | |
| 848 | + require_once($this->extractorPath . '/' . $file); | |
| 849 | + $class = substr($file, 0, -8); | |
| 850 | + | |
| 851 | + if (class_exists($class)) | |
| 852 | + { | |
| 853 | + continue; | |
| 854 | + } | |
| 855 | + | |
| 856 | + $extractor = new $class; | |
| 857 | + if (!($class instanceof DocumentExtractor)) | |
| 858 | + { | |
| 859 | + continue; | |
| 860 | + } | |
| 861 | + | |
| 862 | + $extractor->registerMimeTypes(); | |
| 863 | + } | |
| 864 | + } | |
| 865 | + closedir($dir); | |
| 866 | + } | |
| 867 | + | |
| 868 | + /** | |
| 869 | + * This is used as a possible obtimisation effort. It may be overridden in that case. | |
| 870 | + * | |
| 871 | + * @param int $docId | |
| 872 | + * @param string $textFile | |
| 873 | + */ | |
| 874 | + protected function indexDocumentAndDiscussion($docId, $textFile, $title='') | |
| 875 | + { | |
| 876 | + $this->indexDocument($docId, $textFile, $title); | |
| 877 | + $this->indexDiscussion($docId); | |
| 878 | + } | |
| 879 | + | |
| 880 | + /** | |
| 881 | + * Remove the document from the queue. This is normally called when it has been processed. | |
| 882 | + * | |
| 883 | + * @param int $docid | |
| 884 | + */ | |
| 885 | + public static function unqueueDocument($docid) | |
| 886 | + { | |
| 887 | + $sql = "DELETE FROM index_files WHERE document_id=$docid"; | |
| 888 | + DBUtil::runQuery($sql); | |
| 889 | + } | |
| 890 | + | |
| 891 | + /** | |
| 892 | + * Run a query on the index. | |
| 893 | + * | |
| 894 | + * @param string $query | |
| 895 | + * @return array | |
| 896 | + */ | |
| 897 | + public abstract function query($query); | |
| 898 | + | |
| 899 | + /** | |
| 900 | + * Converts an integer to a string that can be easily compared and reversed. | |
| 901 | + * | |
| 902 | + * @param int $int | |
| 903 | + * @return string | |
| 904 | + */ | |
| 905 | + public static function longToString($int) | |
| 906 | + { | |
| 907 | + $maxlen = 14; | |
| 908 | + | |
| 909 | + $a2z = array('a','b','c','d','e','f','g','h','i','j'); | |
| 910 | + $o29 = array('0','1','2','3','4','5','6','7','8','9'); | |
| 911 | + $l = str_pad('',$maxlen - strlen("$int"),'0') . $int; | |
| 912 | + | |
| 913 | + return str_replace($o29, $a2z, $l); | |
| 914 | + } | |
| 915 | + | |
| 916 | + /** | |
| 917 | + * Converts a string to an integer. | |
| 918 | + * | |
| 919 | + * @param string $str | |
| 920 | + * @return int | |
| 921 | + */ | |
| 922 | + public static function stringToLong($str) | |
| 923 | + { | |
| 924 | + $a2z = array('a','b','c','d','e','f','g','h','i','j'); | |
| 925 | + $o29 = array('0','1','2','3','4','5','6','7','8','9'); | |
| 926 | + | |
| 927 | + $int = str_replace($a2z, $o29, $str) + 0; | |
| 928 | + | |
| 929 | + return $int; | |
| 930 | + } | |
| 931 | + | |
| 932 | + /** | |
| 933 | + * Possibly we can optimise indexes. This method must be overriden. | |
| 934 | + * | |
| 935 | + */ | |
| 936 | + public function optimise() | |
| 937 | + { | |
| 938 | + // do nothing | |
| 939 | + } | |
| 940 | +} | |
| 941 | + | |
| 942 | +?> | |
| 0 | 943 | \ No newline at end of file | ... | ... |
search2/indexing/indexers/JavaXMLRPCLuceneIndexer.inc.php
0 → 100755
search2/indexing/indexers/PHPLuceneIndexer.inc.php
0 → 100755
| 1 | +<? | |
| 2 | + | |
| 3 | +require_once 'Zend/Search/Lucene.php'; | |
| 4 | + | |
| 5 | +class PHPLuceneIndexer extends Indexer | |
| 6 | +{ | |
| 7 | + /** | |
| 8 | + * @var Zend_Search_Lucene | |
| 9 | + */ | |
| 10 | + private $lucene; | |
| 11 | + | |
| 12 | + /** | |
| 13 | + * The constructor for PHP Lucene | |
| 14 | + * | |
| 15 | + * @param boolean $create Optional. If true, the lucene index will be recreated. | |
| 16 | + */ | |
| 17 | + public function __construct() | |
| 18 | + { | |
| 19 | + parent::__construct(); | |
| 20 | + $config =& KTConfig::getSingleton(); | |
| 21 | + $indexPath = $config->get('indexer/luceneDirectory'); | |
| 22 | + $this->lucene = new Zend_Search_Lucene($indexPath, false); | |
| 23 | + } | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * Creates an index to be used. | |
| 27 | + * | |
| 28 | + */ | |
| 29 | + public static function createIndex() | |
| 30 | + { | |
| 31 | + $config =& KTConfig::getSingleton(); | |
| 32 | + $indexPath = $config->get('indexer/luceneDirectory'); | |
| 33 | + $lucene = new Zend_Search_Lucene($indexPath, true); | |
| 34 | + } | |
| 35 | + | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * A refactored method to add the document to the index.. | |
| 39 | + * | |
| 40 | + * @param int $docid | |
| 41 | + * @param string $content | |
| 42 | + * @param string $discussion | |
| 43 | + */ | |
| 44 | + private function addDocument($docid, $content, $discussion, $title='') | |
| 45 | + { | |
| 46 | + $doc = new Zend_Search_Lucene_Document(); | |
| 47 | + $doc->addField(Zend_Search_Lucene_Field::Text('DocumentID', PHPLuceneIndexer::longToString($docid))); | |
| 48 | + $doc->addField(Zend_Search_Lucene_Field::Text('Content', $content, 'UTF-8')); | |
| 49 | + $doc->addField(Zend_Search_Lucene_Field::Text('Discussion', $discussion, 'UTF-8')); | |
| 50 | + $doc->addField(Zend_Search_Lucene_Field::Text('Title', $title, 'UTF-8')); | |
| 51 | + $this->lucene->addDocument($doc); | |
| 52 | + } | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * Indexes a document based on a text file. | |
| 56 | + * | |
| 57 | + * @param int $docid | |
| 58 | + * @param string $textfile | |
| 59 | + * @return boolean | |
| 60 | + */ | |
| 61 | + protected function indexDocument($docid, $textfile, $title='') | |
| 62 | + { | |
| 63 | + global $default; | |
| 64 | + | |
| 65 | + if (!is_file($textfile)) | |
| 66 | + { | |
| 67 | + $default->log->error("Attempting to index $docid $textfile but it is not available."); | |
| 68 | + return false; | |
| 69 | + } | |
| 70 | + | |
| 71 | + list($content, $discussion) = $this->deleteDocument($docid); | |
| 72 | + | |
| 73 | + $this->addDocument($docid, file_get_contents($textfile), $discussion, $title); | |
| 74 | + | |
| 75 | + return true; | |
| 76 | + } | |
| 77 | + | |
| 78 | + /** | |
| 79 | + * Indexes the content and discussions on a document. | |
| 80 | + * | |
| 81 | + * @param int $docid | |
| 82 | + * @param string $textfile | |
| 83 | + * @return boolean | |
| 84 | + */ | |
| 85 | + protected function indexDocumentAndDiscussion($docid, $textfile, $title='') | |
| 86 | + { | |
| 87 | + global $default; | |
| 88 | + | |
| 89 | + if (!is_file($textfile)) | |
| 90 | + { | |
| 91 | + $default->log->error("Attempting to index $docid $textfile but it is not available."); | |
| 92 | + return false; | |
| 93 | + } | |
| 94 | + | |
| 95 | + $this->deleteDocument($docid); | |
| 96 | + | |
| 97 | + $this->addDocument($docid, file_get_contents($textfile), Indexer::getDiscussionText($docid), $title); | |
| 98 | + | |
| 99 | + return true; | |
| 100 | + } | |
| 101 | + | |
| 102 | + /** | |
| 103 | + * Indexes a discussion on a document.. | |
| 104 | + * | |
| 105 | + * @param int $docid | |
| 106 | + * @return boolean | |
| 107 | + */ | |
| 108 | + protected function indexDiscussion($docid) | |
| 109 | + { | |
| 110 | + list($content, $discussion, $title) = $this->deleteDocument($docid); | |
| 111 | + | |
| 112 | + $this->addDocument($docid, $content, Indexer::getDiscussionText($docid), $title); | |
| 113 | + | |
| 114 | + return true; | |
| 115 | + } | |
| 116 | + | |
| 117 | + /** | |
| 118 | + * Optimise the lucene index. | |
| 119 | + * This can be called periodically to optimise performance and size of the lucene index. | |
| 120 | + * | |
| 121 | + */ | |
| 122 | + public function optimise() | |
| 123 | + { | |
| 124 | + $this->lucene->optimize(); | |
| 125 | + } | |
| 126 | + | |
| 127 | + /** | |
| 128 | + * Removes a document from the index. | |
| 129 | + * | |
| 130 | + * @param int $docid | |
| 131 | + * @return array containing (content, discussion, title) | |
| 132 | + */ | |
| 133 | + public function deleteDocument($docid) | |
| 134 | + { | |
| 135 | + $content = ''; | |
| 136 | + $discussion = ''; | |
| 137 | + $query = Zend_Search_Lucene_Search_QueryParser::parse('DocumentID:' . PHPLuceneIndexer::longToString($docid)); | |
| 138 | + $hits = $this->lucene->find($query); | |
| 139 | + // there should only be one, but we'll loop for safety | |
| 140 | + foreach ($hits as $hit) | |
| 141 | + { | |
| 142 | + $content = $hit->Content; | |
| 143 | + $discussion = $hit->Discussion; | |
| 144 | + $title = $hit->Title; | |
| 145 | + $title=''; | |
| 146 | + | |
| 147 | + $this->lucene->delete($hit); | |
| 148 | + } | |
| 149 | + return array($content, $discussion, $title); | |
| 150 | + } | |
| 151 | + | |
| 152 | + /** | |
| 153 | + * Enter description here... | |
| 154 | + * | |
| 155 | + * @param string $query | |
| 156 | + * @return array | |
| 157 | + */ | |
| 158 | + public function query($query) | |
| 159 | + { | |
| 160 | + $results = array(); | |
| 161 | + $query = Zend_Search_Lucene_Search_QueryParser::parse($query); | |
| 162 | + | |
| 163 | + $hits = $this->lucene->find($query); | |
| 164 | + foreach ($hits as $hit) | |
| 165 | + { | |
| 166 | + $document = $hit->getDocument(); | |
| 167 | + | |
| 168 | + $document_id = PHPLuceneIndexer::stringToLong($document->DocumentID); | |
| 169 | + $content = $document->Content ; | |
| 170 | + $discussion = $document->Discussion ; | |
| 171 | + $title = $document->Title; | |
| 172 | + $score = $hit->score; | |
| 173 | + | |
| 174 | + // avoid adding duplicates. If it is in already, it has higher priority. | |
| 175 | + if (!array_key_exists($document_id, $results) || $score > $results[$document_id]->Score) | |
| 176 | + { | |
| 177 | + $results[$document_id] = new QueryResultItem($document_id, $score, $title, $content, $discussion); | |
| 178 | + } | |
| 179 | + } | |
| 180 | + return $results; | |
| 181 | + } | |
| 182 | +} | |
| 183 | +?> | |
| 0 | 184 | \ No newline at end of file | ... | ... |
search2/search/SearchCommandLexer.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class SearchCommandLexer | |
| 4 | +{ | |
| 5 | + private $data; | |
| 6 | + public $offset; | |
| 7 | + public $length; | |
| 8 | + public $token; | |
| 9 | + public $value; | |
| 10 | + private $state; | |
| 11 | + private $escaped; | |
| 12 | + private $exit; | |
| 13 | + private $lookahead; | |
| 14 | + private $char; | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + public function __construct($data) | |
| 19 | + { | |
| 20 | + $this->offset=0; | |
| 21 | + $this->data=$data; | |
| 22 | + $this->token=null; | |
| 23 | + $this->value=''; | |
| 24 | + $this->length=strlen($data); | |
| 25 | + $this->state = 0; | |
| 26 | + $this->escaped=false; | |
| 27 | + $this->exit=false; | |
| 28 | + $this->lookahead=null; | |
| 29 | + $this->char=null; | |
| 30 | + } | |
| 31 | + | |
| 32 | + private function processNormalChar() | |
| 33 | + { | |
| 34 | + $append=true; | |
| 35 | + $clear=false; | |
| 36 | + $checkwords=false; | |
| 37 | + $word=''; | |
| 38 | + | |
| 39 | + if (in_array($this->char, array('=','(',')','[',']',',','!','<','>','"')) && !empty($this->value)) | |
| 40 | + { | |
| 41 | + $word=$this->value; | |
| 42 | + $checkwords=true; | |
| 43 | + $this->offset--; | |
| 44 | + $append=false; | |
| 45 | + $clear=false; | |
| 46 | + } | |
| 47 | + else | |
| 48 | + switch ($this->char) | |
| 49 | + { | |
| 50 | + case ' ': | |
| 51 | + case "\t": | |
| 52 | + case "\r": | |
| 53 | + case "\n": | |
| 54 | + if (!empty($this->value)) | |
| 55 | + { | |
| 56 | + $word=$this->value; | |
| 57 | + $checkwords=true; | |
| 58 | + } | |
| 59 | + $append=false; | |
| 60 | + $clear=true; | |
| 61 | + break; | |
| 62 | + case '=': | |
| 63 | + $this->token=SearchCommandParser::IS; | |
| 64 | + break; | |
| 65 | + case '(': | |
| 66 | + $this->token=SearchCommandParser::PAR_OPEN; | |
| 67 | + break; | |
| 68 | + case ')': | |
| 69 | + $this->token=SearchCommandParser::PAR_CLOSE; | |
| 70 | + break; | |
| 71 | + case ',': | |
| 72 | + $this->token=SearchCommandParser::COMMA; | |
| 73 | + break; | |
| 74 | + case ':': | |
| 75 | + $this->token=SearchCommandParser::COLON; | |
| 76 | + break; | |
| 77 | + case '[': | |
| 78 | + $this->token=SearchCommandParser::SQUARE_OPEN; | |
| 79 | + break; | |
| 80 | + case ']': | |
| 81 | + $this->token=SearchCommandParser::SQUARE_CLOSE; | |
| 82 | + break; | |
| 83 | + case '!': | |
| 84 | + if ($this->lookahead == '=') | |
| 85 | + { | |
| 86 | + $this->zap(); | |
| 87 | + $this->token=SearchCommandParser::IS_NOT; | |
| 88 | + } | |
| 89 | + else | |
| 90 | + { | |
| 91 | + throw new Exception('Unexpected token: ' . $this->lookahead); | |
| 92 | + } | |
| 93 | + break; | |
| 94 | + case '<': | |
| 95 | + case '>': | |
| 96 | + if ($this->lookahead == '>') | |
| 97 | + { | |
| 98 | + $this->zap(); | |
| 99 | + $this->token=SearchCommandParser::IS_NOT; | |
| 100 | + } | |
| 101 | + elseif ($this->lookahead == '=') | |
| 102 | + { | |
| 103 | + $this->zap(); | |
| 104 | + $this->token=($this->char == '<')?(SearchCommandParser::LE):(SearchCommandParser::GE); | |
| 105 | + } | |
| 106 | + else | |
| 107 | + { | |
| 108 | + $this->token=($this->char == '<')?(SearchCommandParser::LT):(SearchCommandParser::GT); | |
| 109 | + } | |
| 110 | + break; | |
| 111 | + case '"': | |
| 112 | + $clear=true; | |
| 113 | + $this->state=1; | |
| 114 | + break; | |
| 115 | + | |
| 116 | + } | |
| 117 | + if ($clear) | |
| 118 | + { | |
| 119 | + $this->char=''; | |
| 120 | + $this->value=''; | |
| 121 | + $this->token=null; | |
| 122 | + } | |
| 123 | + if ($append) | |
| 124 | + { | |
| 125 | + $this->value .= $this->char; | |
| 126 | + } | |
| 127 | + if (!is_null($this->token)) | |
| 128 | + { | |
| 129 | + $this->exit=true; | |
| 130 | + } | |
| 131 | + if ($checkwords) | |
| 132 | + { | |
| 133 | + $this->exit=true; | |
| 134 | + $this->value = $word; | |
| 135 | + switch (strtolower($word)) | |
| 136 | + { | |
| 137 | + case 'not': | |
| 138 | + $this->token = SearchCommandParser::NOT; | |
| 139 | + break; | |
| 140 | + case 'with': | |
| 141 | + $this->token = SearchCommandParser::WITH; | |
| 142 | + break; | |
| 143 | + case 'like': | |
| 144 | + $this->token = SearchCommandParser::LIKE; | |
| 145 | + break; | |
| 146 | + case 'contains': | |
| 147 | + case 'contain': | |
| 148 | + $this->token = SearchCommandParser::CONTAINS ; | |
| 149 | + break; | |
| 150 | + case 'starts': | |
| 151 | + case 'start': | |
| 152 | + $this->token = SearchCommandParser::START ; | |
| 153 | + break; | |
| 154 | + case 'ends': | |
| 155 | + case 'end': | |
| 156 | + $this->token = SearchCommandParser::END ; | |
| 157 | + break; | |
| 158 | + case 'does': | |
| 159 | + $this->token = SearchCommandParser::DOES ; | |
| 160 | + break; | |
| 161 | + case 'is': | |
| 162 | + $this->token = SearchCommandParser::IS ; | |
| 163 | + break; | |
| 164 | + case 'between': | |
| 165 | + $this->token = SearchCommandParser::BETWEEN ; | |
| 166 | + break; | |
| 167 | + case 'or': | |
| 168 | + $this->token = SearchCommandParser::OPOR ; | |
| 169 | + break; | |
| 170 | + case 'and': | |
| 171 | + $this->token = SearchCommandParser::OPAND ; | |
| 172 | + break; | |
| 173 | + | |
| 174 | + default: | |
| 175 | + | |
| 176 | + $this->token = SearchCommandParser::TERMINAL; | |
| 177 | + break; | |
| 178 | + | |
| 179 | + } | |
| 180 | + } | |
| 181 | + | |
| 182 | + } | |
| 183 | + | |
| 184 | + private function processStringChar() | |
| 185 | + { | |
| 186 | + if ($this->escaped) | |
| 187 | + { | |
| 188 | + switch($this->char) | |
| 189 | + { | |
| 190 | + case 'r': | |
| 191 | + $this->value .= "\r"; | |
| 192 | + break; | |
| 193 | + case 'n': | |
| 194 | + $this->value .= "\n"; | |
| 195 | + break; | |
| 196 | + case 't': | |
| 197 | + $this->value .= "\t"; | |
| 198 | + break; | |
| 199 | + default: | |
| 200 | + $this->value .= $this->char; | |
| 201 | + } | |
| 202 | + $this->escaped=false; | |
| 203 | + } | |
| 204 | + else | |
| 205 | + { | |
| 206 | + switch($this->char) | |
| 207 | + { | |
| 208 | + case '\\': | |
| 209 | + $this->escaped=true; | |
| 210 | + break; | |
| 211 | + case '"': | |
| 212 | + $this->escaped=false; | |
| 213 | + $this->state=0; | |
| 214 | + $this->exit=true; | |
| 215 | + $this->token = SearchCommandParser::VALUE; | |
| 216 | + break; | |
| 217 | + default: | |
| 218 | + $this->value .= $this->char; | |
| 219 | + } | |
| 220 | + } | |
| 221 | + } | |
| 222 | + | |
| 223 | + private function zap() | |
| 224 | + { | |
| 225 | + $this->char = substr($this->data,$this->offset++,1); | |
| 226 | + if ($this->offset <= $this->length) | |
| 227 | + { | |
| 228 | + $this->lookahead= substr($this->data,$this->offset,1); | |
| 229 | + } | |
| 230 | + else | |
| 231 | + { | |
| 232 | + $this->lookahead=null; | |
| 233 | + } | |
| 234 | + } | |
| 235 | + | |
| 236 | + public function yylex() | |
| 237 | + { | |
| 238 | + $this->exit=false; | |
| 239 | + $this->token=null; | |
| 240 | + $this->value=''; | |
| 241 | + while (!$this->exit) | |
| 242 | + { | |
| 243 | + if ($this->length <= $this->offset) | |
| 244 | + { | |
| 245 | + return false; | |
| 246 | + } | |
| 247 | + | |
| 248 | + $this->zap(); | |
| 249 | + switch($this->state) | |
| 250 | + { | |
| 251 | + case 0: // initial | |
| 252 | + $this->processNormalChar(); | |
| 253 | + break; | |
| 254 | + case 1: // instring | |
| 255 | + $this->processStringChar(); | |
| 256 | + break; | |
| 257 | + } | |
| 258 | + | |
| 259 | + if (is_null($this->lookahead) || !is_null($this->token)) | |
| 260 | + { | |
| 261 | + $this->exit=true; | |
| 262 | + } | |
| 263 | + } | |
| 264 | + return true; | |
| 265 | + } | |
| 266 | +} | |
| 267 | + | |
| 268 | +?> | |
| 0 | 269 | \ No newline at end of file | ... | ... |
search2/search/SearchCommandParser.php
0 → 100755
| 1 | +<?php | |
| 2 | +/* Driver template for the PHP_SearchCommandParserrGenerator parser generator. (PHP port of LEMON) | |
| 3 | +*/ | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * This can be used to store both the string representation of | |
| 7 | + * a token, and any useful meta-data associated with the token. | |
| 8 | + * | |
| 9 | + * meta-data should be stored as an array | |
| 10 | + */ | |
| 11 | +class SearchCommandParseryyToken implements ArrayAccess | |
| 12 | +{ | |
| 13 | + public $string = ''; | |
| 14 | + public $metadata = array(); | |
| 15 | + | |
| 16 | + function __construct($s, $m = array()) | |
| 17 | + { | |
| 18 | + if ($s instanceof SearchCommandParseryyToken) { | |
| 19 | + $this->string = $s->string; | |
| 20 | + $this->metadata = $s->metadata; | |
| 21 | + } else { | |
| 22 | + $this->string = (string) $s; | |
| 23 | + if ($m instanceof SearchCommandParseryyToken) { | |
| 24 | + $this->metadata = $m->metadata; | |
| 25 | + } elseif (is_array($m)) { | |
| 26 | + $this->metadata = $m; | |
| 27 | + } | |
| 28 | + } | |
| 29 | + } | |
| 30 | + | |
| 31 | + function __toString() | |
| 32 | + { | |
| 33 | + return $this->_string; | |
| 34 | + } | |
| 35 | + | |
| 36 | + function offsetExists($offset) | |
| 37 | + { | |
| 38 | + return isset($this->metadata[$offset]); | |
| 39 | + } | |
| 40 | + | |
| 41 | + function offsetGet($offset) | |
| 42 | + { | |
| 43 | + return $this->metadata[$offset]; | |
| 44 | + } | |
| 45 | + | |
| 46 | + function offsetSet($offset, $value) | |
| 47 | + { | |
| 48 | + if ($offset === null) { | |
| 49 | + if (isset($value[0])) { | |
| 50 | + $x = ($value instanceof SearchCommandParseryyToken) ? | |
| 51 | + $value->metadata : $value; | |
| 52 | + $this->metadata = array_merge($this->metadata, $x); | |
| 53 | + return; | |
| 54 | + } | |
| 55 | + $offset = count($this->metadata); | |
| 56 | + } | |
| 57 | + if ($value === null) { | |
| 58 | + return; | |
| 59 | + } | |
| 60 | + if ($value instanceof SearchCommandParseryyToken) { | |
| 61 | + if ($value->metadata) { | |
| 62 | + $this->metadata[$offset] = $value->metadata; | |
| 63 | + } | |
| 64 | + } elseif ($value) { | |
| 65 | + $this->metadata[$offset] = $value; | |
| 66 | + } | |
| 67 | + } | |
| 68 | + | |
| 69 | + function offsetUnset($offset) | |
| 70 | + { | |
| 71 | + unset($this->metadata[$offset]); | |
| 72 | + } | |
| 73 | +} | |
| 74 | + | |
| 75 | +/** The following structure represents a single element of the | |
| 76 | + * parser's stack. Information stored includes: | |
| 77 | + * | |
| 78 | + * + The state number for the parser at this level of the stack. | |
| 79 | + * | |
| 80 | + * + The value of the token stored at this level of the stack. | |
| 81 | + * (In other words, the "major" token.) | |
| 82 | + * | |
| 83 | + * + The semantic value stored at this level of the stack. This is | |
| 84 | + * the information used by the action routines in the grammar. | |
| 85 | + * It is sometimes called the "minor" token. | |
| 86 | + */ | |
| 87 | +class SearchCommandParseryyStackEntry | |
| 88 | +{ | |
| 89 | + public $stateno; /* The state-number */ | |
| 90 | + public $major; /* The major token value. This is the code | |
| 91 | + ** number for the token at this stack level */ | |
| 92 | + public $minor; /* The user-supplied minor token value. This | |
| 93 | + ** is the value of the token */ | |
| 94 | +}; | |
| 95 | + | |
| 96 | +// code external to the class is included here | |
| 97 | + | |
| 98 | +// declare_class is output here | |
| 99 | +#line 2 "SearchCommandParser.y" | |
| 100 | +class SearchCommandParser#line 102 "SearchCommandParser.php" | |
| 101 | +{ | |
| 102 | +/* First off, code is included which follows the "include_class" declaration | |
| 103 | +** in the input file. */ | |
| 104 | +#line 4 "SearchCommandParser.y" | |
| 105 | + | |
| 106 | + | |
| 107 | + private $expr_result; | |
| 108 | + private $parse_result; | |
| 109 | + | |
| 110 | + public function __construct() | |
| 111 | + { | |
| 112 | + $this->parse_result = 'ok'; | |
| 113 | + } | |
| 114 | + | |
| 115 | + public function getExprResult() | |
| 116 | + { | |
| 117 | + return $this->expr_result; | |
| 118 | + } | |
| 119 | + | |
| 120 | + public function isExprOk() | |
| 121 | + { | |
| 122 | + return $this->parse_result == 'ok'; | |
| 123 | + } | |
| 124 | + | |
| 125 | +#line 128 "SearchCommandParser.php" | |
| 126 | + | |
| 127 | +/* Next is all token values, as class constants | |
| 128 | +*/ | |
| 129 | +/* | |
| 130 | +** These constants (all generated automatically by the parser generator) | |
| 131 | +** specify the various kinds of tokens (terminals) that the parser | |
| 132 | +** understands. | |
| 133 | +** | |
| 134 | +** Each symbol here is a terminal symbol in the grammar. | |
| 135 | +*/ | |
| 136 | + const OPOR = 1; | |
| 137 | + const OPAND = 2; | |
| 138 | + const NOT = 3; | |
| 139 | + const IS = 4; | |
| 140 | + const CONTAIN = 5; | |
| 141 | + const LIKE = 6; | |
| 142 | + const BETWEEN = 7; | |
| 143 | + const START = 8; | |
| 144 | + const END = 9; | |
| 145 | + const GT = 10; | |
| 146 | + const LE = 11; | |
| 147 | + const LT = 12; | |
| 148 | + const GE = 13; | |
| 149 | + const PAR_OPEN = 14; | |
| 150 | + const PAR_CLOSE = 15; | |
| 151 | + const DOES = 16; | |
| 152 | + const COLON = 17; | |
| 153 | + const SQUARE_OPEN = 18; | |
| 154 | + const SQUARE_CLOSE = 19; | |
| 155 | + const TERMINAL = 20; | |
| 156 | + const VALUE = 21; | |
| 157 | + const COMMA = 22; | |
| 158 | + const CONTAINS = 23; | |
| 159 | + const WITH = 24; | |
| 160 | + const IS_NOT = 25; | |
| 161 | + const YY_NO_ACTION = 84; | |
| 162 | + const YY_ACCEPT_ACTION = 83; | |
| 163 | + const YY_ERROR_ACTION = 82; | |
| 164 | + | |
| 165 | +/* Next are that tables used to determine what action to take based on the | |
| 166 | +** current state and lookahead token. These tables are used to implement | |
| 167 | +** functions that take a state number and lookahead value and return an | |
| 168 | +** action integer. | |
| 169 | +** | |
| 170 | +** Suppose the action integer is N. Then the action is determined as | |
| 171 | +** follows | |
| 172 | +** | |
| 173 | +** 0 <= N < self::YYNSTATE Shift N. That is, | |
| 174 | +** push the lookahead | |
| 175 | +** token onto the stack | |
| 176 | +** and goto state N. | |
| 177 | +** | |
| 178 | +** self::YYNSTATE <= N < self::YYNSTATE+self::YYNRULE Reduce by rule N-YYNSTATE. | |
| 179 | +** | |
| 180 | +** N == self::YYNSTATE+self::YYNRULE A syntax error has occurred. | |
| 181 | +** | |
| 182 | +** N == self::YYNSTATE+self::YYNRULE+1 The parser accepts its | |
| 183 | +** input. (and concludes parsing) | |
| 184 | +** | |
| 185 | +** N == self::YYNSTATE+self::YYNRULE+2 No such action. Denotes unused | |
| 186 | +** slots in the yy_action[] table. | |
| 187 | +** | |
| 188 | +** The action table is constructed as a single large static array $yy_action. | |
| 189 | +** Given state S and lookahead X, the action is computed as | |
| 190 | +** | |
| 191 | +** self::$yy_action[self::$yy_shift_ofst[S] + X ] | |
| 192 | +** | |
| 193 | +** If the index value self::$yy_shift_ofst[S]+X is out of range or if the value | |
| 194 | +** self::$yy_lookahead[self::$yy_shift_ofst[S]+X] is not equal to X or if | |
| 195 | +** self::$yy_shift_ofst[S] is equal to self::YY_SHIFT_USE_DFLT, it means that | |
| 196 | +** the action is not in the table and that self::$yy_default[S] should be used instead. | |
| 197 | +** | |
| 198 | +** The formula above is for computing the action when the lookahead is | |
| 199 | +** a terminal symbol. If the lookahead is a non-terminal (as occurs after | |
| 200 | +** a reduce action) then the static $yy_reduce_ofst array is used in place of | |
| 201 | +** the static $yy_shift_ofst array and self::YY_REDUCE_USE_DFLT is used in place of | |
| 202 | +** self::YY_SHIFT_USE_DFLT. | |
| 203 | +** | |
| 204 | +** The following are the tables generated in this section: | |
| 205 | +** | |
| 206 | +** self::$yy_action A single table containing all actions. | |
| 207 | +** self::$yy_lookahead A table containing the lookahead for each entry in | |
| 208 | +** yy_action. Used to detect hash collisions. | |
| 209 | +** self::$yy_shift_ofst For each state, the offset into self::$yy_action for | |
| 210 | +** shifting terminals. | |
| 211 | +** self::$yy_reduce_ofst For each state, the offset into self::$yy_action for | |
| 212 | +** shifting non-terminals after a reduce. | |
| 213 | +** self::$yy_default Default action for each state. | |
| 214 | +*/ | |
| 215 | + const YY_SZ_ACTTAB = 70; | |
| 216 | +static public $yy_action = array( | |
| 217 | + /* 0 */ 52, 15, 8, 7, 4, 23, 22, 37, 34, 54, | |
| 218 | + /* 10 */ 33, 3, 5, 16, 9, 2, 21, 83, 1, 13, | |
| 219 | + /* 20 */ 50, 32, 36, 3, 5, 44, 17, 26, 47, 1, | |
| 220 | + /* 30 */ 19, 39, 1, 41, 14, 46, 20, 1, 45, 38, | |
| 221 | + /* 40 */ 1, 6, 35, 10, 42, 27, 31, 12, 5, 24, | |
| 222 | + /* 50 */ 18, 53, 28, 52, 63, 63, 63, 30, 63, 63, | |
| 223 | + /* 60 */ 63, 49, 48, 29, 40, 43, 51, 63, 11, 25, | |
| 224 | + ); | |
| 225 | + static public $yy_lookahead = array( | |
| 226 | + /* 0 */ 3, 4, 6, 7, 3, 8, 9, 10, 11, 12, | |
| 227 | + /* 10 */ 13, 1, 2, 16, 17, 14, 27, 28, 29, 18, | |
| 228 | + /* 20 */ 23, 20, 25, 1, 2, 15, 14, 27, 33, 29, | |
| 229 | + /* 30 */ 27, 24, 29, 21, 30, 27, 32, 29, 27, 24, | |
| 230 | + /* 40 */ 29, 2, 19, 18, 15, 21, 19, 5, 2, 33, | |
| 231 | + /* 50 */ 22, 31, 31, 3, 34, 34, 34, 31, 34, 34, | |
| 232 | + /* 60 */ 34, 31, 31, 31, 31, 31, 31, 34, 32, 32, | |
| 233 | +); | |
| 234 | + const YY_SHIFT_USE_DFLT = -5; | |
| 235 | + const YY_SHIFT_MAX = 31; | |
| 236 | + static public $yy_shift_ofst = array( | |
| 237 | + /* 0 */ 1, -3, 1, 1, 1, 1, 12, 12, 12, 12, | |
| 238 | + /* 10 */ 12, 12, 12, 12, 12, 50, 50, 24, 24, 10, | |
| 239 | + /* 20 */ -4, 22, 15, 7, 29, 42, 46, 28, 27, 39, | |
| 240 | + /* 30 */ 23, 25, | |
| 241 | +); | |
| 242 | + const YY_REDUCE_USE_DFLT = -12; | |
| 243 | + const YY_REDUCE_MAX = 18; | |
| 244 | + static public $yy_reduce_ofst = array( | |
| 245 | + /* 0 */ -11, 4, 3, 0, 8, 11, 31, 32, 33, 30, | |
| 246 | + /* 10 */ 26, 20, 35, 21, 34, 36, 37, 16, -5, | |
| 247 | +); | |
| 248 | + static public $yyExpectedTokens = array( | |
| 249 | + /* 0 */ array(3, 14, 18, 20, ), | |
| 250 | + /* 1 */ array(3, 4, 8, 9, 10, 11, 12, 13, 16, 17, 23, 25, ), | |
| 251 | + /* 2 */ array(3, 14, 18, 20, ), | |
| 252 | + /* 3 */ array(3, 14, 18, 20, ), | |
| 253 | + /* 4 */ array(3, 14, 18, 20, ), | |
| 254 | + /* 5 */ array(3, 14, 18, 20, ), | |
| 255 | + /* 6 */ array(14, 21, ), | |
| 256 | + /* 7 */ array(14, 21, ), | |
| 257 | + /* 8 */ array(14, 21, ), | |
| 258 | + /* 9 */ array(14, 21, ), | |
| 259 | + /* 10 */ array(14, 21, ), | |
| 260 | + /* 11 */ array(14, 21, ), | |
| 261 | + /* 12 */ array(14, 21, ), | |
| 262 | + /* 13 */ array(14, 21, ), | |
| 263 | + /* 14 */ array(14, 21, ), | |
| 264 | + /* 15 */ array(3, ), | |
| 265 | + /* 16 */ array(3, ), | |
| 266 | + /* 17 */ array(21, ), | |
| 267 | + /* 18 */ array(21, ), | |
| 268 | + /* 19 */ array(1, 2, 15, ), | |
| 269 | + /* 20 */ array(6, 7, ), | |
| 270 | + /* 21 */ array(1, 2, ), | |
| 271 | + /* 22 */ array(24, ), | |
| 272 | + /* 23 */ array(24, ), | |
| 273 | + /* 24 */ array(15, ), | |
| 274 | + /* 25 */ array(5, ), | |
| 275 | + /* 26 */ array(2, ), | |
| 276 | + /* 27 */ array(22, ), | |
| 277 | + /* 28 */ array(19, ), | |
| 278 | + /* 29 */ array(2, ), | |
| 279 | + /* 30 */ array(19, ), | |
| 280 | + /* 31 */ array(18, ), | |
| 281 | + /* 32 */ array(), | |
| 282 | + /* 33 */ array(), | |
| 283 | + /* 34 */ array(), | |
| 284 | + /* 35 */ array(), | |
| 285 | + /* 36 */ array(), | |
| 286 | + /* 37 */ array(), | |
| 287 | + /* 38 */ array(), | |
| 288 | + /* 39 */ array(), | |
| 289 | + /* 40 */ array(), | |
| 290 | + /* 41 */ array(), | |
| 291 | + /* 42 */ array(), | |
| 292 | + /* 43 */ array(), | |
| 293 | + /* 44 */ array(), | |
| 294 | + /* 45 */ array(), | |
| 295 | + /* 46 */ array(), | |
| 296 | + /* 47 */ array(), | |
| 297 | + /* 48 */ array(), | |
| 298 | + /* 49 */ array(), | |
| 299 | + /* 50 */ array(), | |
| 300 | + /* 51 */ array(), | |
| 301 | + /* 52 */ array(), | |
| 302 | + /* 53 */ array(), | |
| 303 | + /* 54 */ array(), | |
| 304 | +); | |
| 305 | + static public $yy_default = array( | |
| 306 | + /* 0 */ 82, 66, 82, 82, 82, 82, 82, 82, 82, 82, | |
| 307 | + /* 10 */ 82, 82, 82, 82, 82, 66, 66, 82, 82, 82, | |
| 308 | + /* 20 */ 82, 55, 82, 82, 82, 82, 57, 73, 82, 82, | |
| 309 | + /* 30 */ 82, 82, 69, 78, 77, 68, 81, 76, 80, 79, | |
| 310 | + /* 40 */ 62, 70, 71, 60, 59, 56, 58, 72, 61, 65, | |
| 311 | + /* 50 */ 74, 64, 67, 63, 75, | |
| 312 | +); | |
| 313 | +/* The next thing included is series of defines which control | |
| 314 | +** various aspects of the generated parser. | |
| 315 | +** self::YYNOCODE is a number which corresponds | |
| 316 | +** to no legal terminal or nonterminal number. This | |
| 317 | +** number is used to fill in empty slots of the hash | |
| 318 | +** table. | |
| 319 | +** self::YYFALLBACK If defined, this indicates that one or more tokens | |
| 320 | +** have fall-back values which should be used if the | |
| 321 | +** original value of the token will not parse. | |
| 322 | +** self::YYSTACKDEPTH is the maximum depth of the parser's stack. | |
| 323 | +** self::YYNSTATE the combined number of states. | |
| 324 | +** self::YYNRULE the number of rules in the grammar | |
| 325 | +** self::YYERRORSYMBOL is the code number of the error symbol. If not | |
| 326 | +** defined, then do no error processing. | |
| 327 | +*/ | |
| 328 | + const YYNOCODE = 35; | |
| 329 | + const YYSTACKDEPTH = 100; | |
| 330 | + const YYNSTATE = 55; | |
| 331 | + const YYNRULE = 27; | |
| 332 | + const YYERRORSYMBOL = 26; | |
| 333 | + const YYERRSYMDT = 'yy0'; | |
| 334 | + const YYFALLBACK = 0; | |
| 335 | + /** The next table maps tokens into fallback tokens. If a construct | |
| 336 | + * like the following: | |
| 337 | + * | |
| 338 | + * %fallback ID X Y Z. | |
| 339 | + * | |
| 340 | + * appears in the grammer, then ID becomes a fallback token for X, Y, | |
| 341 | + * and Z. Whenever one of the tokens X, Y, or Z is input to the parser | |
| 342 | + * but it does not parse, the type of the token is changed to ID and | |
| 343 | + * the parse is retried before an error is thrown. | |
| 344 | + */ | |
| 345 | + static public $yyFallback = array( | |
| 346 | + ); | |
| 347 | + /** | |
| 348 | + * Turn parser tracing on by giving a stream to which to write the trace | |
| 349 | + * and a prompt to preface each trace message. Tracing is turned off | |
| 350 | + * by making either argument NULL | |
| 351 | + * | |
| 352 | + * Inputs: | |
| 353 | + * | |
| 354 | + * - A stream resource to which trace output should be written. | |
| 355 | + * If NULL, then tracing is turned off. | |
| 356 | + * - A prefix string written at the beginning of every | |
| 357 | + * line of trace output. If NULL, then tracing is | |
| 358 | + * turned off. | |
| 359 | + * | |
| 360 | + * Outputs: | |
| 361 | + * | |
| 362 | + * - None. | |
| 363 | + * @param resource | |
| 364 | + * @param string | |
| 365 | + */ | |
| 366 | + static function Trace($TraceFILE, $zTracePrompt) | |
| 367 | + { | |
| 368 | + if (!$TraceFILE) { | |
| 369 | + $zTracePrompt = 0; | |
| 370 | + } elseif (!$zTracePrompt) { | |
| 371 | + $TraceFILE = 0; | |
| 372 | + } | |
| 373 | + self::$yyTraceFILE = $TraceFILE; | |
| 374 | + self::$yyTracePrompt = $zTracePrompt; | |
| 375 | + } | |
| 376 | + | |
| 377 | + /** | |
| 378 | + * Output debug information to output (php://output stream) | |
| 379 | + */ | |
| 380 | + static function PrintTrace() | |
| 381 | + { | |
| 382 | + self::$yyTraceFILE = fopen('php://output', 'w'); | |
| 383 | + self::$yyTracePrompt = ''; | |
| 384 | + } | |
| 385 | + | |
| 386 | + /** | |
| 387 | + * @var resource|0 | |
| 388 | + */ | |
| 389 | + static public $yyTraceFILE; | |
| 390 | + /** | |
| 391 | + * String to prepend to debug output | |
| 392 | + * @var string|0 | |
| 393 | + */ | |
| 394 | + static public $yyTracePrompt; | |
| 395 | + /** | |
| 396 | + * @var int | |
| 397 | + */ | |
| 398 | + public $yyidx; /* Index of top element in stack */ | |
| 399 | + /** | |
| 400 | + * @var int | |
| 401 | + */ | |
| 402 | + public $yyerrcnt; /* Shifts left before out of the error */ | |
| 403 | + /** | |
| 404 | + * @var array | |
| 405 | + */ | |
| 406 | + public $yystack = array(); /* The parser's stack */ | |
| 407 | + | |
| 408 | + /** | |
| 409 | + * For tracing shifts, the names of all terminals and nonterminals | |
| 410 | + * are required. The following table supplies these names | |
| 411 | + * @var array | |
| 412 | + */ | |
| 413 | + static public $yyTokenName = array( | |
| 414 | + '$', 'OPOR', 'OPAND', 'NOT', | |
| 415 | + 'IS', 'CONTAIN', 'LIKE', 'BETWEEN', | |
| 416 | + 'START', 'END', 'GT', 'LE', | |
| 417 | + 'LT', 'GE', 'PAR_OPEN', 'PAR_CLOSE', | |
| 418 | + 'DOES', 'COLON', 'SQUARE_OPEN', 'SQUARE_CLOSE', | |
| 419 | + 'TERMINAL', 'VALUE', 'COMMA', 'CONTAINS', | |
| 420 | + 'WITH', 'IS_NOT', 'error', 'expr', | |
| 421 | + 'cmdline', 'terminal', 'operator', 'value', | |
| 422 | + 'notop', 'valuelist', | |
| 423 | + ); | |
| 424 | + | |
| 425 | + /** | |
| 426 | + * For tracing reduce actions, the names of all rules are required. | |
| 427 | + * @var array | |
| 428 | + */ | |
| 429 | + static public $yyRuleName = array( | |
| 430 | + /* 0 */ "cmdline ::= expr", | |
| 431 | + /* 1 */ "expr ::= expr OPAND expr", | |
| 432 | + /* 2 */ "expr ::= expr OPOR expr", | |
| 433 | + /* 3 */ "expr ::= NOT expr", | |
| 434 | + /* 4 */ "expr ::= PAR_OPEN expr PAR_CLOSE", | |
| 435 | + /* 5 */ "expr ::= terminal operator value", | |
| 436 | + /* 6 */ "expr ::= terminal notop BETWEEN value OPAND value", | |
| 437 | + /* 7 */ "expr ::= terminal notop LIKE value", | |
| 438 | + /* 8 */ "expr ::= terminal IS notop value", | |
| 439 | + /* 9 */ "expr ::= terminal DOES notop CONTAIN value", | |
| 440 | + /* 10 */ "expr ::= terminal COLON value", | |
| 441 | + /* 11 */ "notop ::=", | |
| 442 | + /* 12 */ "notop ::= NOT", | |
| 443 | + /* 13 */ "terminal ::= SQUARE_OPEN value SQUARE_CLOSE SQUARE_OPEN value SQUARE_CLOSE", | |
| 444 | + /* 14 */ "terminal ::= TERMINAL", | |
| 445 | + /* 15 */ "value ::= VALUE", | |
| 446 | + /* 16 */ "value ::= PAR_OPEN valuelist PAR_CLOSE", | |
| 447 | + /* 17 */ "valuelist ::= VALUE COMMA valuelist", | |
| 448 | + /* 18 */ "valuelist ::= VALUE", | |
| 449 | + /* 19 */ "operator ::= CONTAINS", | |
| 450 | + /* 20 */ "operator ::= LT", | |
| 451 | + /* 21 */ "operator ::= GT", | |
| 452 | + /* 22 */ "operator ::= LE", | |
| 453 | + /* 23 */ "operator ::= GE", | |
| 454 | + /* 24 */ "operator ::= START WITH", | |
| 455 | + /* 25 */ "operator ::= END WITH", | |
| 456 | + /* 26 */ "operator ::= IS_NOT", | |
| 457 | + ); | |
| 458 | + | |
| 459 | + /** | |
| 460 | + * This function returns the symbolic name associated with a token | |
| 461 | + * value. | |
| 462 | + * @param int | |
| 463 | + * @return string | |
| 464 | + */ | |
| 465 | + function tokenName($tokenType) | |
| 466 | + { | |
| 467 | + if ($tokenType === 0) { | |
| 468 | + return 'End of Input'; | |
| 469 | + } | |
| 470 | + if ($tokenType > 0 && $tokenType < count(self::$yyTokenName)) { | |
| 471 | + return self::$yyTokenName[$tokenType]; | |
| 472 | + } else { | |
| 473 | + return "Unknown"; | |
| 474 | + } | |
| 475 | + } | |
| 476 | + | |
| 477 | + /** | |
| 478 | + * The following function deletes the value associated with a | |
| 479 | + * symbol. The symbol can be either a terminal or nonterminal. | |
| 480 | + * @param int the symbol code | |
| 481 | + * @param mixed the symbol's value | |
| 482 | + */ | |
| 483 | + static function yy_destructor($yymajor, $yypminor) | |
| 484 | + { | |
| 485 | + switch ($yymajor) { | |
| 486 | + /* Here is inserted the actions which take place when a | |
| 487 | + ** terminal or non-terminal is destroyed. This can happen | |
| 488 | + ** when the symbol is popped from the stack during a | |
| 489 | + ** reduce or during error processing or when a parser is | |
| 490 | + ** being destroyed before it is finished parsing. | |
| 491 | + ** | |
| 492 | + ** Note: during a reduce, the only symbols destroyed are those | |
| 493 | + ** which appear on the RHS of the rule, but which are not used | |
| 494 | + ** inside the C code. | |
| 495 | + */ | |
| 496 | + default: break; /* If no destructor action specified: do nothing */ | |
| 497 | + } | |
| 498 | + } | |
| 499 | + | |
| 500 | + /** | |
| 501 | + * Pop the parser's stack once. | |
| 502 | + * | |
| 503 | + * If there is a destructor routine associated with the token which | |
| 504 | + * is popped from the stack, then call it. | |
| 505 | + * | |
| 506 | + * Return the major token number for the symbol popped. | |
| 507 | + * @param SearchCommandParseryyParser | |
| 508 | + * @return int | |
| 509 | + */ | |
| 510 | + function yy_pop_parser_stack() | |
| 511 | + { | |
| 512 | + if (!count($this->yystack)) { | |
| 513 | + return; | |
| 514 | + } | |
| 515 | + $yytos = array_pop($this->yystack); | |
| 516 | + if (self::$yyTraceFILE && $this->yyidx >= 0) { | |
| 517 | + fwrite(self::$yyTraceFILE, | |
| 518 | + self::$yyTracePrompt . 'Popping ' . self::$yyTokenName[$yytos->major] . | |
| 519 | + "\n"); | |
| 520 | + } | |
| 521 | + $yymajor = $yytos->major; | |
| 522 | + self::yy_destructor($yymajor, $yytos->minor); | |
| 523 | + $this->yyidx--; | |
| 524 | + return $yymajor; | |
| 525 | + } | |
| 526 | + | |
| 527 | + /** | |
| 528 | + * Deallocate and destroy a parser. Destructors are all called for | |
| 529 | + * all stack elements before shutting the parser down. | |
| 530 | + */ | |
| 531 | + function __destruct() | |
| 532 | + { | |
| 533 | + while ($this->yyidx >= 0) { | |
| 534 | + $this->yy_pop_parser_stack(); | |
| 535 | + } | |
| 536 | + if (is_resource(self::$yyTraceFILE)) { | |
| 537 | + fclose(self::$yyTraceFILE); | |
| 538 | + } | |
| 539 | + } | |
| 540 | + | |
| 541 | + /** | |
| 542 | + * Based on the current state and parser stack, get a list of all | |
| 543 | + * possible lookahead tokens | |
| 544 | + * @param int | |
| 545 | + * @return array | |
| 546 | + */ | |
| 547 | + function yy_get_expected_tokens($token) | |
| 548 | + { | |
| 549 | + $state = $this->yystack[$this->yyidx]->stateno; | |
| 550 | + $expected = self::$yyExpectedTokens[$state]; | |
| 551 | + if (in_array($token, self::$yyExpectedTokens[$state], true)) { | |
| 552 | + return $expected; | |
| 553 | + } | |
| 554 | + $stack = $this->yystack; | |
| 555 | + $yyidx = $this->yyidx; | |
| 556 | + do { | |
| 557 | + $yyact = $this->yy_find_shift_action($token); | |
| 558 | + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { | |
| 559 | + // reduce action | |
| 560 | + $done = 0; | |
| 561 | + do { | |
| 562 | + if ($done++ == 100) { | |
| 563 | + $this->yyidx = $yyidx; | |
| 564 | + $this->yystack = $stack; | |
| 565 | + // too much recursion prevents proper detection | |
| 566 | + // so give up | |
| 567 | + return array_unique($expected); | |
| 568 | + } | |
| 569 | + $yyruleno = $yyact - self::YYNSTATE; | |
| 570 | + $this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs']; | |
| 571 | + $nextstate = $this->yy_find_reduce_action( | |
| 572 | + $this->yystack[$this->yyidx]->stateno, | |
| 573 | + self::$yyRuleInfo[$yyruleno]['lhs']); | |
| 574 | + if (isset(self::$yyExpectedTokens[$nextstate])) { | |
| 575 | + $expected += self::$yyExpectedTokens[$nextstate]; | |
| 576 | + if (in_array($token, | |
| 577 | + self::$yyExpectedTokens[$nextstate], true)) { | |
| 578 | + $this->yyidx = $yyidx; | |
| 579 | + $this->yystack = $stack; | |
| 580 | + return array_unique($expected); | |
| 581 | + } | |
| 582 | + } | |
| 583 | + if ($nextstate < self::YYNSTATE) { | |
| 584 | + // we need to shift a non-terminal | |
| 585 | + $this->yyidx++; | |
| 586 | + $x = new SearchCommandParseryyStackEntry; | |
| 587 | + $x->stateno = $nextstate; | |
| 588 | + $x->major = self::$yyRuleInfo[$yyruleno]['lhs']; | |
| 589 | + $this->yystack[$this->yyidx] = $x; | |
| 590 | + continue 2; | |
| 591 | + } elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) { | |
| 592 | + $this->yyidx = $yyidx; | |
| 593 | + $this->yystack = $stack; | |
| 594 | + // the last token was just ignored, we can't accept | |
| 595 | + // by ignoring input, this is in essence ignoring a | |
| 596 | + // syntax error! | |
| 597 | + return array_unique($expected); | |
| 598 | + } elseif ($nextstate === self::YY_NO_ACTION) { | |
| 599 | + $this->yyidx = $yyidx; | |
| 600 | + $this->yystack = $stack; | |
| 601 | + // input accepted, but not shifted (I guess) | |
| 602 | + return $expected; | |
| 603 | + } else { | |
| 604 | + $yyact = $nextstate; | |
| 605 | + } | |
| 606 | + } while (true); | |
| 607 | + } | |
| 608 | + break; | |
| 609 | + } while (true); | |
| 610 | + return array_unique($expected); | |
| 611 | + } | |
| 612 | + | |
| 613 | + /** | |
| 614 | + * Based on the parser state and current parser stack, determine whether | |
| 615 | + * the lookahead token is possible. | |
| 616 | + * | |
| 617 | + * The parser will convert the token value to an error token if not. This | |
| 618 | + * catches some unusual edge cases where the parser would fail. | |
| 619 | + * @param int | |
| 620 | + * @return bool | |
| 621 | + */ | |
| 622 | + function yy_is_expected_token($token) | |
| 623 | + { | |
| 624 | + if ($token === 0) { | |
| 625 | + return true; // 0 is not part of this | |
| 626 | + } | |
| 627 | + $state = $this->yystack[$this->yyidx]->stateno; | |
| 628 | + if (in_array($token, self::$yyExpectedTokens[$state], true)) { | |
| 629 | + return true; | |
| 630 | + } | |
| 631 | + $stack = $this->yystack; | |
| 632 | + $yyidx = $this->yyidx; | |
| 633 | + do { | |
| 634 | + $yyact = $this->yy_find_shift_action($token); | |
| 635 | + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { | |
| 636 | + // reduce action | |
| 637 | + $done = 0; | |
| 638 | + do { | |
| 639 | + if ($done++ == 100) { | |
| 640 | + $this->yyidx = $yyidx; | |
| 641 | + $this->yystack = $stack; | |
| 642 | + // too much recursion prevents proper detection | |
| 643 | + // so give up | |
| 644 | + return true; | |
| 645 | + } | |
| 646 | + $yyruleno = $yyact - self::YYNSTATE; | |
| 647 | + $this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs']; | |
| 648 | + $nextstate = $this->yy_find_reduce_action( | |
| 649 | + $this->yystack[$this->yyidx]->stateno, | |
| 650 | + self::$yyRuleInfo[$yyruleno]['lhs']); | |
| 651 | + if (isset(self::$yyExpectedTokens[$nextstate]) && | |
| 652 | + in_array($token, self::$yyExpectedTokens[$nextstate], true)) { | |
| 653 | + $this->yyidx = $yyidx; | |
| 654 | + $this->yystack = $stack; | |
| 655 | + return true; | |
| 656 | + } | |
| 657 | + if ($nextstate < self::YYNSTATE) { | |
| 658 | + // we need to shift a non-terminal | |
| 659 | + $this->yyidx++; | |
| 660 | + $x = new SearchCommandParseryyStackEntry; | |
| 661 | + $x->stateno = $nextstate; | |
| 662 | + $x->major = self::$yyRuleInfo[$yyruleno]['lhs']; | |
| 663 | + $this->yystack[$this->yyidx] = $x; | |
| 664 | + continue 2; | |
| 665 | + } elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) { | |
| 666 | + $this->yyidx = $yyidx; | |
| 667 | + $this->yystack = $stack; | |
| 668 | + if (!$token) { | |
| 669 | + // end of input: this is valid | |
| 670 | + return true; | |
| 671 | + } | |
| 672 | + // the last token was just ignored, we can't accept | |
| 673 | + // by ignoring input, this is in essence ignoring a | |
| 674 | + // syntax error! | |
| 675 | + return false; | |
| 676 | + } elseif ($nextstate === self::YY_NO_ACTION) { | |
| 677 | + $this->yyidx = $yyidx; | |
| 678 | + $this->yystack = $stack; | |
| 679 | + // input accepted, but not shifted (I guess) | |
| 680 | + return true; | |
| 681 | + } else { | |
| 682 | + $yyact = $nextstate; | |
| 683 | + } | |
| 684 | + } while (true); | |
| 685 | + } | |
| 686 | + break; | |
| 687 | + } while (true); | |
| 688 | + $this->yyidx = $yyidx; | |
| 689 | + $this->yystack = $stack; | |
| 690 | + return true; | |
| 691 | + } | |
| 692 | + | |
| 693 | + /** | |
| 694 | + * Find the appropriate action for a parser given the terminal | |
| 695 | + * look-ahead token iLookAhead. | |
| 696 | + * | |
| 697 | + * If the look-ahead token is YYNOCODE, then check to see if the action is | |
| 698 | + * independent of the look-ahead. If it is, return the action, otherwise | |
| 699 | + * return YY_NO_ACTION. | |
| 700 | + * @param int The look-ahead token | |
| 701 | + */ | |
| 702 | + function yy_find_shift_action($iLookAhead) | |
| 703 | + { | |
| 704 | + $stateno = $this->yystack[$this->yyidx]->stateno; | |
| 705 | + | |
| 706 | + /* if ($this->yyidx < 0) return self::YY_NO_ACTION; */ | |
| 707 | + if (!isset(self::$yy_shift_ofst[$stateno])) { | |
| 708 | + // no shift actions | |
| 709 | + return self::$yy_default[$stateno]; | |
| 710 | + } | |
| 711 | + $i = self::$yy_shift_ofst[$stateno]; | |
| 712 | + if ($i === self::YY_SHIFT_USE_DFLT) { | |
| 713 | + return self::$yy_default[$stateno]; | |
| 714 | + } | |
| 715 | + if ($iLookAhead == self::YYNOCODE) { | |
| 716 | + return self::YY_NO_ACTION; | |
| 717 | + } | |
| 718 | + $i += $iLookAhead; | |
| 719 | + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || | |
| 720 | + self::$yy_lookahead[$i] != $iLookAhead) { | |
| 721 | + if (count(self::$yyFallback) && $iLookAhead < count(self::$yyFallback) | |
| 722 | + && ($iFallback = self::$yyFallback[$iLookAhead]) != 0) { | |
| 723 | + if (self::$yyTraceFILE) { | |
| 724 | + fwrite(self::$yyTraceFILE, self::$yyTracePrompt . "FALLBACK " . | |
| 725 | + self::$yyTokenName[$iLookAhead] . " => " . | |
| 726 | + self::$yyTokenName[$iFallback] . "\n"); | |
| 727 | + } | |
| 728 | + return $this->yy_find_shift_action($iFallback); | |
| 729 | + } | |
| 730 | + return self::$yy_default[$stateno]; | |
| 731 | + } else { | |
| 732 | + return self::$yy_action[$i]; | |
| 733 | + } | |
| 734 | + } | |
| 735 | + | |
| 736 | + /** | |
| 737 | + * Find the appropriate action for a parser given the non-terminal | |
| 738 | + * look-ahead token $iLookAhead. | |
| 739 | + * | |
| 740 | + * If the look-ahead token is self::YYNOCODE, then check to see if the action is | |
| 741 | + * independent of the look-ahead. If it is, return the action, otherwise | |
| 742 | + * return self::YY_NO_ACTION. | |
| 743 | + * @param int Current state number | |
| 744 | + * @param int The look-ahead token | |
| 745 | + */ | |
| 746 | + function yy_find_reduce_action($stateno, $iLookAhead) | |
| 747 | + { | |
| 748 | + /* $stateno = $this->yystack[$this->yyidx]->stateno; */ | |
| 749 | + | |
| 750 | + if (!isset(self::$yy_reduce_ofst[$stateno])) { | |
| 751 | + return self::$yy_default[$stateno]; | |
| 752 | + } | |
| 753 | + $i = self::$yy_reduce_ofst[$stateno]; | |
| 754 | + if ($i == self::YY_REDUCE_USE_DFLT) { | |
| 755 | + return self::$yy_default[$stateno]; | |
| 756 | + } | |
| 757 | + if ($iLookAhead == self::YYNOCODE) { | |
| 758 | + return self::YY_NO_ACTION; | |
| 759 | + } | |
| 760 | + $i += $iLookAhead; | |
| 761 | + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || | |
| 762 | + self::$yy_lookahead[$i] != $iLookAhead) { | |
| 763 | + return self::$yy_default[$stateno]; | |
| 764 | + } else { | |
| 765 | + return self::$yy_action[$i]; | |
| 766 | + } | |
| 767 | + } | |
| 768 | + | |
| 769 | + /** | |
| 770 | + * Perform a shift action. | |
| 771 | + * @param int The new state to shift in | |
| 772 | + * @param int The major token to shift in | |
| 773 | + * @param mixed the minor token to shift in | |
| 774 | + */ | |
| 775 | + function yy_shift($yyNewState, $yyMajor, $yypMinor) | |
| 776 | + { | |
| 777 | + $this->yyidx++; | |
| 778 | + if ($this->yyidx >= self::YYSTACKDEPTH) { | |
| 779 | + $this->yyidx--; | |
| 780 | + if (self::$yyTraceFILE) { | |
| 781 | + fprintf(self::$yyTraceFILE, "%sStack Overflow!\n", self::$yyTracePrompt); | |
| 782 | + } | |
| 783 | + while ($this->yyidx >= 0) { | |
| 784 | + $this->yy_pop_parser_stack(); | |
| 785 | + } | |
| 786 | + /* Here code is inserted which will execute if the parser | |
| 787 | + ** stack ever overflows */ | |
| 788 | + return; | |
| 789 | + } | |
| 790 | + $yytos = new SearchCommandParseryyStackEntry; | |
| 791 | + $yytos->stateno = $yyNewState; | |
| 792 | + $yytos->major = $yyMajor; | |
| 793 | + $yytos->minor = $yypMinor; | |
| 794 | + array_push($this->yystack, $yytos); | |
| 795 | + if (self::$yyTraceFILE && $this->yyidx > 0) { | |
| 796 | + fprintf(self::$yyTraceFILE, "%sShift %d\n", self::$yyTracePrompt, | |
| 797 | + $yyNewState); | |
| 798 | + fprintf(self::$yyTraceFILE, "%sStack:", self::$yyTracePrompt); | |
| 799 | + for($i = 1; $i <= $this->yyidx; $i++) { | |
| 800 | + fprintf(self::$yyTraceFILE, " %s", | |
| 801 | + self::$yyTokenName[$this->yystack[$i]->major]); | |
| 802 | + } | |
| 803 | + fwrite(self::$yyTraceFILE,"\n"); | |
| 804 | + } | |
| 805 | + } | |
| 806 | + | |
| 807 | + /** | |
| 808 | + * The following table contains information about every rule that | |
| 809 | + * is used during the reduce. | |
| 810 | + * | |
| 811 | + * <pre> | |
| 812 | + * array( | |
| 813 | + * array( | |
| 814 | + * int $lhs; Symbol on the left-hand side of the rule | |
| 815 | + * int $nrhs; Number of right-hand side symbols in the rule | |
| 816 | + * ),... | |
| 817 | + * ); | |
| 818 | + * </pre> | |
| 819 | + */ | |
| 820 | + static public $yyRuleInfo = array( | |
| 821 | + array( 'lhs' => 28, 'rhs' => 1 ), | |
| 822 | + array( 'lhs' => 27, 'rhs' => 3 ), | |
| 823 | + array( 'lhs' => 27, 'rhs' => 3 ), | |
| 824 | + array( 'lhs' => 27, 'rhs' => 2 ), | |
| 825 | + array( 'lhs' => 27, 'rhs' => 3 ), | |
| 826 | + array( 'lhs' => 27, 'rhs' => 3 ), | |
| 827 | + array( 'lhs' => 27, 'rhs' => 6 ), | |
| 828 | + array( 'lhs' => 27, 'rhs' => 4 ), | |
| 829 | + array( 'lhs' => 27, 'rhs' => 4 ), | |
| 830 | + array( 'lhs' => 27, 'rhs' => 5 ), | |
| 831 | + array( 'lhs' => 27, 'rhs' => 3 ), | |
| 832 | + array( 'lhs' => 32, 'rhs' => 0 ), | |
| 833 | + array( 'lhs' => 32, 'rhs' => 1 ), | |
| 834 | + array( 'lhs' => 29, 'rhs' => 6 ), | |
| 835 | + array( 'lhs' => 29, 'rhs' => 1 ), | |
| 836 | + array( 'lhs' => 31, 'rhs' => 1 ), | |
| 837 | + array( 'lhs' => 31, 'rhs' => 3 ), | |
| 838 | + array( 'lhs' => 33, 'rhs' => 3 ), | |
| 839 | + array( 'lhs' => 33, 'rhs' => 1 ), | |
| 840 | + array( 'lhs' => 30, 'rhs' => 1 ), | |
| 841 | + array( 'lhs' => 30, 'rhs' => 1 ), | |
| 842 | + array( 'lhs' => 30, 'rhs' => 1 ), | |
| 843 | + array( 'lhs' => 30, 'rhs' => 1 ), | |
| 844 | + array( 'lhs' => 30, 'rhs' => 1 ), | |
| 845 | + array( 'lhs' => 30, 'rhs' => 2 ), | |
| 846 | + array( 'lhs' => 30, 'rhs' => 2 ), | |
| 847 | + array( 'lhs' => 30, 'rhs' => 1 ), | |
| 848 | + ); | |
| 849 | + | |
| 850 | + /** | |
| 851 | + * The following table contains a mapping of reduce action to method name | |
| 852 | + * that handles the reduction. | |
| 853 | + * | |
| 854 | + * If a rule is not set, it has no handler. | |
| 855 | + */ | |
| 856 | + static public $yyReduceMap = array( | |
| 857 | + 0 => 0, | |
| 858 | + 1 => 1, | |
| 859 | + 2 => 2, | |
| 860 | + 3 => 3, | |
| 861 | + 4 => 4, | |
| 862 | + 16 => 4, | |
| 863 | + 5 => 5, | |
| 864 | + 6 => 6, | |
| 865 | + 7 => 7, | |
| 866 | + 8 => 8, | |
| 867 | + 9 => 9, | |
| 868 | + 10 => 10, | |
| 869 | + 11 => 11, | |
| 870 | + 12 => 12, | |
| 871 | + 13 => 13, | |
| 872 | + 14 => 14, | |
| 873 | + 15 => 15, | |
| 874 | + 17 => 17, | |
| 875 | + 18 => 18, | |
| 876 | + 19 => 19, | |
| 877 | + 20 => 20, | |
| 878 | + 21 => 21, | |
| 879 | + 22 => 22, | |
| 880 | + 23 => 23, | |
| 881 | + 24 => 24, | |
| 882 | + 25 => 25, | |
| 883 | + 26 => 26, | |
| 884 | + ); | |
| 885 | + /* Beginning here are the reduction cases. A typical example | |
| 886 | + ** follows: | |
| 887 | + ** #line <lineno> <grammarfile> | |
| 888 | + ** function yy_r0($yymsp){ ... } // User supplied code | |
| 889 | + ** #line <lineno> <thisfile> | |
| 890 | + */ | |
| 891 | +#line 53 "SearchCommandParser.y" | |
| 892 | + function yy_r0(){ | |
| 893 | + $this->expr_result = $this->yystack[$this->yyidx + 0]->minor; | |
| 894 | + } | |
| 895 | +#line 900 "SearchCommandParser.php" | |
| 896 | +#line 58 "SearchCommandParser.y" | |
| 897 | + function yy_r1(){ | |
| 898 | + $this->_retvalue = new OpExpr($this->yystack[$this->yyidx + -2]->minor, ExprOp::OP_AND, $this->yystack[$this->yyidx + 0]->minor); | |
| 899 | + } | |
| 900 | +#line 905 "SearchCommandParser.php" | |
| 901 | +#line 63 "SearchCommandParser.y" | |
| 902 | + function yy_r2(){ | |
| 903 | + $this->_retvalue = new OpExpr($this->yystack[$this->yyidx + -2]->minor, ExprOp::OP_OR, $this->yystack[$this->yyidx + 0]->minor); | |
| 904 | + } | |
| 905 | +#line 910 "SearchCommandParser.php" | |
| 906 | +#line 68 "SearchCommandParser.y" | |
| 907 | + function yy_r3(){ | |
| 908 | + $expr = $this->yystack[$this->yyidx + 0]->minor; | |
| 909 | + $expr->not(!$expr->not()); | |
| 910 | + $this->_retvalue = $expr; | |
| 911 | + } | |
| 912 | +#line 917 "SearchCommandParser.php" | |
| 913 | +#line 75 "SearchCommandParser.y" | |
| 914 | + function yy_r4(){ | |
| 915 | + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; | |
| 916 | + } | |
| 917 | +#line 922 "SearchCommandParser.php" | |
| 918 | +#line 80 "SearchCommandParser.y" | |
| 919 | + function yy_r5(){ | |
| 920 | + $op = $this->yystack[$this->yyidx + -1]->minor; | |
| 921 | + $not = false; | |
| 922 | + if ($op == ExprOp::IS_NOT) | |
| 923 | + { | |
| 924 | + $op = ExprOp::IS; | |
| 925 | + $not = true; | |
| 926 | + } | |
| 927 | + | |
| 928 | + $fld = new OpExpr($this->yystack[$this->yyidx + -2]->minor, $op, $this->yystack[$this->yyidx + 0]->minor); | |
| 929 | + $fld->not($not); | |
| 930 | + $this->_retvalue = $fld; | |
| 931 | + } | |
| 932 | +#line 937 "SearchCommandParser.php" | |
| 933 | +#line 95 "SearchCommandParser.y" | |
| 934 | + function yy_r6(){ | |
| 935 | + $expr = new OpExpr($this->yystack[$this->yyidx + -5]->minor, ExprOp::BETWEEN, new BetweenValueExpr($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor)); | |
| 936 | + $expr->not($this->yystack[$this->yyidx + -4]->minor); | |
| 937 | + $this->_retvalue=$expr; | |
| 938 | + } | |
| 939 | +#line 944 "SearchCommandParser.php" | |
| 940 | +#line 102 "SearchCommandParser.y" | |
| 941 | + function yy_r7(){ | |
| 942 | + $expr = new OpExpr($this->yystack[$this->yyidx + -3]->minor, ExprOp::LIKE, $this->yystack[$this->yyidx + 0]->minor); | |
| 943 | + $expr->not($this->yystack[$this->yyidx + -2]->minor); | |
| 944 | + $this->_retvalue=$expr; | |
| 945 | + } | |
| 946 | +#line 951 "SearchCommandParser.php" | |
| 947 | +#line 109 "SearchCommandParser.y" | |
| 948 | + function yy_r8(){ | |
| 949 | + $expr = new OpExpr($this->yystack[$this->yyidx + -3]->minor, ExprOp::IS, $this->yystack[$this->yyidx + 0]->minor); | |
| 950 | + $expr->not($this->yystack[$this->yyidx + -1]->minor); | |
| 951 | + $this->_retvalue=$expr; | |
| 952 | + } | |
| 953 | +#line 958 "SearchCommandParser.php" | |
| 954 | +#line 116 "SearchCommandParser.y" | |
| 955 | + function yy_r9(){ | |
| 956 | + $expr = new OpExpr($this->yystack[$this->yyidx + -4]->minor, ExprOp::CONTAINS, $this->yystack[$this->yyidx + 0]->minor); | |
| 957 | + $expr->not($this->yystack[$this->yyidx + -2]->minor); | |
| 958 | + $this->_retvalue=$expr; | |
| 959 | + } | |
| 960 | +#line 965 "SearchCommandParser.php" | |
| 961 | +#line 123 "SearchCommandParser.y" | |
| 962 | + function yy_r10(){ | |
| 963 | + $this->_retvalue = new OpExpr($this->yystack[$this->yyidx + -2]->minor, ExprOp::CONTAINS, $this->yystack[$this->yyidx + 0]->minor); | |
| 964 | + } | |
| 965 | +#line 970 "SearchCommandParser.php" | |
| 966 | +#line 129 "SearchCommandParser.y" | |
| 967 | + function yy_r11(){ | |
| 968 | + $this->_retvalue = false; | |
| 969 | + } | |
| 970 | +#line 975 "SearchCommandParser.php" | |
| 971 | +#line 134 "SearchCommandParser.y" | |
| 972 | + function yy_r12(){ | |
| 973 | + $this->_retvalue = true; | |
| 974 | + } | |
| 975 | +#line 980 "SearchCommandParser.php" | |
| 976 | +#line 139 "SearchCommandParser.y" | |
| 977 | + function yy_r13(){ | |
| 978 | + $registry = ExprFieldRegistry::getRegistry(); | |
| 979 | + $field = $registry->resolveMetadataField($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -1]->minor); | |
| 980 | + $this->_retvalue = $field; | |
| 981 | + } | |
| 982 | +#line 987 "SearchCommandParser.php" | |
| 983 | +#line 146 "SearchCommandParser.y" | |
| 984 | + function yy_r14(){ | |
| 985 | + $registry = ExprFieldRegistry::getRegistry(); | |
| 986 | + $field=$registry->resolveAlias($this->yystack[$this->yyidx + 0]->minor); | |
| 987 | + $this->_retvalue = $field; | |
| 988 | + } | |
| 989 | +#line 994 "SearchCommandParser.php" | |
| 990 | +#line 153 "SearchCommandParser.y" | |
| 991 | + function yy_r15(){ | |
| 992 | + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; | |
| 993 | + } | |
| 994 | +#line 999 "SearchCommandParser.php" | |
| 995 | +#line 163 "SearchCommandParser.y" | |
| 996 | + function yy_r17(){ | |
| 997 | + $this->yystack[$this->yyidx + 0]->minor->addValue($this->yystack[$this->yyidx + -2]->minor); | |
| 998 | + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; | |
| 999 | + } | |
| 1000 | +#line 1005 "SearchCommandParser.php" | |
| 1001 | +#line 169 "SearchCommandParser.y" | |
| 1002 | + function yy_r18(){ | |
| 1003 | + $this->_retvalue = new ValueListExpr($this->yystack[$this->yyidx + 0]->minor); | |
| 1004 | + } | |
| 1005 | +#line 1010 "SearchCommandParser.php" | |
| 1006 | +#line 174 "SearchCommandParser.y" | |
| 1007 | + function yy_r19(){ | |
| 1008 | + $this->_retvalue = ExprOp::CONTAINS; | |
| 1009 | + } | |
| 1010 | +#line 1015 "SearchCommandParser.php" | |
| 1011 | +#line 179 "SearchCommandParser.y" | |
| 1012 | + function yy_r20(){ | |
| 1013 | + $this->_retvalue = ExprOp::LESS_THAN; | |
| 1014 | + } | |
| 1015 | +#line 1020 "SearchCommandParser.php" | |
| 1016 | +#line 184 "SearchCommandParser.y" | |
| 1017 | + function yy_r21(){ | |
| 1018 | + $this->_retvalue = ExprOp::GREATER_THAN; | |
| 1019 | + } | |
| 1020 | +#line 1025 "SearchCommandParser.php" | |
| 1021 | +#line 189 "SearchCommandParser.y" | |
| 1022 | + function yy_r22(){ | |
| 1023 | + $this->_retvalue = ExprOp::LESS_THAN_EQUAL; | |
| 1024 | + } | |
| 1025 | +#line 1030 "SearchCommandParser.php" | |
| 1026 | +#line 194 "SearchCommandParser.y" | |
| 1027 | + function yy_r23(){ | |
| 1028 | + $this->_retvalue = ExprOp::GREATER_THAN_EQUAL; | |
| 1029 | + } | |
| 1030 | +#line 1035 "SearchCommandParser.php" | |
| 1031 | +#line 199 "SearchCommandParser.y" | |
| 1032 | + function yy_r24(){ | |
| 1033 | + $this->_retvalue = ExprOp::STARTS_WITH; | |
| 1034 | + } | |
| 1035 | +#line 1040 "SearchCommandParser.php" | |
| 1036 | +#line 204 "SearchCommandParser.y" | |
| 1037 | + function yy_r25(){ | |
| 1038 | + $this->_retvalue = ExprOp::ENDS_WITH; | |
| 1039 | + } | |
| 1040 | +#line 1045 "SearchCommandParser.php" | |
| 1041 | +#line 209 "SearchCommandParser.y" | |
| 1042 | + function yy_r26(){ | |
| 1043 | + $this->_retvalue = ExprOp::IS_NOT; | |
| 1044 | + } | |
| 1045 | +#line 1050 "SearchCommandParser.php" | |
| 1046 | + | |
| 1047 | + /** | |
| 1048 | + * placeholder for the left hand side in a reduce operation. | |
| 1049 | + * | |
| 1050 | + * For a parser with a rule like this: | |
| 1051 | + * <pre> | |
| 1052 | + * rule(A) ::= B. { A = 1; } | |
| 1053 | + * </pre> | |
| 1054 | + * | |
| 1055 | + * The parser will translate to something like: | |
| 1056 | + * | |
| 1057 | + * <code> | |
| 1058 | + * function yy_r0(){$this->_retvalue = 1;} | |
| 1059 | + * </code> | |
| 1060 | + */ | |
| 1061 | + private $_retvalue; | |
| 1062 | + | |
| 1063 | + /** | |
| 1064 | + * Perform a reduce action and the shift that must immediately | |
| 1065 | + * follow the reduce. | |
| 1066 | + * | |
| 1067 | + * For a rule such as: | |
| 1068 | + * | |
| 1069 | + * <pre> | |
| 1070 | + * A ::= B blah C. { dosomething(); } | |
| 1071 | + * </pre> | |
| 1072 | + * | |
| 1073 | + * This function will first call the action, if any, ("dosomething();" in our | |
| 1074 | + * example), and then it will pop three states from the stack, | |
| 1075 | + * one for each entry on the right-hand side of the expression | |
| 1076 | + * (B, blah, and C in our example rule), and then push the result of the action | |
| 1077 | + * back on to the stack with the resulting state reduced to (as described in the .out | |
| 1078 | + * file) | |
| 1079 | + * @param int Number of the rule by which to reduce | |
| 1080 | + */ | |
| 1081 | + function yy_reduce($yyruleno) | |
| 1082 | + { | |
| 1083 | + //int $yygoto; /* The next state */ | |
| 1084 | + //int $yyact; /* The next action */ | |
| 1085 | + //mixed $yygotominor; /* The LHS of the rule reduced */ | |
| 1086 | + //SearchCommandParseryyStackEntry $yymsp; /* The top of the parser's stack */ | |
| 1087 | + //int $yysize; /* Amount to pop the stack */ | |
| 1088 | + $yymsp = $this->yystack[$this->yyidx]; | |
| 1089 | + if (self::$yyTraceFILE && $yyruleno >= 0 | |
| 1090 | + && $yyruleno < count(self::$yyRuleName)) { | |
| 1091 | + fprintf(self::$yyTraceFILE, "%sReduce (%d) [%s].\n", | |
| 1092 | + self::$yyTracePrompt, $yyruleno, | |
| 1093 | + self::$yyRuleName[$yyruleno]); | |
| 1094 | + } | |
| 1095 | + | |
| 1096 | + $this->_retvalue = $yy_lefthand_side = null; | |
| 1097 | + if (array_key_exists($yyruleno, self::$yyReduceMap)) { | |
| 1098 | + // call the action | |
| 1099 | + $this->_retvalue = null; | |
| 1100 | + $this->{'yy_r' . self::$yyReduceMap[$yyruleno]}(); | |
| 1101 | + $yy_lefthand_side = $this->_retvalue; | |
| 1102 | + } | |
| 1103 | + $yygoto = self::$yyRuleInfo[$yyruleno]['lhs']; | |
| 1104 | + $yysize = self::$yyRuleInfo[$yyruleno]['rhs']; | |
| 1105 | + $this->yyidx -= $yysize; | |
| 1106 | + for($i = $yysize; $i; $i--) { | |
| 1107 | + // pop all of the right-hand side parameters | |
| 1108 | + array_pop($this->yystack); | |
| 1109 | + } | |
| 1110 | + $yyact = $this->yy_find_reduce_action($this->yystack[$this->yyidx]->stateno, $yygoto); | |
| 1111 | + if ($yyact < self::YYNSTATE) { | |
| 1112 | + /* If we are not debugging and the reduce action popped at least | |
| 1113 | + ** one element off the stack, then we can push the new element back | |
| 1114 | + ** onto the stack here, and skip the stack overflow test in yy_shift(). | |
| 1115 | + ** That gives a significant speed improvement. */ | |
| 1116 | + if (!self::$yyTraceFILE && $yysize) { | |
| 1117 | + $this->yyidx++; | |
| 1118 | + $x = new SearchCommandParseryyStackEntry; | |
| 1119 | + $x->stateno = $yyact; | |
| 1120 | + $x->major = $yygoto; | |
| 1121 | + $x->minor = $yy_lefthand_side; | |
| 1122 | + $this->yystack[$this->yyidx] = $x; | |
| 1123 | + } else { | |
| 1124 | + $this->yy_shift($yyact, $yygoto, $yy_lefthand_side); | |
| 1125 | + } | |
| 1126 | + } elseif ($yyact == self::YYNSTATE + self::YYNRULE + 1) { | |
| 1127 | + $this->yy_accept(); | |
| 1128 | + } | |
| 1129 | + } | |
| 1130 | + | |
| 1131 | + /** | |
| 1132 | + * The following code executes when the parse fails | |
| 1133 | + * | |
| 1134 | + * Code from %parse_fail is inserted here | |
| 1135 | + */ | |
| 1136 | + function yy_parse_failed() | |
| 1137 | + { | |
| 1138 | + if (self::$yyTraceFILE) { | |
| 1139 | + fprintf(self::$yyTraceFILE, "%sFail!\n", self::$yyTracePrompt); | |
| 1140 | + } | |
| 1141 | + while ($this->yyidx >= 0) { | |
| 1142 | + $this->yy_pop_parser_stack(); | |
| 1143 | + } | |
| 1144 | + /* Here code is inserted which will be executed whenever the | |
| 1145 | + ** parser fails */ | |
| 1146 | +#line 46 "SearchCommandParser.y" | |
| 1147 | + | |
| 1148 | + $this->parse_result = 'syntax'; | |
| 1149 | +#line 1155 "SearchCommandParser.php" | |
| 1150 | + } | |
| 1151 | + | |
| 1152 | + /** | |
| 1153 | + * The following code executes when a syntax error first occurs. | |
| 1154 | + * | |
| 1155 | + * %syntax_error code is inserted here | |
| 1156 | + * @param int The major type of the error token | |
| 1157 | + * @param mixed The minor type of the error token | |
| 1158 | + */ | |
| 1159 | + function yy_syntax_error($yymajor, $TOKEN) | |
| 1160 | + { | |
| 1161 | +#line 35 "SearchCommandParser.y" | |
| 1162 | + | |
| 1163 | + $this->parse_result = 'syntax'; | |
| 1164 | + $this->parse_message = ""; | |
| 1165 | +#line 1172 "SearchCommandParser.php" | |
| 1166 | + } | |
| 1167 | + | |
| 1168 | + /** | |
| 1169 | + * The following is executed when the parser accepts | |
| 1170 | + * | |
| 1171 | + * %parse_accept code is inserted here | |
| 1172 | + */ | |
| 1173 | + function yy_accept() | |
| 1174 | + { | |
| 1175 | + if (self::$yyTraceFILE) { | |
| 1176 | + fprintf(self::$yyTraceFILE, "%sAccept!\n", self::$yyTracePrompt); | |
| 1177 | + } | |
| 1178 | + while ($this->yyidx >= 0) { | |
| 1179 | + $stack = $this->yy_pop_parser_stack(); | |
| 1180 | + } | |
| 1181 | + /* Here code is inserted which will be executed whenever the | |
| 1182 | + ** parser accepts */ | |
| 1183 | +#line 41 "SearchCommandParser.y" | |
| 1184 | + | |
| 1185 | + $this->parse_result = 'ok'; | |
| 1186 | +#line 1194 "SearchCommandParser.php" | |
| 1187 | + } | |
| 1188 | + | |
| 1189 | + /** | |
| 1190 | + * The main parser program. | |
| 1191 | + * | |
| 1192 | + * The first argument is the major token number. The second is | |
| 1193 | + * the token value string as scanned from the input. | |
| 1194 | + * | |
| 1195 | + * @param int the token number | |
| 1196 | + * @param mixed the token value | |
| 1197 | + * @param mixed any extra arguments that should be passed to handlers | |
| 1198 | + */ | |
| 1199 | + function doParse($yymajor, $yytokenvalue) | |
| 1200 | + { | |
| 1201 | +// $yyact; /* The parser action. */ | |
| 1202 | +// $yyendofinput; /* True if we are at the end of input */ | |
| 1203 | + $yyerrorhit = 0; /* True if yymajor has invoked an error */ | |
| 1204 | + | |
| 1205 | + /* (re)initialize the parser, if necessary */ | |
| 1206 | + if ($this->yyidx === null || $this->yyidx < 0) { | |
| 1207 | + /* if ($yymajor == 0) return; // not sure why this was here... */ | |
| 1208 | + $this->yyidx = 0; | |
| 1209 | + $this->yyerrcnt = -1; | |
| 1210 | + $x = new SearchCommandParseryyStackEntry; | |
| 1211 | + $x->stateno = 0; | |
| 1212 | + $x->major = 0; | |
| 1213 | + $this->yystack = array(); | |
| 1214 | + array_push($this->yystack, $x); | |
| 1215 | + } | |
| 1216 | + $yyendofinput = ($yymajor==0); | |
| 1217 | + | |
| 1218 | + if (self::$yyTraceFILE) { | |
| 1219 | + fprintf(self::$yyTraceFILE, "%sInput %s\n", | |
| 1220 | + self::$yyTracePrompt, self::$yyTokenName[$yymajor]); | |
| 1221 | + } | |
| 1222 | + | |
| 1223 | + do { | |
| 1224 | + $yyact = $this->yy_find_shift_action($yymajor); | |
| 1225 | + if ($yymajor < self::YYERRORSYMBOL && | |
| 1226 | + !$this->yy_is_expected_token($yymajor)) { | |
| 1227 | + // force a syntax error | |
| 1228 | + $yyact = self::YY_ERROR_ACTION; | |
| 1229 | + } | |
| 1230 | + if ($yyact < self::YYNSTATE) { | |
| 1231 | + $this->yy_shift($yyact, $yymajor, $yytokenvalue); | |
| 1232 | + $this->yyerrcnt--; | |
| 1233 | + if ($yyendofinput && $this->yyidx >= 0) { | |
| 1234 | + $yymajor = 0; | |
| 1235 | + } else { | |
| 1236 | + $yymajor = self::YYNOCODE; | |
| 1237 | + } | |
| 1238 | + } elseif ($yyact < self::YYNSTATE + self::YYNRULE) { | |
| 1239 | + $this->yy_reduce($yyact - self::YYNSTATE); | |
| 1240 | + } elseif ($yyact == self::YY_ERROR_ACTION) { | |
| 1241 | + if (self::$yyTraceFILE) { | |
| 1242 | + fprintf(self::$yyTraceFILE, "%sSyntax Error!\n", | |
| 1243 | + self::$yyTracePrompt); | |
| 1244 | + } | |
| 1245 | + if (self::YYERRORSYMBOL) { | |
| 1246 | + /* A syntax error has occurred. | |
| 1247 | + ** The response to an error depends upon whether or not the | |
| 1248 | + ** grammar defines an error token "ERROR". | |
| 1249 | + ** | |
| 1250 | + ** This is what we do if the grammar does define ERROR: | |
| 1251 | + ** | |
| 1252 | + ** * Call the %syntax_error function. | |
| 1253 | + ** | |
| 1254 | + ** * Begin popping the stack until we enter a state where | |
| 1255 | + ** it is legal to shift the error symbol, then shift | |
| 1256 | + ** the error symbol. | |
| 1257 | + ** | |
| 1258 | + ** * Set the error count to three. | |
| 1259 | + ** | |
| 1260 | + ** * Begin accepting and shifting new tokens. No new error | |
| 1261 | + ** processing will occur until three tokens have been | |
| 1262 | + ** shifted successfully. | |
| 1263 | + ** | |
| 1264 | + */ | |
| 1265 | + if ($this->yyerrcnt < 0) { | |
| 1266 | + $this->yy_syntax_error($yymajor, $yytokenvalue); | |
| 1267 | + } | |
| 1268 | + $yymx = $this->yystack[$this->yyidx]->major; | |
| 1269 | + if ($yymx == self::YYERRORSYMBOL || $yyerrorhit ){ | |
| 1270 | + if (self::$yyTraceFILE) { | |
| 1271 | + fprintf(self::$yyTraceFILE, "%sDiscard input token %s\n", | |
| 1272 | + self::$yyTracePrompt, self::$yyTokenName[$yymajor]); | |
| 1273 | + } | |
| 1274 | + $this->yy_destructor($yymajor, $yytokenvalue); | |
| 1275 | + $yymajor = self::YYNOCODE; | |
| 1276 | + } else { | |
| 1277 | + while ($this->yyidx >= 0 && | |
| 1278 | + $yymx != self::YYERRORSYMBOL && | |
| 1279 | + ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE | |
| 1280 | + ){ | |
| 1281 | + $this->yy_pop_parser_stack(); | |
| 1282 | + } | |
| 1283 | + if ($this->yyidx < 0 || $yymajor==0) { | |
| 1284 | + $this->yy_destructor($yymajor, $yytokenvalue); | |
| 1285 | + $this->yy_parse_failed(); | |
| 1286 | + $yymajor = self::YYNOCODE; | |
| 1287 | + } elseif ($yymx != self::YYERRORSYMBOL) { | |
| 1288 | + $u2 = 0; | |
| 1289 | + $this->yy_shift($yyact, self::YYERRORSYMBOL, $u2); | |
| 1290 | + } | |
| 1291 | + } | |
| 1292 | + $this->yyerrcnt = 3; | |
| 1293 | + $yyerrorhit = 1; | |
| 1294 | + } else { | |
| 1295 | + /* YYERRORSYMBOL is not defined */ | |
| 1296 | + /* This is what we do if the grammar does not define ERROR: | |
| 1297 | + ** | |
| 1298 | + ** * Report an error message, and throw away the input token. | |
| 1299 | + ** | |
| 1300 | + ** * If the input token is $, then fail the parse. | |
| 1301 | + ** | |
| 1302 | + ** As before, subsequent error messages are suppressed until | |
| 1303 | + ** three input tokens have been successfully shifted. | |
| 1304 | + */ | |
| 1305 | + if ($this->yyerrcnt <= 0) { | |
| 1306 | + $this->yy_syntax_error($yymajor, $yytokenvalue); | |
| 1307 | + } | |
| 1308 | + $this->yyerrcnt = 3; | |
| 1309 | + $this->yy_destructor($yymajor, $yytokenvalue); | |
| 1310 | + if ($yyendofinput) { | |
| 1311 | + $this->yy_parse_failed(); | |
| 1312 | + } | |
| 1313 | + $yymajor = self::YYNOCODE; | |
| 1314 | + } | |
| 1315 | + } else { | |
| 1316 | + $this->yy_accept(); | |
| 1317 | + $yymajor = self::YYNOCODE; | |
| 1318 | + } | |
| 1319 | + } while ($yymajor != self::YYNOCODE && $this->yyidx >= 0); | |
| 1320 | + } | |
| 1321 | +} | |
| 0 | 1322 | \ No newline at end of file | ... | ... |
search2/search/SearchCommandParser.y
0 → 100755
| 1 | +%name SearchCommandParser | |
| 2 | +%declare_class {class SearchCommandParser} | |
| 3 | + | |
| 4 | +%include_class { | |
| 5 | + | |
| 6 | + private $expr_result; | |
| 7 | + private $parse_result; | |
| 8 | + | |
| 9 | + public function __construct() | |
| 10 | + { | |
| 11 | + $this->parse_result = 'ok'; | |
| 12 | + } | |
| 13 | + | |
| 14 | + public function getExprResult() | |
| 15 | + { | |
| 16 | + return $this->expr_result; | |
| 17 | + } | |
| 18 | + | |
| 19 | + public function isExprOk() | |
| 20 | + { | |
| 21 | + return $this->parse_result == 'ok'; | |
| 22 | + } | |
| 23 | + | |
| 24 | +} | |
| 25 | + | |
| 26 | +%type expr {Expr} | |
| 27 | + | |
| 28 | +%left OPOR. | |
| 29 | +%left OPAND. | |
| 30 | +%right NOT. | |
| 31 | +%left IS CONTAIN LIKE BETWEEN START END. | |
| 32 | +%left GT LE LT GE. | |
| 33 | + | |
| 34 | +%syntax_error | |
| 35 | +{ | |
| 36 | + $this->parse_result = 'syntax'; | |
| 37 | + $this->parse_message = ""; | |
| 38 | +} | |
| 39 | + | |
| 40 | +%parse_accept | |
| 41 | +{ | |
| 42 | + $this->parse_result = 'ok'; | |
| 43 | +} | |
| 44 | + | |
| 45 | +%parse_failure | |
| 46 | +{ | |
| 47 | + $this->parse_result = 'syntax'; | |
| 48 | +} | |
| 49 | + | |
| 50 | +%start_symbol cmdline | |
| 51 | + | |
| 52 | +cmdline ::= expr(A). | |
| 53 | +{ | |
| 54 | + $this->expr_result = A; | |
| 55 | +} | |
| 56 | + | |
| 57 | +expr(A) ::= expr(B) OPAND expr(C). | |
| 58 | +{ | |
| 59 | + A = new OpExpr(B, ExprOp::OP_AND, C); | |
| 60 | +} | |
| 61 | + | |
| 62 | +expr(A) ::= expr(B) OPOR expr(C). | |
| 63 | +{ | |
| 64 | + A = new OpExpr(B, ExprOp::OP_OR, C); | |
| 65 | +} | |
| 66 | + | |
| 67 | +expr(A) ::= NOT expr(B). | |
| 68 | +{ | |
| 69 | + $expr = B; | |
| 70 | + $expr->not(!$expr->not()); | |
| 71 | + A = $expr; | |
| 72 | +} | |
| 73 | + | |
| 74 | +expr(A) ::= PAR_OPEN expr(B) PAR_CLOSE. | |
| 75 | +{ | |
| 76 | + A = B; | |
| 77 | +} | |
| 78 | + | |
| 79 | +expr(A) ::= terminal(B) operator(C) value(D). | |
| 80 | +{ | |
| 81 | + $op = C; | |
| 82 | + $not = false; | |
| 83 | + if ($op == ExprOp::IS_NOT) | |
| 84 | + { | |
| 85 | + $op = ExprOp::IS; | |
| 86 | + $not = true; | |
| 87 | + } | |
| 88 | + | |
| 89 | + $fld = new OpExpr(B, $op, D); | |
| 90 | + $fld->not($not); | |
| 91 | + A = $fld; | |
| 92 | +} | |
| 93 | + | |
| 94 | +expr(A) ::= terminal(B) notop(C) BETWEEN value(D) OPAND value(E). [BETWEEN] | |
| 95 | +{ | |
| 96 | + $expr = new OpExpr(B, ExprOp::BETWEEN, new BetweenValueExpr(D, E)); | |
| 97 | + $expr->not(C); | |
| 98 | + A=$expr; | |
| 99 | +} | |
| 100 | + | |
| 101 | +expr(A) ::= terminal(B) notop(C) LIKE value(D). | |
| 102 | +{ | |
| 103 | + $expr = new OpExpr(B, ExprOp::LIKE, D); | |
| 104 | + $expr->not(C); | |
| 105 | + A=$expr; | |
| 106 | +} | |
| 107 | + | |
| 108 | +expr(A) ::= terminal(B) IS notop(C) value(D). | |
| 109 | +{ | |
| 110 | + $expr = new OpExpr(B, ExprOp::IS, D); | |
| 111 | + $expr->not(C); | |
| 112 | + A=$expr; | |
| 113 | +} | |
| 114 | + | |
| 115 | +expr(A) ::= terminal(B) DOES notop(C) CONTAIN value(D). | |
| 116 | +{ | |
| 117 | + $expr = new OpExpr(B, ExprOp::CONTAINS, D); | |
| 118 | + $expr->not(C); | |
| 119 | + A=$expr; | |
| 120 | +} | |
| 121 | + | |
| 122 | +expr(A) ::= terminal(B) COLON value(C). | |
| 123 | +{ | |
| 124 | + A = new OpExpr(B, ExprOp::CONTAINS, C); | |
| 125 | +} | |
| 126 | + | |
| 127 | + | |
| 128 | +notop(A) ::= . | |
| 129 | +{ | |
| 130 | + A = false; | |
| 131 | +} | |
| 132 | + | |
| 133 | +notop(A) ::= NOT. | |
| 134 | +{ | |
| 135 | + A = true; | |
| 136 | +} | |
| 137 | + | |
| 138 | +terminal(A) ::= SQUARE_OPEN value(B) SQUARE_CLOSE SQUARE_OPEN value(C) SQUARE_CLOSE. | |
| 139 | +{ | |
| 140 | + $registry = ExprFieldRegistry::getRegistry(); | |
| 141 | + $field = $registry->resolveMetadataField(B, C); | |
| 142 | + A = $field; | |
| 143 | +} | |
| 144 | + | |
| 145 | +terminal(A) ::= TERMINAL(B). | |
| 146 | +{ | |
| 147 | + $registry = ExprFieldRegistry::getRegistry(); | |
| 148 | + $field=$registry->resolveAlias(B); | |
| 149 | + A = $field; | |
| 150 | +} | |
| 151 | + | |
| 152 | +value(A) ::= VALUE(B). | |
| 153 | +{ | |
| 154 | + A = B; | |
| 155 | +} | |
| 156 | + | |
| 157 | +value(A) ::= PAR_OPEN valuelist(B) PAR_CLOSE. | |
| 158 | +{ | |
| 159 | + A = B; | |
| 160 | +} | |
| 161 | + | |
| 162 | +valuelist(A) ::= VALUE(B) COMMA valuelist(C). | |
| 163 | +{ | |
| 164 | + C->addValue(B); | |
| 165 | + A = C; | |
| 166 | +} | |
| 167 | + | |
| 168 | +valuelist(A) ::= VALUE(B). | |
| 169 | +{ | |
| 170 | + A = new ValueListExpr(B); | |
| 171 | +} | |
| 172 | + | |
| 173 | +operator(A) ::= CONTAINS. | |
| 174 | +{ | |
| 175 | + A = ExprOp::CONTAINS; | |
| 176 | +} | |
| 177 | + | |
| 178 | +operator(A) ::= LT. | |
| 179 | +{ | |
| 180 | + A = ExprOp::LESS_THAN; | |
| 181 | +} | |
| 182 | + | |
| 183 | +operator(A) ::= GT. | |
| 184 | +{ | |
| 185 | + A = ExprOp::GREATER_THAN; | |
| 186 | +} | |
| 187 | + | |
| 188 | +operator(A) ::= LE. | |
| 189 | +{ | |
| 190 | + A = ExprOp::LESS_THAN_EQUAL; | |
| 191 | +} | |
| 192 | + | |
| 193 | +operator(A) ::= GE. | |
| 194 | +{ | |
| 195 | + A = ExprOp::GREATER_THAN_EQUAL; | |
| 196 | +} | |
| 197 | + | |
| 198 | +operator(A) ::= START WITH. | |
| 199 | +{ | |
| 200 | + A = ExprOp::STARTS_WITH; | |
| 201 | +} | |
| 202 | + | |
| 203 | +operator(A) ::= END WITH. | |
| 204 | +{ | |
| 205 | + A = ExprOp::ENDS_WITH; | |
| 206 | +} | |
| 207 | + | |
| 208 | +operator(A) ::= IS_NOT. | |
| 209 | +{ | |
| 210 | + A = ExprOp::IS_NOT; | |
| 211 | +} | ... | ... |
search2/search/bin/cronSavedSearch.php
0 → 100644
| 1 | +<? | |
| 2 | + | |
| 3 | +require_once(realpath('../../../config/dmsDefaults.php')); | |
| 4 | +//require_once('indexing/indexerCore.inc.php'); | |
| 5 | + | |
| 6 | +// TODO!! | |
| 7 | +//$changed_docs = SearchHelper::getSavedSearchEvents(); | |
| 8 | + | |
| 9 | +die('todo'); | |
| 10 | +/* | |
| 11 | + | |
| 12 | +how this works - | |
| 13 | + | |
| 14 | +a saved search is created. | |
| 15 | + | |
| 16 | +1) any changes - ie new docs, checkins, metadata updates, etc are logged to the saved_search_events table | |
| 17 | +2) periodically, iterate through all documents - do search, and mail user results. remove the event indication. | |
| 18 | + | |
| 19 | + | |
| 20 | +*/ | |
| 21 | +?> | |
| 0 | 22 | \ No newline at end of file | ... | ... |
search2/search/expr.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +//require_once('../../config/dmsDefaults.php'); | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * This is the ideal case, but more complex | |
| 7 | + * | |
| 8 | + */ | |
| 9 | + | |
| 10 | +require_once('indexing/indexerCore.inc.php'); | |
| 11 | +require_once('search/fieldRegistry.inc.php'); | |
| 12 | +require_once('search/exprConstants.inc.php'); | |
| 13 | + | |
| 14 | +class RankManager | |
| 15 | +{ | |
| 16 | + /** | |
| 17 | + * This array contains the rankings of fields on database tables. | |
| 18 | + * | |
| 19 | + * @var array | |
| 20 | + */ | |
| 21 | + private $db; | |
| 22 | + /** | |
| 23 | + * Contains the rankings of metadata fields on fieldset/field combinations. | |
| 24 | + * | |
| 25 | + * @var array | |
| 26 | + */ | |
| 27 | + private $metadata; | |
| 28 | + /** | |
| 29 | + * Contains ranking factor for discussion matching | |
| 30 | + * | |
| 31 | + * @var float | |
| 32 | + */ | |
| 33 | + private $discussion; | |
| 34 | + /** | |
| 35 | + * Contains the ranking factor for text matching | |
| 36 | + * | |
| 37 | + * @var float | |
| 38 | + */ | |
| 39 | + private $text; | |
| 40 | + | |
| 41 | + private function __construct() | |
| 42 | + { | |
| 43 | + $this->dbfields=array(); | |
| 44 | + $sql = "SELECT groupname, itemname, ranking, type FROM search_ranking"; | |
| 45 | + $rs = DBUtil::getResultArray($sql); | |
| 46 | + foreach($rs as $item) | |
| 47 | + { | |
| 48 | + switch ($item['type']) | |
| 49 | + { | |
| 50 | + case 'T': | |
| 51 | + $this->db[$item['groupname']][$item['itemname']] = $item['ranking']+0; | |
| 52 | + break; | |
| 53 | + case 'M': | |
| 54 | + $this->metadata[$item['groupname']][$item['itemname']] = $item['ranking']+0; | |
| 55 | + break; | |
| 56 | + case 'S': | |
| 57 | + switch($item['groupname']) | |
| 58 | + { | |
| 59 | + case 'Discussion': | |
| 60 | + $this->discussion = $item['ranking']+0; | |
| 61 | + break; | |
| 62 | + case 'DocumentText': | |
| 63 | + $this->text = $item['ranking']+0; | |
| 64 | + break; | |
| 65 | + } | |
| 66 | + break; | |
| 67 | + } | |
| 68 | + } | |
| 69 | + } | |
| 70 | + | |
| 71 | + /** | |
| 72 | + * Enter description here... | |
| 73 | + * | |
| 74 | + * @return RankManager | |
| 75 | + */ | |
| 76 | + public static function get() | |
| 77 | + { | |
| 78 | + static $singleton = null; | |
| 79 | + if (is_null($singleton)) | |
| 80 | + { | |
| 81 | + $singleton = new RankManager(); | |
| 82 | + } | |
| 83 | + return $singleton; | |
| 84 | + } | |
| 85 | + | |
| 86 | + public function scoreField($groupname, $type='T', $itemname='') | |
| 87 | + { | |
| 88 | + switch($type) | |
| 89 | + { | |
| 90 | + case 'T': | |
| 91 | + return $this->db[$groupname][$itemname]; | |
| 92 | + case 'M': | |
| 93 | + return $this->metadata[$groupname][$itemname]; | |
| 94 | + case 'S': | |
| 95 | + switch($groupname) | |
| 96 | + { | |
| 97 | + case 'Discussion': | |
| 98 | + return $this->discussion; | |
| 99 | + case 'DocumentText': | |
| 100 | + return $this->text; | |
| 101 | + default: | |
| 102 | + return 0; | |
| 103 | + } | |
| 104 | + default: | |
| 105 | + return 0; | |
| 106 | + } | |
| 107 | + } | |
| 108 | +} | |
| 109 | + | |
| 110 | + | |
| 111 | +class Expr | |
| 112 | +{ | |
| 113 | + /** | |
| 114 | + * The parent expression | |
| 115 | + * | |
| 116 | + * @var Expr | |
| 117 | + */ | |
| 118 | + protected $parent; | |
| 119 | + | |
| 120 | + protected static $node_id = 0; | |
| 121 | + | |
| 122 | + protected $expr_id; | |
| 123 | + | |
| 124 | + public function __construct() | |
| 125 | + { | |
| 126 | + $this->expr_id = Expr::$node_id++; | |
| 127 | + } | |
| 128 | + | |
| 129 | + public function getExprId() | |
| 130 | + { | |
| 131 | + return $this->expr_id; | |
| 132 | + } | |
| 133 | + | |
| 134 | + /** | |
| 135 | + * Coverts the expression to a string | |
| 136 | + * | |
| 137 | + * @return string | |
| 138 | + */ | |
| 139 | + public function __toString() | |
| 140 | + { | |
| 141 | + throw new Exception('Not yet implemented in ' . get_class($this)); | |
| 142 | + } | |
| 143 | + | |
| 144 | + /** | |
| 145 | + * Reference to the parent expression | |
| 146 | + * | |
| 147 | + * @return Expr | |
| 148 | + */ | |
| 149 | + public function &getParent() | |
| 150 | + { | |
| 151 | + return $this->parent; | |
| 152 | + } | |
| 153 | + | |
| 154 | + /** | |
| 155 | + * Sets the parent expiression | |
| 156 | + * | |
| 157 | + * @param Expr $parent | |
| 158 | + */ | |
| 159 | + public function setParent(&$parent) | |
| 160 | + { | |
| 161 | + $this->parent = &$parent; | |
| 162 | + } | |
| 163 | + | |
| 164 | + /** | |
| 165 | + * Is the expression valid | |
| 166 | + * | |
| 167 | + * @return boolean | |
| 168 | + */ | |
| 169 | + public function is_valid() | |
| 170 | + { | |
| 171 | + return true; | |
| 172 | + } | |
| 173 | + | |
| 174 | + public function isExpr() | |
| 175 | + { | |
| 176 | + return $this instanceof OpExpr; | |
| 177 | + } | |
| 178 | + | |
| 179 | + public function isOpExpr() | |
| 180 | + { | |
| 181 | + return $this instanceof OpExpr; | |
| 182 | + } | |
| 183 | + public function isValueExpr() | |
| 184 | + { | |
| 185 | + return $this instanceof ValueExpr; | |
| 186 | + } | |
| 187 | + public function isValueListExpr() | |
| 188 | + { | |
| 189 | + return $this instanceof ValueListExpr; | |
| 190 | + } | |
| 191 | + | |
| 192 | + public function isDbExpr() | |
| 193 | + { | |
| 194 | + return $this instanceof DBFieldExpr; | |
| 195 | + } | |
| 196 | + | |
| 197 | + public function isFieldExpr() | |
| 198 | + { | |
| 199 | + return $this instanceof FieldExpr; | |
| 200 | + } | |
| 201 | + | |
| 202 | + public function isSearchableText() | |
| 203 | + { | |
| 204 | + return $this instanceof SearchableText ; | |
| 205 | + } | |
| 206 | + | |
| 207 | + public function isMetadataField() | |
| 208 | + { | |
| 209 | + return $this instanceof MetadataField; | |
| 210 | + } | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + public function toViz(&$str, $phase) | |
| 217 | + { | |
| 218 | + throw new Exception('To be implemented' . get_class($this)); | |
| 219 | + } | |
| 220 | + | |
| 221 | + public function toVizGraph($options=array()) | |
| 222 | + { | |
| 223 | + $str = "digraph tree {\n"; | |
| 224 | + if (isset($options['left-to-right']) && $options['left-to-right']) | |
| 225 | + { | |
| 226 | + $str .= "rankdir=LR\n"; | |
| 227 | + } | |
| 228 | + | |
| 229 | + $this->toViz($str, 0); | |
| 230 | + $this->toViz($str, 1); | |
| 231 | + | |
| 232 | + $str .= "}\n"; | |
| 233 | + | |
| 234 | + if (isset($options['tofile'])) | |
| 235 | + { | |
| 236 | + $path=dirname($options['tofile']); | |
| 237 | + $filename=basename($options['tofile']); | |
| 238 | + $ext = pathinfo($filename, PATHINFO_EXTENSION); | |
| 239 | + $base = substr($filename, 0, -strlen($ext)-1); | |
| 240 | + | |
| 241 | + $dotfile="$path/$base.$ext"; | |
| 242 | + $jpgfile="$path/$base.jpg"; | |
| 243 | + $fp = fopen($dotfile,'wt'); | |
| 244 | + fwrite($fp, $str); | |
| 245 | + fclose($fp); | |
| 246 | + | |
| 247 | + system("dot -Tjpg -o$jpgfile $dotfile"); | |
| 248 | + | |
| 249 | + if (isset($options['view']) && $options['view']) | |
| 250 | + { | |
| 251 | + system("eog $jpgfile"); | |
| 252 | + } | |
| 253 | + } | |
| 254 | + | |
| 255 | + return $str; | |
| 256 | + } | |
| 257 | +} | |
| 258 | + | |
| 259 | +class FieldExpr extends Expr | |
| 260 | +{ | |
| 261 | + /** | |
| 262 | + * Name of the field | |
| 263 | + * | |
| 264 | + * @var string | |
| 265 | + */ | |
| 266 | + protected $field; | |
| 267 | + | |
| 268 | + protected $alias; | |
| 269 | + | |
| 270 | + protected $display; | |
| 271 | + | |
| 272 | + | |
| 273 | + /** | |
| 274 | + * Constructor for the field expression | |
| 275 | + * | |
| 276 | + * @param string $field | |
| 277 | + */ | |
| 278 | + public function __construct($field, $display=null) | |
| 279 | + { | |
| 280 | + parent::__construct(); | |
| 281 | + $this->field=$field; | |
| 282 | + if (is_null($display)) | |
| 283 | + { | |
| 284 | + $display=get_class($this); | |
| 285 | + } | |
| 286 | + $this->display = $display; | |
| 287 | + $this->setAlias(get_class($this)); | |
| 288 | + } | |
| 289 | + | |
| 290 | + public function setAlias($alias) | |
| 291 | + { | |
| 292 | + $this->alias=$alias; | |
| 293 | + } | |
| 294 | + | |
| 295 | + public function getDisplay() | |
| 296 | + { | |
| 297 | + return $this->display; | |
| 298 | + } | |
| 299 | + | |
| 300 | + public function getAlias() | |
| 301 | + { | |
| 302 | + return $this->alias; | |
| 303 | + } | |
| 304 | + | |
| 305 | + public function getFullName() | |
| 306 | + { | |
| 307 | + return $this->alias . '.' . $this->field; | |
| 308 | + } | |
| 309 | + | |
| 310 | + /** | |
| 311 | + * Returns the field | |
| 312 | + * | |
| 313 | + * @return string | |
| 314 | + */ | |
| 315 | + public function getField() | |
| 316 | + { | |
| 317 | + return $this->field; | |
| 318 | + } | |
| 319 | + | |
| 320 | + /** | |
| 321 | + * Coverts the expression to a string | |
| 322 | + * | |
| 323 | + * @return string | |
| 324 | + */ | |
| 325 | + public function __toString() | |
| 326 | + { | |
| 327 | + return $this->alias; | |
| 328 | + } | |
| 329 | + | |
| 330 | + public function toViz(&$str, $phase) | |
| 331 | + { | |
| 332 | + if ($phase == 0) | |
| 333 | + { | |
| 334 | + $expr_id = $this->getExprId(); | |
| 335 | + $str .= "struct$expr_id [style=rounded, label=\"$expr_id: FIELD[$this->alias]\"]\n"; | |
| 336 | + } | |
| 337 | + } | |
| 338 | + | |
| 339 | + public function rewrite(&$left, &$op, &$right, $not=false) | |
| 340 | + { | |
| 341 | + $input = $left->getInputRequirements(); | |
| 342 | + | |
| 343 | + if ($input['value']['type'] != FieldInputType::FULLTEXT) | |
| 344 | + { | |
| 345 | + return; | |
| 346 | + } | |
| 347 | + | |
| 348 | + | |
| 349 | + if ($right->isValueExpr()) | |
| 350 | + { | |
| 351 | + $value = $right->getValue(); | |
| 352 | + } | |
| 353 | + else | |
| 354 | + { | |
| 355 | + $value = $right; | |
| 356 | + } | |
| 357 | + | |
| 358 | + if (substr($value,0,1) != '\'' || substr($value,-1) != '\'') | |
| 359 | + { | |
| 360 | + OpExpr::rewriteString($left, $op, $right, $not); | |
| 361 | + } | |
| 362 | + else | |
| 363 | + { | |
| 364 | + $right = new ValueExpr(trim(substr($value,1,-1))); | |
| 365 | + } | |
| 366 | + } | |
| 367 | +} | |
| 368 | + | |
| 369 | +class DBFieldExpr extends FieldExpr | |
| 370 | +{ | |
| 371 | + /** | |
| 372 | + * The table the field is associated with | |
| 373 | + * | |
| 374 | + * @var string | |
| 375 | + */ | |
| 376 | + protected $table; | |
| 377 | + | |
| 378 | + protected $jointable; | |
| 379 | + protected $joinfield; | |
| 380 | + protected $matchfield; | |
| 381 | + protected $quotedvalue; | |
| 382 | + | |
| 383 | + | |
| 384 | + /** | |
| 385 | + * Constructor for the database field | |
| 386 | + * | |
| 387 | + * @param string $field | |
| 388 | + * @param string $table | |
| 389 | + */ | |
| 390 | + public function __construct($field, $table, $display=null) | |
| 391 | + { | |
| 392 | + if (is_null($display)) | |
| 393 | + { | |
| 394 | + $display = get_class($this); | |
| 395 | + } | |
| 396 | + | |
| 397 | + parent::__construct($field, $display); | |
| 398 | + | |
| 399 | + $this->table=$table; | |
| 400 | + $this->jointable = null; | |
| 401 | + $this->joinfield = null; | |
| 402 | + $this->matchfield = null; | |
| 403 | + $this->quotedvalue=true; | |
| 404 | + } | |
| 405 | + | |
| 406 | + /** | |
| 407 | + * Returns the table name | |
| 408 | + * | |
| 409 | + * @return string | |
| 410 | + */ | |
| 411 | + public function getTable() | |
| 412 | + { | |
| 413 | + return $this->table; | |
| 414 | + } | |
| 415 | + | |
| 416 | + public function joinTo($table, $field) | |
| 417 | + { | |
| 418 | + $this->jointable=$table; | |
| 419 | + $this->joinfield=$field; | |
| 420 | + } | |
| 421 | + public function matchField($field) | |
| 422 | + { | |
| 423 | + $this->matchfield = $field; | |
| 424 | + } | |
| 425 | + | |
| 426 | + public function modifyName($name) | |
| 427 | + { | |
| 428 | + return $name; | |
| 429 | + } | |
| 430 | + | |
| 431 | + public function modifyValue($value) | |
| 432 | + { | |
| 433 | + return $value; | |
| 434 | + } | |
| 435 | + | |
| 436 | + | |
| 437 | + public function getJoinTable() { return $this->jointable; } | |
| 438 | + public function getJoinField() { return $this->joinfield; } | |
| 439 | + public function getMatchingField() { return $this->matchfield; } | |
| 440 | + public function isValueQuoted($quotedvalue = null) | |
| 441 | + { | |
| 442 | + if (isset($quotedvalue)) | |
| 443 | + { | |
| 444 | + $this->quotedvalue = $quotedvalue; | |
| 445 | + } | |
| 446 | + return $this->quotedvalue; | |
| 447 | + } | |
| 448 | +} | |
| 449 | + | |
| 450 | +class MetadataField extends DBFieldExpr | |
| 451 | +{ | |
| 452 | + protected $fieldset; | |
| 453 | + protected $fieldid; | |
| 454 | + protected $fieldsetid; | |
| 455 | + | |
| 456 | + public function __construct($fieldset, $field, $fieldsetid, $fieldid) | |
| 457 | + { | |
| 458 | + parent::__construct($field, 'document_fields_link'); | |
| 459 | + $this->fieldset=$fieldset; | |
| 460 | + $this->fieldid=$fieldid; | |
| 461 | + $this->fieldsetid=$fieldsetid; | |
| 462 | + } | |
| 463 | + | |
| 464 | + public function getFieldSet() | |
| 465 | + { | |
| 466 | + return $this->fieldset; | |
| 467 | + } | |
| 468 | + | |
| 469 | + public function getFieldId() | |
| 470 | + { | |
| 471 | + return $this->fieldid; | |
| 472 | + } | |
| 473 | + | |
| 474 | + public function getFieldSetId() | |
| 475 | + { | |
| 476 | + return $this->fieldsetid; | |
| 477 | + } | |
| 478 | + | |
| 479 | + public function getInputRequirements() | |
| 480 | + { | |
| 481 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 482 | + } | |
| 483 | + | |
| 484 | + /** | |
| 485 | + * Coverts the expression to a string | |
| 486 | + * | |
| 487 | + * @return string | |
| 488 | + */ | |
| 489 | + public function __toString() | |
| 490 | + { | |
| 491 | + return "METADATA[$this->fieldset][$this->field]"; | |
| 492 | + } | |
| 493 | + | |
| 494 | +} | |
| 495 | + | |
| 496 | +class SearchableText extends FieldExpr | |
| 497 | +{ | |
| 498 | +} | |
| 499 | + | |
| 500 | +class ValueExpr extends Expr | |
| 501 | +{ | |
| 502 | + /** | |
| 503 | + * The value | |
| 504 | + * | |
| 505 | + * @var mixed | |
| 506 | + */ | |
| 507 | + protected $value; | |
| 508 | + | |
| 509 | + /** | |
| 510 | + * Constructor for the value expression | |
| 511 | + * | |
| 512 | + * @param mixed $value | |
| 513 | + */ | |
| 514 | + public function __construct($value) | |
| 515 | + { | |
| 516 | + parent::__construct(); | |
| 517 | + $this->value=$value; | |
| 518 | + } | |
| 519 | + | |
| 520 | + public function getValue() | |
| 521 | + { | |
| 522 | + return $this->value; | |
| 523 | + } | |
| 524 | + | |
| 525 | + /** | |
| 526 | + * Converts the value to a string | |
| 527 | + * | |
| 528 | + * @return unknown | |
| 529 | + */ | |
| 530 | + public function __toString() | |
| 531 | + { | |
| 532 | + return (string) "\"$this->value\""; | |
| 533 | + } | |
| 534 | + | |
| 535 | + public function toViz(&$str, $phase) | |
| 536 | + { | |
| 537 | + if ($phase == 0) | |
| 538 | + { | |
| 539 | + $expr_id = $this->getExprId(); | |
| 540 | + $value = addslashes($this->value); | |
| 541 | + $str .= "struct$expr_id [style=ellipse, label=\"$expr_id: \\\"$value\\\"\"]\n"; | |
| 542 | + } | |
| 543 | + } | |
| 544 | + | |
| 545 | + public function getSQL($field, $fieldname, $op, $not=false) | |
| 546 | + { | |
| 547 | + $val = $field->modifyValue($this->getValue()); | |
| 548 | + $quote = ''; | |
| 549 | + if ($field->isValueQuoted()) | |
| 550 | + { | |
| 551 | + $val = addslashes($val); | |
| 552 | + $quote = '\''; | |
| 553 | + } | |
| 554 | + | |
| 555 | + switch($op) | |
| 556 | + { | |
| 557 | + case ExprOp::CONTAINS: | |
| 558 | + $sql = "$fieldname LIKE '%$val%'"; | |
| 559 | + break; | |
| 560 | + case ExprOp::STARTS_WITH: | |
| 561 | + $sql = "$fieldname LIKE '$val%'"; | |
| 562 | + break; | |
| 563 | + case ExprOp::ENDS_WITH: | |
| 564 | + $sql = "$fieldname LIKE '%$val'"; | |
| 565 | + break; | |
| 566 | + case ExprOp::IS: | |
| 567 | + $sql = "$fieldname = $quote$val$quote"; | |
| 568 | + break; | |
| 569 | + case ExprOp::GREATER_THAN : | |
| 570 | + $sql = "$fieldname > $quote$val$quote"; | |
| 571 | + break; | |
| 572 | + case ExprOp::GREATER_THAN_EQUAL : | |
| 573 | + $sql = "$fieldname >= $quote$val$quote"; | |
| 574 | + break; | |
| 575 | + case ExprOp::LESS_THAN : | |
| 576 | + $sql = "$fieldname < $quote$val$quote"; | |
| 577 | + break; | |
| 578 | + case ExprOp::LESS_THAN_EQUAL : | |
| 579 | + $sql = "$fieldname <= $quote$val$quote"; | |
| 580 | + break; | |
| 581 | + default: | |
| 582 | + throw new Exception('Unknown op: ' . $op); | |
| 583 | + } | |
| 584 | + | |
| 585 | + if ($not) | |
| 586 | + { | |
| 587 | + $sql = "not ($sql)"; | |
| 588 | + } | |
| 589 | + | |
| 590 | + return $sql; | |
| 591 | + } | |
| 592 | + | |
| 593 | +} | |
| 594 | + | |
| 595 | +class ValueListExpr extends Expr | |
| 596 | +{ | |
| 597 | + /** | |
| 598 | + * The value | |
| 599 | + * | |
| 600 | + * @var mixed | |
| 601 | + */ | |
| 602 | + protected $values; | |
| 603 | + | |
| 604 | + /** | |
| 605 | + * Constructor for the value expression | |
| 606 | + * | |
| 607 | + * @param mixed $value | |
| 608 | + */ | |
| 609 | + public function __construct($value) | |
| 610 | + { | |
| 611 | + parent::__construct($value); | |
| 612 | + $this->values=array($value); | |
| 613 | + } | |
| 614 | + | |
| 615 | + public function addValue($value) | |
| 616 | + { | |
| 617 | + $this->values[] = $value; | |
| 618 | + } | |
| 619 | + | |
| 620 | + | |
| 621 | + public function getValue($param=null) | |
| 622 | + { | |
| 623 | + if (!empty($param)) | |
| 624 | + { | |
| 625 | + return $this->values[$param]; | |
| 626 | + } | |
| 627 | + $str = ''; | |
| 628 | + | |
| 629 | + foreach($this->values as $value) | |
| 630 | + { | |
| 631 | + if ($str != '') $str .= ','; | |
| 632 | + $str .= "\"$value\""; | |
| 633 | + } | |
| 634 | + | |
| 635 | + return $str; | |
| 636 | + } | |
| 637 | + | |
| 638 | + /** | |
| 639 | + * Converts the value to a string | |
| 640 | + * | |
| 641 | + * @return unknown | |
| 642 | + */ | |
| 643 | + public function __toString() | |
| 644 | + { | |
| 645 | + return $this->getValue(); | |
| 646 | + } | |
| 647 | + | |
| 648 | + public function toViz(&$str, $phase) | |
| 649 | + { | |
| 650 | + if ($phase == 0) | |
| 651 | + { | |
| 652 | + $expr_id = $this->getExprId(); | |
| 653 | + | |
| 654 | + $str .= "struct$expr_id [style=ellipse, label=\"$expr_id: "; | |
| 655 | + $i=0; | |
| 656 | + foreach($this->values as $value) | |
| 657 | + { | |
| 658 | + if ($i++>0) $str .= ','; | |
| 659 | + $value = addslashes($value); | |
| 660 | + $str .= "\\\"$value\\\""; | |
| 661 | + } | |
| 662 | + $str .= "\"]\n"; | |
| 663 | + } | |
| 664 | + } | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + public function rewrite(&$left, &$op, &$right, &$not) | |
| 669 | + { | |
| 670 | + if (count($this->values) == 1) | |
| 671 | + { | |
| 672 | + $right = new ValueExpr($this->values[0]); | |
| 673 | + return; | |
| 674 | + } | |
| 675 | + $newops = array(); | |
| 676 | + foreach($this->values as $value) | |
| 677 | + { | |
| 678 | + $classname = get_class($left); | |
| 679 | + $class = new $classname; | |
| 680 | + $newop = new OpExpr($class, $op, $value); | |
| 681 | + $newops[] = $newop; | |
| 682 | + } | |
| 683 | + | |
| 684 | + $result = $newops[0]; | |
| 685 | + for($i=1;$i<count($newops);$i++) | |
| 686 | + { | |
| 687 | + $result = new OpExpr($result, ExprOp::OP_OR, $newops[$i]); | |
| 688 | + } | |
| 689 | + | |
| 690 | + $left = $result->left(); | |
| 691 | + $op = $result->op(); | |
| 692 | + $right = $result->right(); | |
| 693 | + } | |
| 694 | + | |
| 695 | +} | |
| 696 | + | |
| 697 | + | |
| 698 | +class BetweenValueExpr extends ValueExpr | |
| 699 | +{ | |
| 700 | + protected $endvalue; | |
| 701 | + | |
| 702 | + public function __construct($start, $end) | |
| 703 | + { | |
| 704 | + parent::__construct($start); | |
| 705 | + $this->endvalue = $end; | |
| 706 | + } | |
| 707 | + | |
| 708 | + public function getStart() | |
| 709 | + { | |
| 710 | + return $this->getValue(); | |
| 711 | + } | |
| 712 | + | |
| 713 | + public function getEnd() | |
| 714 | + { | |
| 715 | + return $this->endvalue; | |
| 716 | + } | |
| 717 | + | |
| 718 | + /** | |
| 719 | + * Converts the value to a string | |
| 720 | + * | |
| 721 | + * @return unknown | |
| 722 | + */ | |
| 723 | + public function __toString() | |
| 724 | + { | |
| 725 | + return (string) $this->value . ' AND ' . $this->endvalue; | |
| 726 | + } | |
| 727 | + | |
| 728 | + public function toViz(&$str, $phase) | |
| 729 | + { | |
| 730 | + if ($phase == 0) | |
| 731 | + { | |
| 732 | + $value = addslashes($this->value); | |
| 733 | + $value2 = addslashes($this->endvalue); | |
| 734 | + | |
| 735 | + $expr_id = $this->getExprId(); | |
| 736 | + $str .= "struct$expr_id [style=rounded, label=\"$expr_id: $value AND $value2\"]\n"; | |
| 737 | + } | |
| 738 | + } | |
| 739 | + | |
| 740 | + public function getSQL($field, $fieldname, $op, $not=false) | |
| 741 | + { | |
| 742 | + if ($op != ExprOp::BETWEEN) | |
| 743 | + { | |
| 744 | + throw new Exception('Unexpected operator: ' . $op); | |
| 745 | + } | |
| 746 | + | |
| 747 | + $quote = ''; | |
| 748 | + | |
| 749 | + $start = $field->modifyValue($this->getStart()); | |
| 750 | + $end = $field->modifyValue($this->getEnd()); | |
| 751 | + | |
| 752 | + if ($field->isValueQuoted()) | |
| 753 | + { | |
| 754 | + $start = addslashes($start); | |
| 755 | + $end = addslashes($end); | |
| 756 | + $quote = '\''; | |
| 757 | + } | |
| 758 | + | |
| 759 | + | |
| 760 | + $not = $not?' NOT ':''; | |
| 761 | + return "$not ($fieldname $op $quote$start$quote AND $quote$end$quote) "; | |
| 762 | + } | |
| 763 | +} | |
| 764 | + | |
| 765 | +interface QueryBuilder | |
| 766 | +{ | |
| 767 | + function buildComplexQuery($expr); | |
| 768 | + | |
| 769 | + function buildSimpleQuery($op, $group); | |
| 770 | + | |
| 771 | + function getRanking($result); | |
| 772 | + | |
| 773 | + function getResultText($result); | |
| 774 | + | |
| 775 | +} | |
| 776 | + | |
| 777 | +class TextQueryBuilder implements QueryBuilder | |
| 778 | +{ | |
| 779 | + private $text; | |
| 780 | + private $query; | |
| 781 | + | |
| 782 | + public function buildComplexQuery($expr) | |
| 783 | + { | |
| 784 | + $left = $expr->left(); | |
| 785 | + $right = $expr->right(); | |
| 786 | + if (DefaultOpCollection::isBoolean($expr)) | |
| 787 | + { | |
| 788 | + $query = '(' . $this->buildComplexQuery($left) . ' ' . $expr->op() . ' ' . $this->buildComplexQuery($right) . ')'; | |
| 789 | + | |
| 790 | + if ($expr->not()) | |
| 791 | + { | |
| 792 | + $query = "NOT $query"; | |
| 793 | + } | |
| 794 | + } | |
| 795 | + else | |
| 796 | + { | |
| 797 | + $fieldname = $left->getField(); | |
| 798 | + $value = addslashes($right->getValue()); | |
| 799 | + | |
| 800 | + $not = $expr->not()?' NOT ':''; | |
| 801 | + | |
| 802 | + $query = "$not$fieldname: \"$value\""; | |
| 803 | + } | |
| 804 | + | |
| 805 | + return $query; | |
| 806 | + } | |
| 807 | + | |
| 808 | + public function buildSimpleQuery($op, $group) | |
| 809 | + { | |
| 810 | + $query = ''; | |
| 811 | + foreach($group as $expr) | |
| 812 | + { | |
| 813 | + if (!empty($query)) | |
| 814 | + { | |
| 815 | + $query .= " $op "; | |
| 816 | + } | |
| 817 | + | |
| 818 | + $left = $expr->left(); | |
| 819 | + $right = $expr->right(); | |
| 820 | + | |
| 821 | + $fieldname = $left->getField(); | |
| 822 | + $value = addslashes($right->getValue()); | |
| 823 | + | |
| 824 | + $not = $expr->not()?' NOT ':''; | |
| 825 | + | |
| 826 | + $query .= "$not$fieldname: \"$value\""; | |
| 827 | + } | |
| 828 | + | |
| 829 | + return $query; | |
| 830 | + } | |
| 831 | + | |
| 832 | + public function getRanking($result) | |
| 833 | + { | |
| 834 | + $init = $result->Rank; | |
| 835 | + $score=0; | |
| 836 | + $ranker = RankManager::get(); | |
| 837 | + $discussion = $result->Discussion; | |
| 838 | + if (!empty($discussion)) | |
| 839 | + { | |
| 840 | + $score += $init *$ranker->scoreField('Discussion', 'S'); | |
| 841 | + } | |
| 842 | + else | |
| 843 | + { | |
| 844 | + $score += $init *$ranker->scoreField('DocumentText', 'S'); | |
| 845 | + | |
| 846 | + } | |
| 847 | + return $score; | |
| 848 | + } | |
| 849 | + | |
| 850 | + public function setQuery($query) | |
| 851 | + { | |
| 852 | + $this->query = $query; | |
| 853 | + } | |
| 854 | + | |
| 855 | + private function extractText($word, $maxwords=40, $maxlen=512) | |
| 856 | + { | |
| 857 | + $offset=stripos($this->text, $word); | |
| 858 | + | |
| 859 | + if ($offset == false) | |
| 860 | + { | |
| 861 | + return array(false, false); | |
| 862 | + } | |
| 863 | + | |
| 864 | + $text = substr($this->text, 0 , $offset); | |
| 865 | + | |
| 866 | + $lastsentence = strrpos($text, '.'); | |
| 867 | + if (!$lastsentence) $lastsentence=0; | |
| 868 | + | |
| 869 | + if ($offset - $lastsentence > $maxlen) | |
| 870 | + { | |
| 871 | + $lastsentence = $offset - $maxlen; | |
| 872 | + } | |
| 873 | + | |
| 874 | + $text = substr($this->text, $lastsentence, $offset - $lastsentence); | |
| 875 | + | |
| 876 | + $wordoffset= strlen($text)-1; | |
| 877 | + $words = $maxwords; | |
| 878 | + while ($words > 0) | |
| 879 | + { | |
| 880 | + $text = substr($text, 0, $wordoffset); | |
| 881 | + $foundoffset = strrpos($text, ' '); | |
| 882 | + if ($foundoffset === false) | |
| 883 | + { | |
| 884 | + break; | |
| 885 | + } | |
| 886 | + $wordoffset = $foundoffset; | |
| 887 | + $words--; | |
| 888 | + } | |
| 889 | + | |
| 890 | + $startOffset = $lastsentence + $wordoffset; | |
| 891 | + | |
| 892 | + $nextsentence = strpos($this->text, '.', $offset); | |
| 893 | + | |
| 894 | + $words = $maxwords; | |
| 895 | + $endOffset = $offset; | |
| 896 | + while ($words > 0) | |
| 897 | + { | |
| 898 | + $foundoffset = strpos($this->text, ' ', $endOffset+1); | |
| 899 | + if ($foundoffset === false) | |
| 900 | + { | |
| 901 | + break; | |
| 902 | + } | |
| 903 | + if ($endOffset > $offset + $maxlen) | |
| 904 | + { | |
| 905 | + break; | |
| 906 | + } | |
| 907 | + if ($endOffset > $nextsentence) | |
| 908 | + { | |
| 909 | + $endOffset = $nextsentence-1; | |
| 910 | + break; | |
| 911 | + } | |
| 912 | + $endOffset = $foundoffset; | |
| 913 | + | |
| 914 | + $words--; | |
| 915 | + } | |
| 916 | + | |
| 917 | + return array($startOffset, substr($this->text, $startOffset, $endOffset - $startOffset + 1)); | |
| 918 | + } | |
| 919 | + | |
| 920 | + | |
| 921 | + public function getResultText($result) | |
| 922 | + { | |
| 923 | + $this->text = substr($result->Text,0,40960); | |
| 924 | + $words = array(); | |
| 925 | + $sentences = array(); | |
| 926 | + | |
| 927 | + preg_match_all('("[^"]*")',$this->query, $matches,PREG_OFFSET_CAPTURE); | |
| 928 | + | |
| 929 | + foreach($matches[0] as $word) | |
| 930 | + { | |
| 931 | + list($word,$offset) = $word; | |
| 932 | + $word = substr($word,1,-1); | |
| 933 | + $wordlen = strlen($word); | |
| 934 | + $res = $this->extractText($word); | |
| 935 | + list($sentenceOffset,$sentence) = $res; | |
| 936 | + | |
| 937 | + if ($sentenceOffset === false) | |
| 938 | + { | |
| 939 | + continue; | |
| 940 | + } | |
| 941 | + | |
| 942 | + if (array_key_exists($sentenceOffset, $sentences)) | |
| 943 | + { | |
| 944 | + $sentences[$sentenceOffset]['score']++; | |
| 945 | + } | |
| 946 | + else | |
| 947 | + { | |
| 948 | + $sentences[$sentenceOffset] = array( | |
| 949 | + 'sentence'=>$sentence, | |
| 950 | + 'score'=>1 | |
| 951 | + ); | |
| 952 | + } | |
| 953 | + | |
| 954 | + $sentence = $sentences[$sentenceOffset]['sentence']; | |
| 955 | + | |
| 956 | + preg_match_all("@$word@i",$sentence, $swords,PREG_OFFSET_CAPTURE); | |
| 957 | + foreach($swords[0] as $wordx) | |
| 958 | + { | |
| 959 | + list($wordx,$offset) = $wordx; | |
| 960 | + | |
| 961 | + $sentence = substr($sentence,0, $offset) . '<b>' . substr($sentence, $offset, $wordlen) . '</b>' . substr($sentence, $offset + $wordlen); | |
| 962 | + } | |
| 963 | + | |
| 964 | + $sentences[$sentenceOffset]['sentence'] = $sentence; | |
| 965 | + | |
| 966 | + $words[$word] = array( | |
| 967 | + 'sentence'=>$sentenceOffset | |
| 968 | + ); | |
| 969 | + } | |
| 970 | + | |
| 971 | + ksort($sentences); | |
| 972 | + $result = ''; | |
| 973 | + | |
| 974 | + foreach($sentences as $o=>$i) | |
| 975 | + { | |
| 976 | + if (!empty($result)) $result .= ' ... '; | |
| 977 | + $result .= $i['sentence']; | |
| 978 | + } | |
| 979 | + | |
| 980 | + return $result; | |
| 981 | + } | |
| 982 | + | |
| 983 | +} | |
| 984 | + | |
| 985 | +class SQLQueryBuilder implements QueryBuilder | |
| 986 | +{ | |
| 987 | + private $used_tables; | |
| 988 | + private $aliases; | |
| 989 | + private $sql; | |
| 990 | + private $db; | |
| 991 | + private $metadata; | |
| 992 | + | |
| 993 | + public function __construct() | |
| 994 | + { | |
| 995 | + $this->used_tables = array( | |
| 996 | + 'documents'=>1, | |
| 997 | + 'document_metadata_version'=>1, | |
| 998 | + 'document_content_version'=>0, | |
| 999 | + 'tag_words'=>0, | |
| 1000 | + 'document_fields_link'=>0 | |
| 1001 | + ); | |
| 1002 | + | |
| 1003 | + $this->aliases = array( | |
| 1004 | + 'documents'=>'d', | |
| 1005 | + 'document_metadata_version'=>'dmv', | |
| 1006 | + 'document_content_version'=>'dcv', | |
| 1007 | + 'tag_words'=>'tw', | |
| 1008 | + 'document_fields_link'=>'pdfl' | |
| 1009 | + ); | |
| 1010 | + | |
| 1011 | + $this->sql = ''; | |
| 1012 | + $this->db = array(); | |
| 1013 | + $this->metadata = array(); | |
| 1014 | + } | |
| 1015 | + | |
| 1016 | + /** | |
| 1017 | + * This looks up a table name to find the appropriate alias. | |
| 1018 | + * | |
| 1019 | + * @param string $tablename | |
| 1020 | + * @return string | |
| 1021 | + */ | |
| 1022 | + private function resolveTableToAlias($tablename) | |
| 1023 | + { | |
| 1024 | + if (array_key_exists($tablename, $this->aliases)) | |
| 1025 | + { | |
| 1026 | + return $this->aliases[$tablename]; | |
| 1027 | + } | |
| 1028 | + throw new Exception("Unknown tablename '$tablename'"); | |
| 1029 | + } | |
| 1030 | + | |
| 1031 | + private function exploreExprs($expr, $parent=null) | |
| 1032 | + { | |
| 1033 | + if ($expr->isMetadataField()) | |
| 1034 | + { | |
| 1035 | + $this->metadata[] = & $parent; | |
| 1036 | + } | |
| 1037 | + elseif ($expr->isDBExpr()) | |
| 1038 | + { | |
| 1039 | + $this->db[] = & $parent; | |
| 1040 | + $this->used_tables[$expr->getTable()]++; | |
| 1041 | + } | |
| 1042 | + elseif ($expr->isOpExpr()) | |
| 1043 | + { | |
| 1044 | + $left = & $expr->left(); | |
| 1045 | + $right = & $expr->right(); | |
| 1046 | + if (DefaultOpCollection::isBoolean($expr)) | |
| 1047 | + { | |
| 1048 | + $this->exploreExprs($left, $expr); | |
| 1049 | + $this->exploreExprs($right, $expr); | |
| 1050 | + } | |
| 1051 | + else | |
| 1052 | + { | |
| 1053 | + // if it is not a boolean, we only need to explore left as it is the one where the main field is defined. | |
| 1054 | + $this->exploreExprs($left, $expr); | |
| 1055 | + } | |
| 1056 | + } | |
| 1057 | + } | |
| 1058 | + | |
| 1059 | + private function exploreGroup($group) | |
| 1060 | + { | |
| 1061 | + // split up metadata and determine table usage | |
| 1062 | + foreach($group as $expr) | |
| 1063 | + { | |
| 1064 | + $field = $expr->left(); | |
| 1065 | + | |
| 1066 | + if ($field->isMetadataField()) | |
| 1067 | + { | |
| 1068 | + $this->metadata[] = $expr->getParent(); | |
| 1069 | + } | |
| 1070 | + elseif ($field->isDBExpr()) | |
| 1071 | + { | |
| 1072 | + $this->db[] = $expr->getParent(); | |
| 1073 | + $this->used_tables[$field->getTable()]++; | |
| 1074 | + } | |
| 1075 | + } | |
| 1076 | + } | |
| 1077 | + | |
| 1078 | + private function getFieldnameFromExpr($expr) | |
| 1079 | + { | |
| 1080 | + $field = $expr->left(); | |
| 1081 | + if (is_null($field->getJoinTable())) | |
| 1082 | + { | |
| 1083 | + $alias = $this->resolveTableToAlias($field->getTable()); | |
| 1084 | + $fieldname = $alias . '.' . $field->getField(); | |
| 1085 | + } | |
| 1086 | + else | |
| 1087 | + { | |
| 1088 | + $offset = $this->resolveJoinOffset($expr); | |
| 1089 | + $matching = $field->getMatchingField(); | |
| 1090 | + $tablename = $field->getJoinTable(); | |
| 1091 | + $fieldname = "$tablename$offset.$matching"; | |
| 1092 | + } | |
| 1093 | + | |
| 1094 | + return $fieldname; | |
| 1095 | + } | |
| 1096 | + | |
| 1097 | + private function getSQLEvalExpr($expr) | |
| 1098 | + { | |
| 1099 | + $left = $expr->left(); | |
| 1100 | + $right = $expr->right(); | |
| 1101 | + if ($left->isMetadataField()) | |
| 1102 | + { | |
| 1103 | + $offset = $this->resolveMetadataOffset($expr) + 1; | |
| 1104 | + | |
| 1105 | + $fieldset = $left->getField(); | |
| 1106 | + $query = '(' . "df$offset.name='$fieldset' AND " . $right->getSQL($left, "dfl$offset.value", $expr->op(), false) . ')'; | |
| 1107 | + | |
| 1108 | + } | |
| 1109 | + else | |
| 1110 | + { | |
| 1111 | + $fieldname = $this->getFieldnameFromExpr($expr); | |
| 1112 | + | |
| 1113 | + $query = $right->getSQL($left, $left->modifyName($fieldname), $expr->op(), $expr->not());; | |
| 1114 | + } | |
| 1115 | + return $query; | |
| 1116 | + } | |
| 1117 | + | |
| 1118 | + private function buildCoreSQL() | |
| 1119 | + { | |
| 1120 | + if (count($this->metadata) + count($this->db) == 0) | |
| 1121 | + { | |
| 1122 | + throw new Exception('nothing to do'); | |
| 1123 | + } | |
| 1124 | + | |
| 1125 | + // we are doing this because content table is dependant on metadata table | |
| 1126 | + if ($this->used_tables['document_content_version'] > 0) $this->used_tables['document_metadata_version']++; | |
| 1127 | + | |
| 1128 | + $sql = | |
| 1129 | + 'SELECT ' . "\n"; | |
| 1130 | + | |
| 1131 | + $sql .= | |
| 1132 | + ' DISTINCT d.id, dmv.name as title'; | |
| 1133 | + | |
| 1134 | + $offset=0; | |
| 1135 | + foreach($this->db as $expr) | |
| 1136 | + { | |
| 1137 | + $offset++; | |
| 1138 | + $sql .= ", ifnull(" . $this->getSQLEvalExpr($expr) . ",0) as expr$offset "; | |
| 1139 | + } | |
| 1140 | + | |
| 1141 | + foreach($this->metadata as $expr) | |
| 1142 | + { | |
| 1143 | + $offset++; | |
| 1144 | + $sql .= ", ifnull(" . $this->getSQLEvalExpr($expr) . ",0) as expr$offset "; | |
| 1145 | + } | |
| 1146 | + | |
| 1147 | + $sql .= | |
| 1148 | + "\n" . 'FROM ' ."\n" . | |
| 1149 | + ' documents d ' ."\n"; | |
| 1150 | + | |
| 1151 | + if ($this->used_tables['document_metadata_version'] > 0) | |
| 1152 | + { | |
| 1153 | + $sql .= ' INNER JOIN document_metadata_version dmv ON d.metadata_version_id=dmv.id' . "\n"; | |
| 1154 | + } | |
| 1155 | + if ($this->used_tables['document_content_version'] > 0) | |
| 1156 | + { | |
| 1157 | + $sql .= ' INNER JOIN document_content_version dcv ON dmv.content_version_id=dcv.id ' . "\n"; | |
| 1158 | + } | |
| 1159 | + if ($this->used_tables['document_fields_link'] > 0) | |
| 1160 | + { | |
| 1161 | + $sql .= ' LEFT JOIN document_fields_link pdfl ON dmv.id=pdfl.metadata_version_id ' . "\n"; | |
| 1162 | + } | |
| 1163 | + | |
| 1164 | + if ($this->used_tables['tag_words'] > 0) | |
| 1165 | + { | |
| 1166 | + $sql .= ' LEFT OUTER JOIN document_tags dt ON dt.document_id=d.id ' . "\n" . | |
| 1167 | + ' LEFT OUTER JOIN tag_words tw ON dt.tag_id = tw.id ' . "\n"; | |
| 1168 | + } | |
| 1169 | + | |
| 1170 | + $offset = 0; | |
| 1171 | + foreach($this->db as $expr) | |
| 1172 | + { | |
| 1173 | + $field = $expr->left(); | |
| 1174 | + $jointable=$field->getJoinTable(); | |
| 1175 | + if (!is_null($jointable)) | |
| 1176 | + { | |
| 1177 | + $fieldname = $this->resolveTableToAlias($field->getTable()) . '.' . $field->getField(); | |
| 1178 | + | |
| 1179 | + $joinalias = "$jointable$offset"; | |
| 1180 | + $joinfield = $field->getJoinField(); | |
| 1181 | + $sql .= " LEFT OUTER JOIN $jointable $joinalias ON $fieldname=$joinalias.$joinfield\n"; | |
| 1182 | + } | |
| 1183 | + $offset++; | |
| 1184 | + } | |
| 1185 | + | |
| 1186 | + | |
| 1187 | + | |
| 1188 | + $offset=0; | |
| 1189 | + foreach($this->metadata as $expr) | |
| 1190 | + { | |
| 1191 | + $offset++; | |
| 1192 | + $field = $expr->left(); | |
| 1193 | + | |
| 1194 | + $fieldid = $field->getFieldId(); | |
| 1195 | + $sql .= " LEFT JOIN document_fields_link dfl$offset ON dfl$offset.metadata_version_id=d.metadata_version_id AND dfl$offset.document_field_id=$fieldid" . "\n"; | |
| 1196 | + $sql .= " LEFT JOIN document_fields df$offset ON df$offset.id=dfl$offset.document_field_id" . "\n"; | |
| 1197 | + } | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + $sql .= | |
| 1201 | + 'WHERE dmv.status_id=1 AND d.status_id=1 AND ' . "\n "; | |
| 1202 | + | |
| 1203 | + return $sql; | |
| 1204 | + } | |
| 1205 | + | |
| 1206 | + private function resolveMetadataOffset($expr) | |
| 1207 | + { | |
| 1208 | + assert($expr->left()->isMetadataField() ); | |
| 1209 | + | |
| 1210 | + $offset=0; | |
| 1211 | + foreach($this->metadata as $item) | |
| 1212 | + { | |
| 1213 | + if ($item->getExprId() == $expr->getExprId()) | |
| 1214 | + { | |
| 1215 | + return $offset; | |
| 1216 | + } | |
| 1217 | + $offset++; | |
| 1218 | + } | |
| 1219 | + throw new Exception('metadata field not found'); | |
| 1220 | + } | |
| 1221 | + | |
| 1222 | + private function resolveJoinOffset($expr) | |
| 1223 | + { | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + $offset=0; | |
| 1227 | + foreach($this->db as $item) | |
| 1228 | + { | |
| 1229 | + if ($item->getExprId() == $expr->getExprId()) | |
| 1230 | + { | |
| 1231 | + return $offset; | |
| 1232 | + } | |
| 1233 | + $offset++; | |
| 1234 | + } | |
| 1235 | + throw new Exception('join field not found'); | |
| 1236 | + } | |
| 1237 | + | |
| 1238 | + private function buildCoreSQLExpr($expr) | |
| 1239 | + { | |
| 1240 | + $left = $expr->left(); | |
| 1241 | + $right = $expr->right(); | |
| 1242 | + if (DefaultOpCollection::isBoolean($expr)) | |
| 1243 | + { | |
| 1244 | + $query = '(' . $this->buildCoreSQLExpr($left) . ' ' . $expr->op() . ' ' . $this->buildCoreSQLExpr($right) . ')'; | |
| 1245 | + } | |
| 1246 | + else | |
| 1247 | + { | |
| 1248 | + $query = $this->getSQLEvalExpr($expr); | |
| 1249 | + } | |
| 1250 | + | |
| 1251 | + if ($expr->not()) | |
| 1252 | + { | |
| 1253 | + $query = "NOT $query"; | |
| 1254 | + } | |
| 1255 | + | |
| 1256 | + return $query; | |
| 1257 | + } | |
| 1258 | + | |
| 1259 | + public function buildComplexQuery($expr) | |
| 1260 | + { | |
| 1261 | +// print "building complex \n\n"; | |
| 1262 | + $this->exploreExprs($expr); | |
| 1263 | + | |
| 1264 | + $sql = $this->buildCoreSQL(); | |
| 1265 | + | |
| 1266 | + $sql .= $this->buildCoreSQLExpr($expr); | |
| 1267 | + | |
| 1268 | + return $sql; | |
| 1269 | + } | |
| 1270 | + | |
| 1271 | + public function buildSimpleQuery($op, $group) | |
| 1272 | + { | |
| 1273 | +// print "building simple \n\n"; | |
| 1274 | + $this->exploreGroup($group); | |
| 1275 | + | |
| 1276 | + $sql = $this->buildCoreSQL(); | |
| 1277 | + | |
| 1278 | + $offset=0; | |
| 1279 | + foreach($this->db as $expr) | |
| 1280 | + { | |
| 1281 | + if ($offset++) | |
| 1282 | + { | |
| 1283 | + $sql .= " $op\n " ; | |
| 1284 | + } | |
| 1285 | + | |
| 1286 | + $field = $expr->left(); | |
| 1287 | + | |
| 1288 | + if (is_null($field->getJoinTable())) | |
| 1289 | + { | |
| 1290 | + $alias = $this->resolveTableToAlias($field->getTable()); | |
| 1291 | + $fieldname = $alias . '.' . $field->getField(); | |
| 1292 | + } | |
| 1293 | + else | |
| 1294 | + { | |
| 1295 | + $offset = $this->resolveJoinOffset($expr); | |
| 1296 | + $matching = $field->getMatchingField(); | |
| 1297 | + $tablename = $field->getJoinTable(); | |
| 1298 | + $fieldname = "$tablename$offset.$matching"; | |
| 1299 | + } | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + $value = $expr->right(); | |
| 1303 | + $sql .= $value->getSQL($field, $left->modifyName($fieldname), $expr->op(), $expr->not()); | |
| 1304 | + } | |
| 1305 | + | |
| 1306 | + $moffset=0; | |
| 1307 | + foreach($this->metadata as $expr) | |
| 1308 | + { | |
| 1309 | + $moffset++; | |
| 1310 | + if ($offset++) | |
| 1311 | + { | |
| 1312 | + $sql .= " $op\n " ; | |
| 1313 | + } | |
| 1314 | + | |
| 1315 | + $field = $expr->left(); | |
| 1316 | + $value = $expr->right(); | |
| 1317 | + | |
| 1318 | + $sql .= $value->getSQL($field, "dfl$moffset.value", $expr->getOp()); | |
| 1319 | + } | |
| 1320 | + | |
| 1321 | + return $sql; | |
| 1322 | + } | |
| 1323 | + | |
| 1324 | + public function getRanking($result) | |
| 1325 | + { | |
| 1326 | + $ranker = RankManager::get(); | |
| 1327 | + $score = 0; | |
| 1328 | + foreach($result as $col=>$val) | |
| 1329 | + { | |
| 1330 | + if ($val + 0 == 0) | |
| 1331 | + { | |
| 1332 | + // we are not interested if the expression failed | |
| 1333 | + continue; | |
| 1334 | + } | |
| 1335 | + | |
| 1336 | + if (substr($col, 0, 4) == 'expr' && is_numeric(substr($col, 4))) | |
| 1337 | + { | |
| 1338 | + | |
| 1339 | + $exprno = substr($col, 4); | |
| 1340 | + if ($exprno <= count($this->db)) | |
| 1341 | + { | |
| 1342 | + $expr = $this->db[$exprno-1]; | |
| 1343 | + $left=$expr->left(); | |
| 1344 | + $score += $ranker->scoreField($left->getTable(), 'T', $left->getField()); | |
| 1345 | + } | |
| 1346 | + else | |
| 1347 | + { | |
| 1348 | + $exprno -= count($this->db); | |
| 1349 | + $expr = $this->metadata[$exprno-1]; | |
| 1350 | + $left=$expr->left(); | |
| 1351 | + $score += $ranker->scoreField($left->getTable(), 'M', $left->getField()); | |
| 1352 | + } | |
| 1353 | + } | |
| 1354 | + } | |
| 1355 | + | |
| 1356 | + return $score; | |
| 1357 | + } | |
| 1358 | + | |
| 1359 | + public function getResultText($result) | |
| 1360 | + { | |
| 1361 | + $text = array(); | |
| 1362 | + foreach($result as $col=>$val) | |
| 1363 | + { | |
| 1364 | + if (substr($col, 0, 4) == 'expr' && is_numeric(substr($col, 4))) | |
| 1365 | + { | |
| 1366 | + if ($val + 0 == 0) | |
| 1367 | + { | |
| 1368 | + // we are not interested if the expression failed | |
| 1369 | + continue; | |
| 1370 | + } | |
| 1371 | + $exprno = substr($col, 4); | |
| 1372 | + if ($exprno <= count($this->db)) | |
| 1373 | + { | |
| 1374 | + $expr = $this->db[$exprno-1]; | |
| 1375 | + } | |
| 1376 | + else | |
| 1377 | + { | |
| 1378 | + $exprno -= count($this->db); | |
| 1379 | + $expr = $this->metadata[$exprno-1]; | |
| 1380 | + } | |
| 1381 | + $text[] = (string) $expr; | |
| 1382 | + } | |
| 1383 | + } | |
| 1384 | + return '(' . implode(') AND (', $text) . ')'; | |
| 1385 | + } | |
| 1386 | + | |
| 1387 | + | |
| 1388 | +} | |
| 1389 | + | |
| 1390 | + | |
| 1391 | + | |
| 1392 | +class OpExpr extends Expr | |
| 1393 | +{ | |
| 1394 | + /** | |
| 1395 | + * The left side of the expression | |
| 1396 | + * | |
| 1397 | + * @var Expr | |
| 1398 | + */ | |
| 1399 | + protected $left_expr; | |
| 1400 | + | |
| 1401 | + /** | |
| 1402 | + * The operator on the left and right | |
| 1403 | + * | |
| 1404 | + * @var ExprOp | |
| 1405 | + */ | |
| 1406 | + protected $op; | |
| 1407 | + /** | |
| 1408 | + * The right side of the expression | |
| 1409 | + * | |
| 1410 | + * @var Expr | |
| 1411 | + */ | |
| 1412 | + protected $right_expr; | |
| 1413 | + | |
| 1414 | + /** | |
| 1415 | + * This indicates that the expression is negative | |
| 1416 | + * | |
| 1417 | + * @var boolean | |
| 1418 | + */ | |
| 1419 | + protected $not; | |
| 1420 | + | |
| 1421 | + protected $point; | |
| 1422 | + | |
| 1423 | + protected $has_text; | |
| 1424 | + protected $has_db; | |
| 1425 | + | |
| 1426 | + private $debug = false; | |
| 1427 | + | |
| 1428 | +// protected $flattened; | |
| 1429 | + | |
| 1430 | + protected $results; | |
| 1431 | + | |
| 1432 | + public function setResults($results) | |
| 1433 | + { | |
| 1434 | + $this->results=$results; | |
| 1435 | + } | |
| 1436 | + public function getResults() | |
| 1437 | + { | |
| 1438 | + return $this->results; | |
| 1439 | + } | |
| 1440 | + | |
| 1441 | + public function setHasDb($value=true) | |
| 1442 | + { | |
| 1443 | + $this->has_db=$value; | |
| 1444 | + } | |
| 1445 | + | |
| 1446 | + public function setHasText($value=true) | |
| 1447 | + { | |
| 1448 | + $this->has_text=$value; | |
| 1449 | + } | |
| 1450 | + | |
| 1451 | + public function getHasDb() | |
| 1452 | + { | |
| 1453 | + return $this->has_db; | |
| 1454 | + } | |
| 1455 | + public function getHasText() | |
| 1456 | + { | |
| 1457 | + return $this->has_text; | |
| 1458 | + } | |
| 1459 | + public function setPoint($point) | |
| 1460 | + { | |
| 1461 | + $this->point = $point; | |
| 1462 | + /* if (!is_null($point)) | |
| 1463 | + { | |
| 1464 | + $this->flattened = new FlattenedGroup($this); | |
| 1465 | + } | |
| 1466 | + else | |
| 1467 | + { | |
| 1468 | + if (!is_null($this->flattened)) | |
| 1469 | + { | |
| 1470 | + unset($this->flattened); | |
| 1471 | + } | |
| 1472 | + $this->flattened = null; | |
| 1473 | + }*/ | |
| 1474 | + } | |
| 1475 | + | |
| 1476 | + public function getPoint() | |
| 1477 | + { | |
| 1478 | + return $this->point; | |
| 1479 | + } | |
| 1480 | + | |
| 1481 | + public function hasSameOpAs($expr) | |
| 1482 | + { | |
| 1483 | + return $this->op() == $expr->op(); | |
| 1484 | + } | |
| 1485 | + | |
| 1486 | + public static function rewriteString(&$left, &$op, &$right, $not=false) | |
| 1487 | + { | |
| 1488 | + if ($right->isValueExpr()) | |
| 1489 | + { | |
| 1490 | + $value = $right->getValue(); | |
| 1491 | + } | |
| 1492 | + else | |
| 1493 | + { | |
| 1494 | + $value = $right; | |
| 1495 | + } | |
| 1496 | + | |
| 1497 | + $text = array(); | |
| 1498 | + | |
| 1499 | + | |
| 1500 | + preg_match_all('/[\']([^\']*)[\']/',$value, $matches); | |
| 1501 | + | |
| 1502 | + foreach($matches[0] as $item) | |
| 1503 | + { | |
| 1504 | + $text [] = $item; | |
| 1505 | + | |
| 1506 | + $value = str_replace($item, '', $value); | |
| 1507 | + } | |
| 1508 | + | |
| 1509 | + $matches = explode(' ', $value); | |
| 1510 | + | |
| 1511 | + foreach($matches as $item) | |
| 1512 | + { | |
| 1513 | + if (empty($item)) continue; | |
| 1514 | + $text[] = $item; | |
| 1515 | + } | |
| 1516 | + | |
| 1517 | + if (count($text) == 1) | |
| 1518 | + { | |
| 1519 | + return; | |
| 1520 | + } | |
| 1521 | + | |
| 1522 | + $doctext = $left; | |
| 1523 | + | |
| 1524 | + $left = new OpExpr($doctext, $op, new ValueExpr($text[0])); | |
| 1525 | + | |
| 1526 | + for($i=1;$i<count($text);$i++) | |
| 1527 | + { | |
| 1528 | + if ($i==1) | |
| 1529 | + { | |
| 1530 | + $right = new OpExpr($doctext, $op, new ValueExpr($text[$i])); | |
| 1531 | + } | |
| 1532 | + else | |
| 1533 | + { | |
| 1534 | + $join = new OpExpr($doctext, $op, new ValueExpr($text[$i])); | |
| 1535 | + $right = new OpExpr($join, ExprOp::OP_OR, $right); | |
| 1536 | + } | |
| 1537 | + } | |
| 1538 | + | |
| 1539 | + $op = ExprOp::OP_OR; | |
| 1540 | + } | |
| 1541 | + | |
| 1542 | + | |
| 1543 | + /** | |
| 1544 | + * Constructor for the expression | |
| 1545 | + * | |
| 1546 | + * @param Expr $left | |
| 1547 | + * @param ExprOp $op | |
| 1548 | + * @param Expr $right | |
| 1549 | + */ | |
| 1550 | + public function __construct($left, $op, $right, $not = false) | |
| 1551 | + { | |
| 1552 | + // if left is a string, we assume we should convert it to a FieldExpr | |
| 1553 | + if (is_string($left)) | |
| 1554 | + { | |
| 1555 | + $left = new $left; | |
| 1556 | + } | |
| 1557 | + | |
| 1558 | + // if right is not an expression, we must convert it! | |
| 1559 | + if (!($right instanceof Expr)) | |
| 1560 | + { | |
| 1561 | + $right = new ValueExpr($right); | |
| 1562 | + } | |
| 1563 | + | |
| 1564 | + if ($right->isValueListExpr()) | |
| 1565 | + { | |
| 1566 | + $right->rewrite($left, $op, $right, $not); | |
| 1567 | + } | |
| 1568 | + else | |
| 1569 | + // rewriting is based on the FieldExpr, and can expand a simple expression | |
| 1570 | + // into something a little bigger. | |
| 1571 | + if ($left->isFieldExpr()) | |
| 1572 | + { | |
| 1573 | + $left->rewrite($left, $op, $right, $not); | |
| 1574 | + } | |
| 1575 | + | |
| 1576 | + // transformation is required to optimise the expression tree so that | |
| 1577 | + // the queries on the db and full text search are optimised. | |
| 1578 | + if (DefaultOpCollection::isBoolean($op)) | |
| 1579 | + { | |
| 1580 | + $this->transform($left, $op, $right, $not); | |
| 1581 | + } | |
| 1582 | + | |
| 1583 | + parent::__construct(); | |
| 1584 | + | |
| 1585 | + $left->setParent($this); | |
| 1586 | + $right->setParent($this); | |
| 1587 | + $this->left_expr=&$left; | |
| 1588 | + $this->op = $op; | |
| 1589 | + $this->right_expr=&$right; | |
| 1590 | + $this->not = $not; | |
| 1591 | + $this->has_text=false; | |
| 1592 | + | |
| 1593 | + // $this->setPoint('point'); | |
| 1594 | + | |
| 1595 | + if ($left->isSearchableText()) | |
| 1596 | + { | |
| 1597 | + $this->setHasText(); | |
| 1598 | + } | |
| 1599 | + else if ($left->isDBExpr()) | |
| 1600 | + { | |
| 1601 | + $this->setHasDb(); | |
| 1602 | + } | |
| 1603 | + elseif ($left->isOpExpr()) | |
| 1604 | + { | |
| 1605 | + if ($left->getHasText()) { $this->setHasText(); } | |
| 1606 | + if ($left->getHasDb()) { $this->setHasDb(); } | |
| 1607 | + } | |
| 1608 | + | |
| 1609 | + if ($right->isOpExpr()) | |
| 1610 | + { | |
| 1611 | + if ($right->getHasText()) { $this->setHasText(); } | |
| 1612 | + if ($right->getHasDb()) { $this->setHasDb(); } | |
| 1613 | + } | |
| 1614 | + // $this->flattened=null; | |
| 1615 | + | |
| 1616 | + // $left_op, etc indicates that $left expression is a logical expression | |
| 1617 | + $left_op = ($left->isOpExpr() && DefaultOpCollection::isBoolean($left)); | |
| 1618 | + $right_op = ($right->isOpExpr() && DefaultOpCollection::isBoolean($right)); | |
| 1619 | + | |
| 1620 | + // check which trees match | |
| 1621 | + $left_op_match = ($left_op && $this->hasSameOpAs($left)) ; | |
| 1622 | + $right_op_match = ($right_op && $this->hasSameOpAs($left)) ; | |
| 1623 | + | |
| 1624 | + $point = null; | |
| 1625 | + | |
| 1626 | + | |
| 1627 | + if ($left_op_match && $right_op_match) { $point = 'point'; } | |
| 1628 | + | |
| 1629 | + $left_op_match_flex = $left_op_match || ($left->isOpExpr()); | |
| 1630 | + $right_op_match_flex = $right_op_match || ($right->isOpExpr()); | |
| 1631 | + | |
| 1632 | + if ($left_op_match_flex && $right_op_match_flex) { $point = 'point'; } | |
| 1633 | + | |
| 1634 | + if (!is_null($point)) | |
| 1635 | + { | |
| 1636 | + if ($left_op_match && $left->getPoint() == 'point') { $left->setPoint(null); } | |
| 1637 | + if ($right_op_match && $right->getPoint() == 'point') { $right->setPoint(null); } | |
| 1638 | + | |
| 1639 | + if ($left->isMergePoint() && is_null($right->getPoint())) { $right->setPoint('point'); } | |
| 1640 | + if ($right->isMergePoint() && is_null($left->getPoint())) { $left->setPoint('point'); } | |
| 1641 | + | |
| 1642 | + if ($left->isMergePoint() || $right->isMergePoint()) | |
| 1643 | + { | |
| 1644 | + $point = 'merge'; | |
| 1645 | + | |
| 1646 | + if (!$left->isMergePoint()) { $left->setPoint('point'); } | |
| 1647 | + if (!$right->isMergePoint()) { $right->setPoint('point'); } | |
| 1648 | + | |
| 1649 | + if ($this->isDBonly() || $this->isTextOnly()) | |
| 1650 | + { | |
| 1651 | + $this->clearPoint(); | |
| 1652 | + $point = 'point'; | |
| 1653 | + } | |
| 1654 | + } | |
| 1655 | + } | |
| 1656 | + | |
| 1657 | + if ($point == 'point') | |
| 1658 | + { | |
| 1659 | + if ($this->isDBandText()) | |
| 1660 | + { | |
| 1661 | + $point = 'merge'; | |
| 1662 | + $left->setPoint('point'); | |
| 1663 | + $right->setPoint('point'); | |
| 1664 | + } | |
| 1665 | + } | |
| 1666 | + if (is_null($point) && !DefaultOpCollection::isBoolean($op)) | |
| 1667 | + { | |
| 1668 | + $point = 'point'; | |
| 1669 | + } | |
| 1670 | + | |
| 1671 | + $this->setPoint($point); | |
| 1672 | + } | |
| 1673 | + | |
| 1674 | + private function isDBonly() | |
| 1675 | + { | |
| 1676 | + return $this->getHasDb() && !$this->getHasText(); | |
| 1677 | + } | |
| 1678 | + | |
| 1679 | + private function isTextOnly() | |
| 1680 | + { | |
| 1681 | + return !$this->getHasDb() && $this->getHasText(); | |
| 1682 | + } | |
| 1683 | + | |
| 1684 | + private function isDBandText() | |
| 1685 | + { | |
| 1686 | + return $this->getHasDb() && $this->getHasText(); | |
| 1687 | + } | |
| 1688 | + | |
| 1689 | + /** | |
| 1690 | + * Enter description here... | |
| 1691 | + * | |
| 1692 | + * @param OpExpr $expr | |
| 1693 | + */ | |
| 1694 | + protected function clearPoint() | |
| 1695 | + { | |
| 1696 | + if (DefaultOpCollection::isBoolean($this)) | |
| 1697 | + { | |
| 1698 | + $this->left()->clearPoint(); | |
| 1699 | + $this->right()->clearPoint(); | |
| 1700 | + } | |
| 1701 | + if ($this->isMergePoint()) | |
| 1702 | + { | |
| 1703 | + $this->setPoint(null); | |
| 1704 | + } | |
| 1705 | + } | |
| 1706 | + | |
| 1707 | + | |
| 1708 | + protected function isMergePoint() | |
| 1709 | + { | |
| 1710 | + return in_array($this->getPoint(), array('merge','point')); | |
| 1711 | + } | |
| 1712 | + | |
| 1713 | + /** | |
| 1714 | + * Returns the operator on the expression | |
| 1715 | + * | |
| 1716 | + * @return ExprOp | |
| 1717 | + */ | |
| 1718 | + public function op() | |
| 1719 | + { | |
| 1720 | + return $this->op; | |
| 1721 | + } | |
| 1722 | + | |
| 1723 | + /** | |
| 1724 | + * Returns true if the negative of the operator should be used in evaluation | |
| 1725 | + * | |
| 1726 | + * @param boolean $not | |
| 1727 | + * @return boolean | |
| 1728 | + */ | |
| 1729 | + public function not($not=null) | |
| 1730 | + { | |
| 1731 | + if (!is_null($not)) | |
| 1732 | + { | |
| 1733 | + $this->not = $not; | |
| 1734 | + } | |
| 1735 | + | |
| 1736 | + return $this->not; | |
| 1737 | + } | |
| 1738 | + | |
| 1739 | + /** | |
| 1740 | + * The left side of the expression | |
| 1741 | + * | |
| 1742 | + * @return Expr | |
| 1743 | + */ | |
| 1744 | + public function &left() | |
| 1745 | + { | |
| 1746 | + return $this->left_expr; | |
| 1747 | + } | |
| 1748 | + | |
| 1749 | + /** | |
| 1750 | + * The right side of the expression | |
| 1751 | + * | |
| 1752 | + * @return Expr | |
| 1753 | + */ | |
| 1754 | + public function &right() | |
| 1755 | + { | |
| 1756 | + return $this->right_expr; | |
| 1757 | + } | |
| 1758 | + | |
| 1759 | + /** | |
| 1760 | + * Converts the expression to a string | |
| 1761 | + * | |
| 1762 | + * @return string | |
| 1763 | + */ | |
| 1764 | + public function __toString() | |
| 1765 | + { | |
| 1766 | + $expr = $this->left_expr . ' ' . $this->op .' ' . $this->right_expr; | |
| 1767 | + | |
| 1768 | + if (is_null($this->parent)) | |
| 1769 | + { | |
| 1770 | + return $expr; | |
| 1771 | + } | |
| 1772 | + | |
| 1773 | + if ($this->parent->isOpExpr()) | |
| 1774 | + { | |
| 1775 | + if ($this->parent->op != $this->op && in_array($this->op, DefaultOpCollection::$boolean)) | |
| 1776 | + { | |
| 1777 | + $expr = "($expr)"; | |
| 1778 | + } | |
| 1779 | + } | |
| 1780 | + | |
| 1781 | + if ($this->not()) | |
| 1782 | + { | |
| 1783 | + $expr = "!($expr)"; | |
| 1784 | + } | |
| 1785 | + | |
| 1786 | + return $expr; | |
| 1787 | + } | |
| 1788 | + | |
| 1789 | + /** | |
| 1790 | + * Is the expression valid | |
| 1791 | + * | |
| 1792 | + * @return boolean | |
| 1793 | + */ | |
| 1794 | + public function is_valid() | |
| 1795 | + { | |
| 1796 | + $left = $this->left(); | |
| 1797 | + $right = $this->right(); | |
| 1798 | + return $left->is_valid() && $right->is_valid(); | |
| 1799 | + } | |
| 1800 | + | |
| 1801 | + /** | |
| 1802 | + * Finds the results that are in both record sets. | |
| 1803 | + * | |
| 1804 | + * @param array $leftres | |
| 1805 | + * @param array $rightres | |
| 1806 | + * @return array | |
| 1807 | + */ | |
| 1808 | + protected static function intersect($leftres, $rightres) | |
| 1809 | + { | |
| 1810 | + if (empty($leftres) || empty($rightres)) | |
| 1811 | + { | |
| 1812 | + return array(); // small optimisation | |
| 1813 | + } | |
| 1814 | + $result = array(); | |
| 1815 | + foreach($leftres as $item) | |
| 1816 | + { | |
| 1817 | + $document_id = $item->DocumentID; | |
| 1818 | + | |
| 1819 | + if (!$item->IsLive) | |
| 1820 | + { | |
| 1821 | + continue; | |
| 1822 | + } | |
| 1823 | + | |
| 1824 | + if (array_key_exists($document_id, $rightres)) | |
| 1825 | + { | |
| 1826 | + $check = $rightres[$document_id]; | |
| 1827 | + | |
| 1828 | + $result[$document_id] = ($item->Rank < $check->Rank)?$check:$item; | |
| 1829 | + } | |
| 1830 | + } | |
| 1831 | + return $result; | |
| 1832 | + } | |
| 1833 | + | |
| 1834 | + /** | |
| 1835 | + * The objective of this function is to merge the results so that there is a union of the results, | |
| 1836 | + * but there should be no duplicates. | |
| 1837 | + * | |
| 1838 | + * @param array $leftres | |
| 1839 | + * @param array $rightres | |
| 1840 | + * @return array | |
| 1841 | + */ | |
| 1842 | + protected static function union($leftres, $rightres) | |
| 1843 | + { | |
| 1844 | + if (empty($leftres)) | |
| 1845 | + { | |
| 1846 | + return $rightres; // small optimisation | |
| 1847 | + } | |
| 1848 | + if (empty($rightres)) | |
| 1849 | + { | |
| 1850 | + return $leftres; // small optimisation | |
| 1851 | + } | |
| 1852 | + $result = array(); | |
| 1853 | + | |
| 1854 | + foreach($leftres as $item) | |
| 1855 | + { | |
| 1856 | + if ($item->IsLive) | |
| 1857 | + { | |
| 1858 | + $result[$item->DocumentID] = $item; | |
| 1859 | + } | |
| 1860 | + } | |
| 1861 | + | |
| 1862 | + foreach($rightres as $item) | |
| 1863 | + { | |
| 1864 | + if (!array_key_exists($item->DocumentID, $result) || $item->Rank > $result[$item->DocumentID]->Rank) | |
| 1865 | + { | |
| 1866 | + $result[$item->DocumentID] = $item; | |
| 1867 | + } | |
| 1868 | + } | |
| 1869 | + return $result; | |
| 1870 | + } | |
| 1871 | + | |
| 1872 | + /** | |
| 1873 | + * Enter description here... | |
| 1874 | + * | |
| 1875 | + * @param OpExpr $left | |
| 1876 | + * @param ExprOp $op | |
| 1877 | + * @param OpExpr $right | |
| 1878 | + * @param boolean $not | |
| 1879 | + */ | |
| 1880 | + public function transform(& $left, & $op, & $right, & $not) | |
| 1881 | + { | |
| 1882 | + | |
| 1883 | + if (!$left->isOpExpr() || !$right->isOpExpr() || !DefaultOpCollection::isBoolean($op)) | |
| 1884 | + { | |
| 1885 | + return; | |
| 1886 | + } | |
| 1887 | + | |
| 1888 | + if ($left->isTextOnly() && $right->isDBonly()) | |
| 1889 | + { | |
| 1890 | + // we just swap the items around, to ease other transformations | |
| 1891 | + $tmp = $left; | |
| 1892 | + $left = $right; | |
| 1893 | + $right = $tmp; | |
| 1894 | + return; | |
| 1895 | + } | |
| 1896 | + | |
| 1897 | + if ($op != $right->op() || !DefaultOpCollection::isBoolean($right)) | |
| 1898 | + { | |
| 1899 | + return; | |
| 1900 | + } | |
| 1901 | + | |
| 1902 | + if ($op == ExprOp::OP_OR && ($not || $right->not())) | |
| 1903 | + { | |
| 1904 | + // NOTE: we can't transform. e.g. | |
| 1905 | + // db or !(db or txt) => db or !db and !txt | |
| 1906 | + // so nothing to do | |
| 1907 | + | |
| 1908 | + // BUT: db and !(db and txt) => db and !db and !txt | |
| 1909 | + return; | |
| 1910 | + } | |
| 1911 | + | |
| 1912 | + $rightLeft = $right->left(); | |
| 1913 | + $rightRight = $right->right(); | |
| 1914 | + | |
| 1915 | + if ($left->isDBonly() && $rightLeft->isDBonly()) | |
| 1916 | + { | |
| 1917 | + $newLeft = new OpExpr( $left, $op, $rightLeft ); | |
| 1918 | + | |
| 1919 | + $right = $rightRight; | |
| 1920 | + $left = $newLeft; | |
| 1921 | + return; | |
| 1922 | + } | |
| 1923 | + | |
| 1924 | + if ($left->isTextOnly() && $rightRight->isTextOnly()) | |
| 1925 | + { | |
| 1926 | + $newRight = new OpExpr($left, $op, $rightRight); | |
| 1927 | + $left = $rightLeft; | |
| 1928 | + $right = $newRight; | |
| 1929 | + return; | |
| 1930 | + } | |
| 1931 | + | |
| 1932 | + } | |
| 1933 | + | |
| 1934 | + private function findDBNode($start, $op, $what) | |
| 1935 | + { | |
| 1936 | + if ($start->op() != $op) | |
| 1937 | + { | |
| 1938 | + return null; | |
| 1939 | + } | |
| 1940 | + switch($what) | |
| 1941 | + { | |
| 1942 | + case 'db': | |
| 1943 | + if ($start->isDBonly()) | |
| 1944 | + { | |
| 1945 | + return $start; | |
| 1946 | + } | |
| 1947 | + break; | |
| 1948 | + case 'txt': | |
| 1949 | + if ($start->isTextOnly()) | |
| 1950 | + { | |
| 1951 | + return $start; | |
| 1952 | + } | |
| 1953 | + break; | |
| 1954 | + } | |
| 1955 | + $node = $this->findDBNode($start->left(), $op, $what); | |
| 1956 | + if (is_null($left)) | |
| 1957 | + { | |
| 1958 | + $node = $this->findDBNode($start->right(), $op, $what); | |
| 1959 | + } | |
| 1960 | + return $node; | |
| 1961 | + | |
| 1962 | + } | |
| 1963 | + | |
| 1964 | + public function traverse($object, $method, $param) | |
| 1965 | + { | |
| 1966 | + if ($this->isOpExpr()) | |
| 1967 | + { | |
| 1968 | + $object->$method($param); | |
| 1969 | + } | |
| 1970 | + } | |
| 1971 | + | |
| 1972 | + private function exploreItem($item, & $group, $interest) | |
| 1973 | + { | |
| 1974 | + if (($interest == 'db' && $item->getHasDb()) || | |
| 1975 | + ($interest == 'text' && $item->getHasText())) | |
| 1976 | + { | |
| 1977 | + if (in_array($item->op(), array(ExprOp::OP_OR, ExprOp::OP_AND))) | |
| 1978 | + { | |
| 1979 | + $this->exploreItem($item->left(), $group, $interest); | |
| 1980 | + $this->exploreItem($item->right(), $group, $interest); | |
| 1981 | + } | |
| 1982 | + else | |
| 1983 | + { | |
| 1984 | + $group[] = $item; | |
| 1985 | + } | |
| 1986 | + } | |
| 1987 | + } | |
| 1988 | + | |
| 1989 | + private function explore($left, $right, & $group, $interest) | |
| 1990 | + { | |
| 1991 | + $this->exploreItem($left, $group, $interest); | |
| 1992 | + $this->exploreItem($right, $group, $interest); | |
| 1993 | + } | |
| 1994 | + | |
| 1995 | + private function exec_db_query($op, $group) | |
| 1996 | + { | |
| 1997 | + if (empty($group)) { return array(); } | |
| 1998 | + | |
| 1999 | + $exprbuilder = new SQLQueryBuilder(); | |
| 2000 | + | |
| 2001 | + if (count($group) == 1) | |
| 2002 | + { | |
| 2003 | + $sql = $exprbuilder->buildComplexQuery($group[0]); | |
| 2004 | + } | |
| 2005 | + else | |
| 2006 | + { | |
| 2007 | + $sql = $exprbuilder->buildSimpleQuery($op, $group); | |
| 2008 | + } | |
| 2009 | + | |
| 2010 | + $results = array(); | |
| 2011 | + | |
| 2012 | + if ($this->debug) print "\n\n$sql\n\n"; | |
| 2013 | + $rs = DBUtil::getResultArray($sql); | |
| 2014 | + | |
| 2015 | + if (PEAR::isError($rs)) | |
| 2016 | + { | |
| 2017 | + throw new Exception($rs->getMessage()); | |
| 2018 | + } | |
| 2019 | + | |
| 2020 | + foreach($rs as $item) | |
| 2021 | + { | |
| 2022 | + $document_id = $item['id']; | |
| 2023 | + $rank = $exprbuilder->getRanking($item); | |
| 2024 | + if (!array_key_exists($document_id, $results) || $rank > $results[$document_id]->Rank) | |
| 2025 | + { | |
| 2026 | + $results[$document_id] = new MatchResult($document_id, $rank, $item['title'], $exprbuilder->getResultText($item)); | |
| 2027 | + } | |
| 2028 | + } | |
| 2029 | + | |
| 2030 | + return $results; | |
| 2031 | + | |
| 2032 | + } | |
| 2033 | + | |
| 2034 | + private function exec_text_query($op, $group) | |
| 2035 | + { | |
| 2036 | + if (empty($group)) { return array(); } | |
| 2037 | + | |
| 2038 | + $exprbuilder = new TextQueryBuilder(); | |
| 2039 | + | |
| 2040 | + if (count($group) == 1) | |
| 2041 | + { | |
| 2042 | + $query = $exprbuilder->buildComplexQuery($group[0]); | |
| 2043 | + } | |
| 2044 | + else | |
| 2045 | + { | |
| 2046 | + $query = $exprbuilder->buildSimpleQuery($op, $group); | |
| 2047 | + } | |
| 2048 | + | |
| 2049 | + $indexer = Indexer::get(); | |
| 2050 | + if ($this->debug) print "\n\n$query\n\n"; | |
| 2051 | + $results = $indexer->query($query); | |
| 2052 | + foreach($results as $item) | |
| 2053 | + { | |
| 2054 | + $item->Rank = $exprbuilder->getRanking($item); | |
| 2055 | + $exprbuilder->setQuery($query); | |
| 2056 | + $item->Text = $exprbuilder->getResultText($item); | |
| 2057 | + } | |
| 2058 | + | |
| 2059 | + return $results; | |
| 2060 | + | |
| 2061 | + | |
| 2062 | + } | |
| 2063 | + | |
| 2064 | + public function evaluate() | |
| 2065 | + { | |
| 2066 | + $left = $this->left(); | |
| 2067 | + $right = $this->right(); | |
| 2068 | + $op = $this->op(); | |
| 2069 | + $point = $this->getPoint(); | |
| 2070 | + $result = array(); | |
| 2071 | + if (empty($point)) | |
| 2072 | + { | |
| 2073 | + $point = 'point'; | |
| 2074 | + } | |
| 2075 | + | |
| 2076 | + if ($point == 'merge') | |
| 2077 | + { | |
| 2078 | + | |
| 2079 | + $leftres = $left->evaluate(); | |
| 2080 | + $rightres = $right->evaluate(); | |
| 2081 | + switch ($op) | |
| 2082 | + { | |
| 2083 | + case ExprOp::OP_AND: | |
| 2084 | + if ($this->debug) print "\n\nmerge: intersect\n\n"; | |
| 2085 | + $result = OpExpr::intersect($leftres, $rightres); | |
| 2086 | + break; | |
| 2087 | + case ExprOp::OP_OR: | |
| 2088 | + if ($this->debug) print "\n\nmerge: union\n\n"; | |
| 2089 | + $result = OpExpr::union($leftres, $rightres); | |
| 2090 | + break; | |
| 2091 | + default: | |
| 2092 | + throw new Exception("this condition should not happen"); | |
| 2093 | + } | |
| 2094 | + } | |
| 2095 | + elseif ($point == 'point') | |
| 2096 | + { | |
| 2097 | + if ($this->isDBonly()) | |
| 2098 | + { | |
| 2099 | + $result = $this->exec_db_query($op, array($this)); | |
| 2100 | + } | |
| 2101 | + elseif ($this->isTextOnly()) | |
| 2102 | + { | |
| 2103 | + $result = $this->exec_text_query($op, array($this)); | |
| 2104 | + } | |
| 2105 | + elseif (in_array($op, array(ExprOp::OP_OR, ExprOp::OP_AND))) | |
| 2106 | + { | |
| 2107 | + $db_group = array(); | |
| 2108 | + $text_group = array(); | |
| 2109 | + $this->explore($left, $right, $db_group, 'db'); | |
| 2110 | + $this->explore($left, $right, $text_group, 'text'); | |
| 2111 | + | |
| 2112 | + $db_result = $this->exec_db_query($op, $db_group); | |
| 2113 | + $text_result = $this->exec_text_query($op, $text_group); | |
| 2114 | + | |
| 2115 | + switch ($op) | |
| 2116 | + { | |
| 2117 | + case ExprOp::OP_AND: | |
| 2118 | + if ($this->debug) print "\n\npoint: intersect\n\n"; | |
| 2119 | + $result = OpExpr::intersect($db_result, $text_result); | |
| 2120 | + break; | |
| 2121 | + case ExprOp::OP_OR: | |
| 2122 | + if ($this->debug) print "\n\nmerge: union\n\n"; | |
| 2123 | + $result = OpExpr::union($db_result, $text_result); | |
| 2124 | + break; | |
| 2125 | + default: | |
| 2126 | + throw new Exception('how did this happen??'); | |
| 2127 | + } | |
| 2128 | + } | |
| 2129 | + else | |
| 2130 | + { | |
| 2131 | + throw new Exception('and this?'); | |
| 2132 | + } | |
| 2133 | + } | |
| 2134 | + else | |
| 2135 | + { | |
| 2136 | + // we don't have to do anything | |
| 2137 | + //throw new Exception('Is this reached ever?'); | |
| 2138 | + } | |
| 2139 | + | |
| 2140 | + $permResults = array(); | |
| 2141 | + foreach($result as $idx=>$item) | |
| 2142 | + { | |
| 2143 | + $doc = Document::get($item->DocumentID); | |
| 2144 | + if (Permission::userHasDocumentReadPermission($doc)) | |
| 2145 | + { | |
| 2146 | + $permResults[$idx] = $item; | |
| 2147 | + } | |
| 2148 | + } | |
| 2149 | + | |
| 2150 | + return $permResults; | |
| 2151 | + } | |
| 2152 | + | |
| 2153 | + public function toViz(&$str, $phase) | |
| 2154 | + { | |
| 2155 | + $expr_id = $this->getExprId(); | |
| 2156 | + $left = $this->left(); | |
| 2157 | + $right = $this->right(); | |
| 2158 | + $hastext = $this->getHasText()?'TEXT':''; | |
| 2159 | + $hasdb = $this->getHasDb()?'DB':''; | |
| 2160 | + switch ($phase) | |
| 2161 | + { | |
| 2162 | + case 0: | |
| 2163 | + $not = $this->not()?'NOT':''; | |
| 2164 | + $str .= "struct$expr_id [style=box, label=\"$expr_id: $not $this->op $this->point $hastext$hasdb\"]\n"; | |
| 2165 | + break; | |
| 2166 | + case 1: | |
| 2167 | + $left_id = $left->getExprId(); | |
| 2168 | + $str .= "struct$expr_id -> struct$left_id\n"; | |
| 2169 | + $right_id = $right->getExprId(); | |
| 2170 | + $str .= "struct$expr_id -> struct$right_id\n"; | |
| 2171 | + break; | |
| 2172 | + } | |
| 2173 | + $left->toViz($str, $phase); | |
| 2174 | + $right->toViz($str, $phase); | |
| 2175 | + } | |
| 2176 | + | |
| 2177 | +} | |
| 2178 | + | |
| 2179 | + | |
| 2180 | + | |
| 2181 | + | |
| 2182 | +?> | |
| 0 | 2183 | \ No newline at end of file | ... | ... |
search2/search/exprConstants.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class ExprOp | |
| 4 | +{ | |
| 5 | + const IS = 'is'; | |
| 6 | + const CONTAINS = 'contains'; | |
| 7 | + const BETWEEN = 'between'; | |
| 8 | + const STARTS_WITH = 'start with'; | |
| 9 | + const ENDS_WITH = 'ends with'; | |
| 10 | + const LIKE = 'like'; | |
| 11 | + const LESS_THAN = '<'; | |
| 12 | + const GREATER_THAN = '>'; | |
| 13 | + const LESS_THAN_EQUAL = '<='; | |
| 14 | + const GREATER_THAN_EQUAL = '>='; | |
| 15 | + const OP_AND = 'AND'; | |
| 16 | + const OP_OR = 'OR'; | |
| 17 | + const IS_NOT = 'is not'; | |
| 18 | + | |
| 19 | +} | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * This is a collection of various operators that may be used | |
| 25 | + */ | |
| 26 | +class DefaultOpCollection | |
| 27 | +{ | |
| 28 | + public static $is = array(ExprOp::IS); | |
| 29 | + public static $contains = array(ExprOp::CONTAINS, ExprOp::STARTS_WITH , ExprOp::ENDS_WITH ); | |
| 30 | + public static $between = array(ExprOp::BETWEEN); | |
| 31 | + public static $boolean = array(ExprOp::OP_OR , ExprOp::OP_AND ); | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * Validates if the operator on the expression's parent is allowed | |
| 35 | + * | |
| 36 | + * @param Expr $expr | |
| 37 | + * @param array $collection | |
| 38 | + * @return boolean | |
| 39 | + */ | |
| 40 | + public static function validateParent(&$expr, &$collection) | |
| 41 | + { | |
| 42 | + $parent = $expr->getParent(); | |
| 43 | + if ($parent instanceof OpExpr) | |
| 44 | + { | |
| 45 | + return in_array($parent->op(), $collection); | |
| 46 | + } | |
| 47 | + return false; | |
| 48 | + } | |
| 49 | + | |
| 50 | + public static function validate(&$expr, &$collection) | |
| 51 | + { | |
| 52 | + if ($expr instanceof OpExpr) | |
| 53 | + { | |
| 54 | + return in_array($expr->op(), $collection); | |
| 55 | + } | |
| 56 | + return false; | |
| 57 | + } | |
| 58 | + | |
| 59 | + public static function isBoolean(&$expr) | |
| 60 | + { | |
| 61 | + if ($expr instanceof OpExpr) | |
| 62 | + { | |
| 63 | + return in_array($expr->op(), DefaultOpCollection::$boolean); | |
| 64 | + } | |
| 65 | + elseif(is_string($expr)) | |
| 66 | + { | |
| 67 | + return in_array($expr, DefaultOpCollection::$boolean); | |
| 68 | + } | |
| 69 | + return false; | |
| 70 | + } | |
| 71 | +} | |
| 72 | + | |
| 73 | +class FieldInputType | |
| 74 | +{ | |
| 75 | + const TEXT = 'STRING'; | |
| 76 | + const INT = 'INT'; | |
| 77 | + const REAL = 'FLOAT'; | |
| 78 | + const BOOLEAN = 'BOOL'; | |
| 79 | + const USER_LIST = 'USERLIST'; | |
| 80 | + const DATE = 'DATE'; | |
| 81 | + const MIME_TYPES = 'MIMETYPES'; | |
| 82 | + const DOCUMENT_TYPES = 'DOCTYPES'; | |
| 83 | + const DATEDIFF = 'DATEDIFF'; | |
| 84 | + const FULLTEXT = 'FULLTEXT'; | |
| 85 | + const FILESIZE = 'FILESIZE'; | |
| 86 | +} | |
| 87 | + | |
| 88 | +?> | |
| 0 | 89 | \ No newline at end of file | ... | ... |
search2/search/fieldRegistry.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +require_once('search/expr.inc.php'); | |
| 4 | + | |
| 5 | +class ResolutionException extends Exception {} | |
| 6 | + | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * This is the primary registry for fields. | |
| 10 | + * | |
| 11 | + */ | |
| 12 | +class ExprFieldRegistry | |
| 13 | +{ | |
| 14 | + /** | |
| 15 | + * Stores all registered fields. | |
| 16 | + * | |
| 17 | + * @var array | |
| 18 | + */ | |
| 19 | + private $fields; | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * Stores all registered aliases. | |
| 23 | + * | |
| 24 | + * @var array | |
| 25 | + */ | |
| 26 | + private $alias; | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * Path to location of class definitions | |
| 30 | + * | |
| 31 | + * @var string | |
| 32 | + */ | |
| 33 | + private $path; | |
| 34 | + | |
| 35 | + private $metadata; | |
| 36 | + | |
| 37 | + private $display; | |
| 38 | + | |
| 39 | + /** | |
| 40 | + * Initialise the registry. | |
| 41 | + * This is private and class must be obtained via the get() method. | |
| 42 | + * | |
| 43 | + * @access private | |
| 44 | + * | |
| 45 | + */ | |
| 46 | + private function __construct() | |
| 47 | + { | |
| 48 | + $this->fields = array(); | |
| 49 | + $this->alias = array(); | |
| 50 | + $this->metadata = array(); | |
| 51 | + $this->display=array(); | |
| 52 | + | |
| 53 | + $config = KTConfig::getSingleton(); | |
| 54 | + | |
| 55 | + $this->path = $config->get('search/fieldsPath'); | |
| 56 | + } | |
| 57 | + | |
| 58 | + /** | |
| 59 | + * Retuns a singleton to the class. | |
| 60 | + * | |
| 61 | + * @return ExprFieldRegistry | |
| 62 | + */ | |
| 63 | + public static function getRegistry() | |
| 64 | + { | |
| 65 | + static $singleton = null; | |
| 66 | + | |
| 67 | + if (is_null($singleton)) | |
| 68 | + { | |
| 69 | + $singleton = new ExprFieldRegistry(); | |
| 70 | + $singleton->registerFields(); | |
| 71 | + } | |
| 72 | + | |
| 73 | + return $singleton; | |
| 74 | + } | |
| 75 | + | |
| 76 | + /** | |
| 77 | + * Add a field to the registry. | |
| 78 | + * | |
| 79 | + * @param FieldExpr $field | |
| 80 | + */ | |
| 81 | + private function registerField($field) | |
| 82 | + { | |
| 83 | + assert(!is_null($field)); | |
| 84 | + $classname = strtolower(get_class($field)); | |
| 85 | + $alias = strtolower($field->getAlias()); | |
| 86 | + | |
| 87 | + if (array_key_exists($classname, $this->fields) || array_key_exists($alias, $this->alias)) | |
| 88 | + { | |
| 89 | + throw new ResolutionException("Class $classname with alias $alias already registered."); | |
| 90 | + } | |
| 91 | + | |
| 92 | + $this->fields[$classname] = $field; | |
| 93 | + $this->alias[$alias] = $field; | |
| 94 | + | |
| 95 | + if ($field instanceof MetadataField ) | |
| 96 | + { | |
| 97 | + $fieldsetn = $field->getFieldSet(); | |
| 98 | + $fieldn= $field->getField(); | |
| 99 | + $this->metadata[$fieldsetn][$fieldn] = $field; | |
| 100 | + $this->display[] = "[\"$fieldsetn\"][\"$fieldn\"]"; | |
| 101 | + } | |
| 102 | + else | |
| 103 | + { | |
| 104 | + $this->display[] = $field->getAlias(); | |
| 105 | + } | |
| 106 | + | |
| 107 | + } | |
| 108 | + | |
| 109 | + public function resolveAlias($alias) | |
| 110 | + { | |
| 111 | + return $this->getField($alias); | |
| 112 | + } | |
| 113 | + | |
| 114 | + public function resolveMetadataField($fieldset, $field) | |
| 115 | + { | |
| 116 | + if (!array_key_exists($fieldset,$this->metadata)) | |
| 117 | + { | |
| 118 | + throw new ResolutionException("Metadata class for fieldset '$fieldset' and field '$field' not found."); | |
| 119 | + } | |
| 120 | + if (!array_key_exists($field,$this->metadata[$fieldset])) | |
| 121 | + { | |
| 122 | + throw new ResolutionException("Metadata class for fieldset '$fieldset' and field '$field' not found."); | |
| 123 | + } | |
| 124 | + return $this->metadata[$fieldset][$field]; | |
| 125 | + } | |
| 126 | + | |
| 127 | + | |
| 128 | + /** | |
| 129 | + * A static method to lookup a field by fieldname. | |
| 130 | + * | |
| 131 | + * @param string $fieldname | |
| 132 | + * @return unknown | |
| 133 | + */ | |
| 134 | + public static function lookupField($fieldname) | |
| 135 | + { | |
| 136 | + $registry = ExprFieldRegistry::get(); | |
| 137 | + return $registry->getField($fieldname); | |
| 138 | + } | |
| 139 | + | |
| 140 | + /** | |
| 141 | + * Returns a field from the registry. | |
| 142 | + * | |
| 143 | + * @param string $fieldname | |
| 144 | + * @return ExprField | |
| 145 | + */ | |
| 146 | + public function getField($fieldname) | |
| 147 | + { | |
| 148 | + $fieldname = strtolower($fieldname); | |
| 149 | + if (array_key_exists($fieldname, $this->fields)) | |
| 150 | + { | |
| 151 | + return $this->fields[$fieldname]; | |
| 152 | + } | |
| 153 | + if (array_key_exists($fieldname, $this->alias)) | |
| 154 | + { | |
| 155 | + return $this->alias[$fieldname]; | |
| 156 | + } | |
| 157 | + throw new ResolutionException('Field not found: ' . $fieldname); | |
| 158 | + } | |
| 159 | + | |
| 160 | + public function getAliasNames() | |
| 161 | + { | |
| 162 | + return $this->display; | |
| 163 | + } | |
| 164 | + | |
| 165 | + /** | |
| 166 | + * Load all fields into the registry | |
| 167 | + * | |
| 168 | + */ | |
| 169 | + public function registerFields() | |
| 170 | + { | |
| 171 | + $this->fields = array(); | |
| 172 | + | |
| 173 | + $dir = opendir($this->path); | |
| 174 | + while (($file = readdir($dir)) !== false) | |
| 175 | + { | |
| 176 | + if (substr($file,-13) == 'Field.inc.php') | |
| 177 | + { | |
| 178 | + require_once($this->path . '/' . $file); | |
| 179 | + $class = substr($file, 0, -8); | |
| 180 | + | |
| 181 | + if (!class_exists($class)) | |
| 182 | + { | |
| 183 | + continue; | |
| 184 | + } | |
| 185 | + | |
| 186 | + $field = new $class; | |
| 187 | + if (is_null($field) || !($field instanceof FieldExpr)) | |
| 188 | + { | |
| 189 | + continue; | |
| 190 | + } | |
| 191 | + | |
| 192 | + $this->registerField($field); | |
| 193 | + } | |
| 194 | + } | |
| 195 | + closedir($dir); | |
| 196 | + | |
| 197 | + $this->registerMetdataFields(); | |
| 198 | + } | |
| 199 | + | |
| 200 | + /** | |
| 201 | + * Registers metdata fields in system. | |
| 202 | + * | |
| 203 | + */ | |
| 204 | + private function registerMetdataFields() | |
| 205 | + { | |
| 206 | + $sql = "SELECT | |
| 207 | + fs.name as fieldset, f.name as field, fs.id as fsid, f.id as fid | |
| 208 | + FROM | |
| 209 | + fieldsets fs | |
| 210 | + INNER JOIN document_fields f ON f.parent_fieldset=fs.id | |
| 211 | + WHERE | |
| 212 | + fs.disabled=0"; | |
| 213 | + $result = DBUtil::getResultArray($sql); | |
| 214 | + | |
| 215 | + foreach($result as $record) | |
| 216 | + { | |
| 217 | + $fieldset = $record['fieldset']; | |
| 218 | + $field = $record['field']; | |
| 219 | + $fieldsetid = $record['fsid']; | |
| 220 | + $fieldid = $record['fid']; | |
| 221 | + $classname = "MetadataField$fieldid"; | |
| 222 | + | |
| 223 | + $classdefn = " | |
| 224 | + class $classname extends MetadataField | |
| 225 | + { | |
| 226 | + public function __construct() | |
| 227 | + { | |
| 228 | + parent::__construct('$fieldset','$field',$fieldsetid, $fieldid); | |
| 229 | + } | |
| 230 | + }"; | |
| 231 | + eval($classdefn); | |
| 232 | + | |
| 233 | + $field = new $classname; | |
| 234 | + $this->registerField($field); | |
| 235 | + } | |
| 236 | + } | |
| 237 | + | |
| 238 | + public function getFields() | |
| 239 | + { | |
| 240 | + $result = array(); | |
| 241 | + foreach($this->fields as $field) | |
| 242 | + { | |
| 243 | + if ($field instanceof MetadataField) | |
| 244 | + { | |
| 245 | + continue; | |
| 246 | + } | |
| 247 | + $result[] = $field; | |
| 248 | + } | |
| 249 | + return $result; | |
| 250 | + } | |
| 251 | + | |
| 252 | +} | |
| 253 | + | |
| 254 | +?> | |
| 0 | 255 | \ No newline at end of file | ... | ... |
search2/search/fields/AnyMetadataField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class AnyMetadataField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('id', 'document_fields_link', 'Any Metadata'); | |
| 8 | + $this->setAlias('Metadata'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +?> | |
| 0 | 23 | \ No newline at end of file | ... | ... |
search2/search/fields/CheckedOutByField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class CheckedOutByField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('checked_out_user_id', 'documents', 'Checked Out By'); | |
| 8 | + $this->setAlias('CheckedOutBy'); | |
| 9 | + $this->joinTo('users', 'id'); | |
| 10 | + $this->matchField('name'); | |
| 11 | + } | |
| 12 | + | |
| 13 | + public function getInputRequirements() | |
| 14 | + { | |
| 15 | + return array('value'=>array('type'=>FieldInputType::USER_LIST)); | |
| 16 | + } | |
| 17 | + | |
| 18 | + public function is_valid() | |
| 19 | + { | |
| 20 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | +?> | |
| 0 | 25 | \ No newline at end of file | ... | ... |
search2/search/fields/CheckedOutDeltaField.inc.php
0 → 100644
| 1 | +<?php | |
| 2 | + | |
| 3 | +class CheckedOutDeltaField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + private $modifiedName; | |
| 6 | + | |
| 7 | + public function __construct() | |
| 8 | + { | |
| 9 | + parent::__construct('checkedout', 'documents', 'Checked Out Delta'); | |
| 10 | + $this->setAlias('CheckedoutDelta'); | |
| 11 | + $this->isValueQuoted(false); | |
| 12 | + } | |
| 13 | + | |
| 14 | + public function modifyName($sql) | |
| 15 | + { | |
| 16 | + $this->modifiedName = $sql; | |
| 17 | + $now = date('Y-m-d'); | |
| 18 | + | |
| 19 | + | |
| 20 | + return "cast('$now' as date)"; | |
| 21 | + } | |
| 22 | + | |
| 23 | + | |
| 24 | + public function modifyValue($value) | |
| 25 | + { | |
| 26 | + | |
| 27 | + return "cast($this->modifiedName + $value as date)"; | |
| 28 | + } | |
| 29 | + | |
| 30 | + public function getInputRequirements() | |
| 31 | + { | |
| 32 | + return array('value'=>array('type'=>FieldInputType::DATEDIFF)); | |
| 33 | + } | |
| 34 | + | |
| 35 | + public function is_valid() | |
| 36 | + { | |
| 37 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$between); | |
| 38 | + } | |
| 39 | +} | |
| 40 | + | |
| 41 | +?> | |
| 0 | 42 | \ No newline at end of file | ... | ... |
search2/search/fields/CheckedOutField.inc.php
0 → 100644
| 1 | +<?php | |
| 2 | + | |
| 3 | +class CheckedOutField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('checkedout', 'documents', 'Checked Out'); | |
| 8 | + $this->setAlias('CheckedOut'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function modifyName($sql) | |
| 12 | + { | |
| 13 | + return "cast($sql as date)"; | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function getInputRequirements() | |
| 17 | + { | |
| 18 | + return array('value'=>array('type'=>FieldInputType::DATE)); | |
| 19 | + } | |
| 20 | + | |
| 21 | + public function is_valid() | |
| 22 | + { | |
| 23 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$between); | |
| 24 | + } | |
| 25 | +} | |
| 26 | + | |
| 27 | +?> | |
| 0 | 28 | \ No newline at end of file | ... | ... |
search2/search/fields/CreatedByField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class CreatedByField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('creator_id', 'documents','Created By'); | |
| 8 | + $this->setAlias('CreatedBy'); | |
| 9 | + $this->joinTo('users', 'id'); | |
| 10 | + $this->matchField('name'); | |
| 11 | + } | |
| 12 | + | |
| 13 | + public function getInputRequirements() | |
| 14 | + { | |
| 15 | + return array('value'=>array('type'=>FieldInputType::USER_LIST)); | |
| 16 | + } | |
| 17 | + | |
| 18 | + public function is_valid() | |
| 19 | + { | |
| 20 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | +?> | |
| 0 | 25 | \ No newline at end of file | ... | ... |
search2/search/fields/CreatedDeltaField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class CreatedDeltaField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + private $modifiedName; | |
| 6 | + | |
| 7 | + public function __construct() | |
| 8 | + { | |
| 9 | + parent::__construct('created', 'documents', 'Created Delta'); | |
| 10 | + $this->setAlias('CreatedDelta'); | |
| 11 | + $this->isValueQuoted(false); | |
| 12 | + } | |
| 13 | + | |
| 14 | + public function modifyName($sql) | |
| 15 | + { | |
| 16 | + $this->modifiedName = $sql; | |
| 17 | + $now = date('Y-m-d'); | |
| 18 | + | |
| 19 | + | |
| 20 | + return "cast('$now' as date)"; | |
| 21 | + } | |
| 22 | + | |
| 23 | + | |
| 24 | + public function modifyValue($value) | |
| 25 | + { | |
| 26 | + | |
| 27 | + return "cast($this->modifiedName + $value as date)"; | |
| 28 | + } | |
| 29 | + | |
| 30 | + public function getInputRequirements() | |
| 31 | + { | |
| 32 | + return array('value'=>array('type'=>FieldInputType::DATEDIFF)); | |
| 33 | + } | |
| 34 | + | |
| 35 | + public function is_valid() | |
| 36 | + { | |
| 37 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$between); | |
| 38 | + } | |
| 39 | +} | |
| 40 | + | |
| 41 | +?> | |
| 0 | 42 | \ No newline at end of file | ... | ... |
search2/search/fields/CreatedField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class CreatedField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('created', 'documents', 'Created'); | |
| 8 | + $this->setAlias('Created'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function modifyName($sql) | |
| 12 | + { | |
| 13 | + return "cast($sql as date)"; | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function getInputRequirements() | |
| 17 | + { | |
| 18 | + return array('value'=>array('type'=>FieldInputType::DATE)); | |
| 19 | + } | |
| 20 | + | |
| 21 | + public function is_valid() | |
| 22 | + { | |
| 23 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$between); | |
| 24 | + } | |
| 25 | +} | |
| 26 | + | |
| 27 | +?> | |
| 0 | 28 | \ No newline at end of file | ... | ... |
search2/search/fields/DiscussionTextField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | + | |
| 4 | +class DiscussionTextField extends SearchableText | |
| 5 | +{ | |
| 6 | + public function __construct() | |
| 7 | + { | |
| 8 | + parent::__construct('Discussion', 'Discussion Text'); | |
| 9 | + $this->setAlias('DiscussionText'); | |
| 10 | + } | |
| 11 | + | |
| 12 | + public function getInputRequirements() | |
| 13 | + { | |
| 14 | + return array('value'=>array('type'=>FieldInputType::FULLTEXT)); | |
| 15 | + } | |
| 16 | + | |
| 17 | + public function is_valid() | |
| 18 | + { | |
| 19 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$contains); | |
| 20 | + } | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | +} | |
| 25 | + | |
| 26 | +?> | |
| 0 | 27 | \ No newline at end of file | ... | ... |
search2/search/fields/DocumentIdField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class DocumentIdField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('id', 'documents', 'Document ID'); | |
| 8 | + $this->setAlias('DocumentId'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::INT)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +?> | |
| 0 | 23 | \ No newline at end of file | ... | ... |
search2/search/fields/DocumentTextField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | + | |
| 4 | +class DocumentTextField extends SearchableText | |
| 5 | +{ | |
| 6 | + public function __construct() | |
| 7 | + { | |
| 8 | + parent::__construct('Content', 'Document Text'); | |
| 9 | + $this->setAlias('DocumentText'); | |
| 10 | + } | |
| 11 | + | |
| 12 | + public function getInputRequirements() | |
| 13 | + { | |
| 14 | + return array('value'=>array('type'=>FieldInputType::FULLTEXT)); | |
| 15 | + } | |
| 16 | + | |
| 17 | + public function is_valid() | |
| 18 | + { | |
| 19 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$contains); | |
| 20 | + } | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | +} | |
| 28 | + | |
| 29 | +?> | |
| 0 | 30 | \ No newline at end of file | ... | ... |
search2/search/fields/DocumentTypeField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class DocumentTypeField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('document_type_id', 'document_metadata_version', 'Document Type'); | |
| 8 | + $this->setAlias('DocumentType'); | |
| 9 | + $this->joinTo('document_types_lookup', 'id'); | |
| 10 | + $this->matchField('name'); | |
| 11 | + } | |
| 12 | + | |
| 13 | + public function getInputRequirements() | |
| 14 | + { | |
| 15 | + return array('value'=>array('type'=>FieldInputType::DOCUMENT_TYPES)); | |
| 16 | + } | |
| 17 | + | |
| 18 | + public function is_valid() | |
| 19 | + { | |
| 20 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | +?> | |
| 0 | 25 | \ No newline at end of file | ... | ... |
search2/search/fields/FilenameField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class FilenameField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('filename', 'document_content_version', 'Filename'); | |
| 8 | + $this->setAlias('Filename'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +?> | |
| 0 | 23 | \ No newline at end of file | ... | ... |
search2/search/fields/FilesizeField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class FilesizeField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('size', 'document_content_version', 'Filesize'); | |
| 8 | + $this->setAlias('Filesize'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::FILESIZE)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +?> | |
| 0 | 23 | \ No newline at end of file | ... | ... |
search2/search/fields/FolderField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class FolderField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('folder_id', 'documents', 'Folder'); | |
| 8 | + $this->setAlias('Folder'); | |
| 9 | + $this->joinTo('folders', 'id'); | |
| 10 | + $this->matchField('full_path'); | |
| 11 | + } | |
| 12 | + | |
| 13 | + public function getInputRequirements() | |
| 14 | + { | |
| 15 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 16 | + } | |
| 17 | + | |
| 18 | + public function is_valid() | |
| 19 | + { | |
| 20 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | +?> | |
| 0 | 25 | \ No newline at end of file | ... | ... |
search2/search/fields/FolderFieldID.inc.php
0 → 100644
| 1 | +<?php | |
| 2 | + | |
| 3 | +class FolderIDField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('folder_id', 'documents', 'Folder ID'); | |
| 8 | + $this->setAlias('FolderID'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::INT)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +?> | |
| 0 | 23 | \ No newline at end of file | ... | ... |
search2/search/fields/GeneralTextField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class GeneralTextField extends SearchableText | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('General', 'General Text'); | |
| 8 | + $this->setAlias('GeneralText'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$contains); | |
| 19 | + } | |
| 20 | + | |
| 21 | + | |
| 22 | + public function rewrite(&$left, &$op, &$right) | |
| 23 | + { | |
| 24 | + // note the grouping of the db queries | |
| 25 | + | |
| 26 | + $left = new OpExpr(new DocumentTextField(), ExprOp::CONTAINS, $right); | |
| 27 | + | |
| 28 | + $op = ExprOp::OP_OR; | |
| 29 | + | |
| 30 | + $right = new OpExpr( | |
| 31 | + new OpExpr(new FilenameField(), ExprOp::CONTAINS, $right), | |
| 32 | + ExprOp::OP_OR, | |
| 33 | + new OpExpr( | |
| 34 | + new OpExpr( | |
| 35 | + new TitleField(), ExprOp::CONTAINS, $right), | |
| 36 | + ExprOp::OP_OR, | |
| 37 | + new OpExpr(new AnyMetadataField(), ExprOp::CONTAINS, $right) | |
| 38 | + ) | |
| 39 | + ); | |
| 40 | + } | |
| 41 | + | |
| 42 | + | |
| 43 | +} | |
| 44 | + | |
| 45 | +?> | |
| 0 | 46 | \ No newline at end of file | ... | ... |
search2/search/fields/IsCheckedOutField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class IsCheckedOutField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('is_checked_out', 'documents','Is Checked Out'); | |
| 8 | + $this->setAlias('IsCheckedOut'); | |
| 9 | + $this->isValueQuoted(false); | |
| 10 | + } | |
| 11 | + | |
| 12 | + public function modifyValue($value) | |
| 13 | + { | |
| 14 | + if (is_numeric($value)) | |
| 15 | + { | |
| 16 | + $value = ($value+0)?1:0; | |
| 17 | + } | |
| 18 | + else | |
| 19 | + { | |
| 20 | + switch(strtolower($value)) | |
| 21 | + { | |
| 22 | + case 'true': | |
| 23 | + case 'yes': | |
| 24 | + $value=1; | |
| 25 | + break; | |
| 26 | + default: | |
| 27 | + $value=0; | |
| 28 | + } | |
| 29 | + } | |
| 30 | + return $value; | |
| 31 | + } | |
| 32 | + | |
| 33 | + public function getInputRequirements() | |
| 34 | + { | |
| 35 | + return array('value'=>array('type'=>FieldInputType::BOOLEAN)); | |
| 36 | + } | |
| 37 | + | |
| 38 | + public function is_valid() | |
| 39 | + { | |
| 40 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 41 | + } | |
| 42 | +} | |
| 43 | + | |
| 44 | +?> | |
| 0 | 45 | \ No newline at end of file | ... | ... |
search2/search/fields/IsImmutableField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class IsImmutableField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('immutable', 'documents', 'Is Immutable'); | |
| 8 | + $this->setAlias('IsImmutable'); | |
| 9 | + $this->isValueQuoted(false); | |
| 10 | + } | |
| 11 | + | |
| 12 | + public function modifyValue($value) | |
| 13 | + { | |
| 14 | + if (is_numeric($value)) | |
| 15 | + { | |
| 16 | + $value = ($value+0)?1:0; | |
| 17 | + } | |
| 18 | + else | |
| 19 | + { | |
| 20 | + switch(strtolower($value)) | |
| 21 | + { | |
| 22 | + case 'true': | |
| 23 | + case 'yes': | |
| 24 | + $value=1; | |
| 25 | + break; | |
| 26 | + default: | |
| 27 | + $value=0; | |
| 28 | + } | |
| 29 | + } | |
| 30 | + return $value; | |
| 31 | + } | |
| 32 | + | |
| 33 | + public function getInputRequirements() | |
| 34 | + { | |
| 35 | + return array('value'=>array('type'=>FieldInputType::BOOLEAN)); | |
| 36 | + } | |
| 37 | + | |
| 38 | + public function is_valid() | |
| 39 | + { | |
| 40 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 41 | + } | |
| 42 | +} | |
| 43 | + | |
| 44 | +?> | |
| 0 | 45 | \ No newline at end of file | ... | ... |
search2/search/fields/MimeTypeField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class MimeTypeField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('mime_id', 'document_content_version', 'Mime Type'); | |
| 8 | + $this->setAlias('MimeType'); | |
| 9 | + $this->joinTo('mime_types', 'id'); | |
| 10 | + $this->matchField('mimetypes'); | |
| 11 | + } | |
| 12 | + | |
| 13 | + public function getInputRequirements() | |
| 14 | + { | |
| 15 | + // ideally MIME_TYPES | |
| 16 | + // but we must rework the mime_types table to be prettier! | |
| 17 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 18 | + } | |
| 19 | + | |
| 20 | + public function is_valid() | |
| 21 | + { | |
| 22 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 23 | + } | |
| 24 | +} | |
| 25 | + | |
| 26 | +?> | |
| 0 | 27 | \ No newline at end of file | ... | ... |
search2/search/fields/ModifiedByField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class ModifiedByField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('modified_user_id', 'documents', 'Modified By'); | |
| 8 | + $this->setAlias('ModifiedBy'); | |
| 9 | + $this->joinTo('users', 'id'); | |
| 10 | + $this->matchField('name'); | |
| 11 | + } | |
| 12 | + | |
| 13 | + public function getInputRequirements() | |
| 14 | + { | |
| 15 | + return array('value'=>array('type'=>FieldInputType::USER_LIST)); | |
| 16 | + } | |
| 17 | + | |
| 18 | + public function is_valid() | |
| 19 | + { | |
| 20 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | +?> | |
| 0 | 25 | \ No newline at end of file | ... | ... |
search2/search/fields/ModifiedDeltaField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class ModifiedDeltaField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + private $modifiedName; | |
| 6 | + | |
| 7 | + public function __construct() | |
| 8 | + { | |
| 9 | + parent::__construct('modified', 'documents', 'Modified Delta'); | |
| 10 | + $this->setAlias('ModifiedDelta'); | |
| 11 | + $this->isValueQuoted(false); | |
| 12 | + } | |
| 13 | + | |
| 14 | + public function modifyName($sql) | |
| 15 | + { | |
| 16 | + $this->modifiedName = $sql; | |
| 17 | + $now = date('Y-m-d'); | |
| 18 | + | |
| 19 | + | |
| 20 | + return "cast('$now' as date)"; | |
| 21 | + } | |
| 22 | + | |
| 23 | + public function modifyValue($value) | |
| 24 | + { | |
| 25 | + return "cast($this->modifiedName + $value as date)"; | |
| 26 | + } | |
| 27 | + | |
| 28 | + public function getInputRequirements() | |
| 29 | + { | |
| 30 | + return array('value'=>array('type'=>FieldInputType::DATEDIFF)); | |
| 31 | + } | |
| 32 | + | |
| 33 | + public function is_valid() | |
| 34 | + { | |
| 35 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$between); | |
| 36 | + } | |
| 37 | +} | |
| 38 | + | |
| 39 | +?> | |
| 0 | 40 | \ No newline at end of file | ... | ... |
search2/search/fields/ModifiedField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class ModifiedField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('modified', 'documents', 'Modified'); | |
| 8 | + $this->setAlias('Modified'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function modifyName($sql) | |
| 12 | + { | |
| 13 | + return "cast($sql as date)"; | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function getInputRequirements() | |
| 17 | + { | |
| 18 | + return array('value'=>array('type'=>FieldInputType::DATE)); | |
| 19 | + } | |
| 20 | + | |
| 21 | + public function is_valid() | |
| 22 | + { | |
| 23 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$between); | |
| 24 | + } | |
| 25 | +} | |
| 26 | + | |
| 27 | +?> | |
| 0 | 28 | \ No newline at end of file | ... | ... |
search2/search/fields/TagField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class TagField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('tag', 'tag_words', 'Tag'); | |
| 8 | + $this->setAlias('Tag'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +?> | |
| 0 | 23 | \ No newline at end of file | ... | ... |
search2/search/fields/TitleField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class TitleField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('name', 'document_metadata_version', 'Title'); | |
| 8 | + $this->setAlias('Title'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +?> | |
| 0 | 23 | \ No newline at end of file | ... | ... |
search2/search/fields/WorkflowField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class WorkflowField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('workflow_id', 'document_metadata_version', 'Workflow'); | |
| 8 | + $this->setAlias('Workflow'); | |
| 9 | + $this->joinTo('workflows', 'id'); | |
| 10 | + $this->matchField('name'); | |
| 11 | + } | |
| 12 | + | |
| 13 | + public function getInputRequirements() | |
| 14 | + { | |
| 15 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 16 | + } | |
| 17 | + | |
| 18 | + public function is_valid() | |
| 19 | + { | |
| 20 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | +?> | |
| 0 | 25 | \ No newline at end of file | ... | ... |
search2/search/fields/WorkflowIDField.inc.php
0 → 100644
| 1 | +<?php | |
| 2 | + | |
| 3 | +class WorkflowIDField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('workflow_id', 'document_metadata_version', 'Workflow ID'); | |
| 8 | + $this->setAlias('WorkflowID'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::INT)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +?> | |
| 0 | 23 | \ No newline at end of file | ... | ... |
search2/search/fields/WorkflowStateField.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | +class WorkflowStateField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('workflow_state_id', 'document_metadata_version', 'Workflow State'); | |
| 8 | + $this->setAlias('WorkflowState'); | |
| 9 | + $this->joinTo('workflow_states', 'id'); | |
| 10 | + $this->matchField('name'); | |
| 11 | + } | |
| 12 | + | |
| 13 | + public function getInputRequirements() | |
| 14 | + { | |
| 15 | + return array('value'=>array('type'=>FieldInputType::TEXT)); | |
| 16 | + } | |
| 17 | + | |
| 18 | + public function is_valid() | |
| 19 | + { | |
| 20 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | +?> | |
| 0 | 25 | \ No newline at end of file | ... | ... |
search2/search/fields/WorkflowStateIDField.inc.php
0 → 100644
| 1 | +<?php | |
| 2 | + | |
| 3 | +class WorkflowStateIDField extends DBFieldExpr | |
| 4 | +{ | |
| 5 | + public function __construct() | |
| 6 | + { | |
| 7 | + parent::__construct('workflow_state_id', 'document_metadata_version', 'Workflow State ID'); | |
| 8 | + $this->setAlias('WorkflowStateID'); | |
| 9 | + } | |
| 10 | + | |
| 11 | + public function getInputRequirements() | |
| 12 | + { | |
| 13 | + return array('value'=>array('type'=>FieldInputType::INT)); | |
| 14 | + } | |
| 15 | + | |
| 16 | + public function is_valid() | |
| 17 | + { | |
| 18 | + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$is); | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +?> | |
| 0 | 23 | \ No newline at end of file | ... | ... |
search2/search/search.inc.php
0 → 100755
| 1 | +<?php | |
| 2 | + | |
| 3 | + | |
| 4 | +require_once('search/SearchCommandParser.php'); | |
| 5 | +require_once('search/SearchCommandLexer.php'); | |
| 6 | +require_once('search/fieldRegistry.inc.php'); | |
| 7 | +require_once('search/expr.inc.php'); | |
| 8 | + | |
| 9 | +function rank_compare($a, $b) | |
| 10 | +{ | |
| 11 | + if ($a->Rank == $b->Rank) | |
| 12 | + { | |
| 13 | + if ($a->Title == $b->Title) | |
| 14 | + return 0; | |
| 15 | + // we'll show docs in ascending order by name | |
| 16 | + return ($a->Title < $b->Title)?-1:1; | |
| 17 | + } | |
| 18 | + // we want to be in descending order | |
| 19 | + return ($a->Rank > $b->Rank)?-1:1; | |
| 20 | +} | |
| 21 | + | |
| 22 | +function search_alias_compare($a, $b) | |
| 23 | +{ | |
| 24 | + if ($a['alias'] == $b['alias']) return 0; | |
| 25 | + return ($a['alias'] < $b['alias'])?-1:1; | |
| 26 | +} | |
| 27 | + | |
| 28 | +class SearchHelper | |
| 29 | +{ | |
| 30 | + public static function getSavedSearchEvents() | |
| 31 | + { | |
| 32 | + // TODO | |
| 33 | + $sql = ""; | |
| 34 | + } | |
| 35 | + | |
| 36 | + public static function getJSdocumentTypesStruct($documenttypes = null) | |
| 37 | + { | |
| 38 | + if (is_null($documenttypes)) | |
| 39 | + { | |
| 40 | + $documenttypes = SearchHelper::getDocumentTypes(); | |
| 41 | + } | |
| 42 | + $dt=0; | |
| 43 | + $documenttypes_str = '['; | |
| 44 | + foreach($documenttypes as $user) | |
| 45 | + { | |
| 46 | + if ($dt++ > 0) $documenttypes_str .= ','; | |
| 47 | + $id=$user['id']; | |
| 48 | + $name=$user['name']; | |
| 49 | + | |
| 50 | + $documenttypes_str .= "\n\t{id: \"$id\", name: \"$name\"}"; | |
| 51 | + } | |
| 52 | + $documenttypes_str .= ']'; | |
| 53 | + return $documenttypes_str; | |
| 54 | + | |
| 55 | + } | |
| 56 | + | |
| 57 | + public static function getJSmimeTypesStruct($mimetypes = null) | |
| 58 | + { | |
| 59 | + if (is_null($mimetypes)) | |
| 60 | + { | |
| 61 | + $mimetypes = SearchHelper::getMimeTypes(); | |
| 62 | + } | |
| 63 | + $mt=0; | |
| 64 | + $mimetypes_str = '['; | |
| 65 | + foreach($mimetypes as $user) | |
| 66 | + { | |
| 67 | + if ($mt++ > 0) $mimetypes_str .= ','; | |
| 68 | + | |
| 69 | + $name=$user['name']; | |
| 70 | + | |
| 71 | + $mimetypes_str .= "\n\t\"$name\""; | |
| 72 | + } | |
| 73 | + $mimetypes_str .= ']'; | |
| 74 | + | |
| 75 | + return $mimetypes_str; | |
| 76 | + } | |
| 77 | + | |
| 78 | + public static function getJSusersStruct($users = null) | |
| 79 | + { | |
| 80 | + if (is_null($users)) | |
| 81 | + { | |
| 82 | + $users = SearchHelper::getUsers(); | |
| 83 | + } | |
| 84 | + | |
| 85 | + $uo=0; | |
| 86 | + $users_str = '['; | |
| 87 | + foreach($users as $user) | |
| 88 | + { | |
| 89 | + if ($uo++ > 0) $users_str .= ','; | |
| 90 | + $id=$user['id']; | |
| 91 | + $name=$user['name']; | |
| 92 | + | |
| 93 | + $users_str .= "\n\t{id: \"$id\", name: \"$name\"}"; | |
| 94 | + } | |
| 95 | + $users_str .= ']'; | |
| 96 | + | |
| 97 | + return $users_str; | |
| 98 | + } | |
| 99 | + | |
| 100 | + public static function getJSfieldsStruct($fields = null) | |
| 101 | + { | |
| 102 | + if (is_null($fields)) | |
| 103 | + { | |
| 104 | + $fields = SearchHelper::getSearchFields(); | |
| 105 | + } | |
| 106 | + $fields_str = '['; | |
| 107 | + $fo=0; | |
| 108 | + foreach($fields as $field) | |
| 109 | + { | |
| 110 | + if ($fo++ > 0) $fields_str .= ','; | |
| 111 | + $alias = $field['alias']; | |
| 112 | + $display = $field['display']; | |
| 113 | + $type = $field['type']; | |
| 114 | + $fields_str .= "\n\t{alias: \"$alias\", name: \"$display\", type:\"$type\"}"; | |
| 115 | + } | |
| 116 | + $fields_str .= ']'; | |
| 117 | + | |
| 118 | + return $fields_str; | |
| 119 | + } | |
| 120 | + | |
| 121 | + public static function getJSworkflowStruct($workflows = null) | |
| 122 | + { | |
| 123 | + if (is_null($workflows)) | |
| 124 | + { | |
| 125 | + $workflows = SearchHelper::getWorkflows(); | |
| 126 | + } | |
| 127 | + | |
| 128 | + $workflow_str = '['; | |
| 129 | + $wo=0; | |
| 130 | + foreach($workflows as $workflow) | |
| 131 | + { | |
| 132 | + if ($wo++ > 0) $workflow_str .= ','; | |
| 133 | + $wid = $workflow['id']; | |
| 134 | + $name = $workflow['name']; | |
| 135 | + | |
| 136 | + $workflow_str .= "\n\t{id:\"$wid\", name: \"$name\", states: [ "; | |
| 137 | + | |
| 138 | + $result['workflows'][$wid] = $workflow; | |
| 139 | + $states = SearchHelper::getWorkflowStates($wid); | |
| 140 | + $result['workflows'][$wid]['states'] = array(); | |
| 141 | + $so=0; | |
| 142 | + foreach($states as $state) | |
| 143 | + { | |
| 144 | + if ($so++>0) $workflow_str .= ','; | |
| 145 | + $sid = $state['id']; | |
| 146 | + $name=$state['name']; | |
| 147 | + $result['workflows'][$wid]['states'][$sid] = $state; | |
| 148 | + $workflow_str .= "\n\t\t{id:\"$wid\", name: \"$name\"}"; | |
| 149 | + } | |
| 150 | + $workflow_str .= ']}'; | |
| 151 | + } | |
| 152 | + $workflow_str .= ']'; | |
| 153 | + | |
| 154 | + return $workflow_str; | |
| 155 | + } | |
| 156 | + | |
| 157 | + public static function getJSfieldsetStruct($fieldsets = null) | |
| 158 | + { | |
| 159 | + if (is_null($fieldsets)) | |
| 160 | + { | |
| 161 | + $fieldsets = SearchHelper::getFieldsets(); | |
| 162 | + } | |
| 163 | + | |
| 164 | + $fieldset_str = '['; | |
| 165 | + $fso=0; | |
| 166 | + foreach($fieldsets as $fieldset) | |
| 167 | + { | |
| 168 | + $fsid=$fieldset['id']; | |
| 169 | + $name = $fieldset['name']; | |
| 170 | + $desc = $fieldset['description']; | |
| 171 | + if ($fso++>0) $fieldset_str .= ','; | |
| 172 | + $fieldset_str .= "\n\t{id:\"$fsid\",name:\"$name\",description:\"$desc\", fields: ["; | |
| 173 | + | |
| 174 | + | |
| 175 | + $result['fieldsets'][$fsid] = $fieldset; | |
| 176 | + $fields = SearchHelper::getFields($fsid); | |
| 177 | + $result['fieldsets'][$fsid]['fields'] = array(); | |
| 178 | + $fo=0; | |
| 179 | + foreach($fields as $field) | |
| 180 | + { | |
| 181 | + if ($fo++ >0) $fieldset_str .= ','; | |
| 182 | + $fid = $field['id']; | |
| 183 | + $name= $field['name']; | |
| 184 | + $desc = $field['description']; | |
| 185 | + $datatype=$field['datatype']; | |
| 186 | + $control=$field['control']; | |
| 187 | + $fieldset_str .= "\n\t\t{id:\"$fid\", name:\"$name\", description:\"$desc\", datatype:\"$datatype\", control:\"$control\", options: ["; | |
| 188 | + $options = $field['options']; | |
| 189 | + $oo = 0; | |
| 190 | + foreach($options as $option) | |
| 191 | + { | |
| 192 | + if ($oo++ > 0) $fieldset_str .= ','; | |
| 193 | + $oid = $option['id']; | |
| 194 | + $name= $option['name']; | |
| 195 | + $fieldset_str .= "\n\t\t\t{id: \"$oid\", name: \"$name\"}"; | |
| 196 | + } | |
| 197 | + $fieldset_str .= ']}'; | |
| 198 | + $result['fieldsets'][$fsid]['fields'][$fid] = $field; | |
| 199 | + } | |
| 200 | + $fieldset_str .= ']}'; | |
| 201 | + | |
| 202 | + } | |
| 203 | + $fieldset_str .= ']'; | |
| 204 | + | |
| 205 | + return $fieldset_str; | |
| 206 | + } | |
| 207 | + | |
| 208 | + | |
| 209 | + public static function getSavedSearches($userID) | |
| 210 | + { | |
| 211 | + $sql = "SELECT id, name FROM search_saved WHERE type='S'"; | |
| 212 | + | |
| 213 | + // if we are not the system admin, then we get only ours or shared searches | |
| 214 | + if (!Permission::userIsSystemAdministrator($userID)) | |
| 215 | + { | |
| 216 | + $sql .= " and ( user_id=$userID OR shared=1 ) "; | |
| 217 | + } | |
| 218 | + | |
| 219 | + $rs = DBUtil::getResultArray($sql); | |
| 220 | + return $rs; | |
| 221 | + } | |
| 222 | + | |
| 223 | + public static function getSearchFields() | |
| 224 | + { | |
| 225 | + $registry = ExprFieldRegistry::getRegistry(); | |
| 226 | + | |
| 227 | + $fields = $registry->getFields(); | |
| 228 | + | |
| 229 | + $results = array(); | |
| 230 | + foreach($fields as $field ) | |
| 231 | + { | |
| 232 | + $type = $field->getInputRequirements(); | |
| 233 | + $type = $type['value']['type']; | |
| 234 | + $results[] = array('alias'=>$field->getAlias(), 'display'=>$field->getDisplay(), 'type'=>$type); | |
| 235 | + } | |
| 236 | + usort($results, search_alias_compare); | |
| 237 | + return $results; | |
| 238 | + } | |
| 239 | + | |
| 240 | + public static function getFolder($folderID, $userid) | |
| 241 | + { | |
| 242 | + $folder = Folder::get($folderID + 0); | |
| 243 | + if (PEAR::isError($folder)) | |
| 244 | + { | |
| 245 | + return $folder; | |
| 246 | + } | |
| 247 | + | |
| 248 | + if (!Permission::userHasFolderReadPermission($folder)) | |
| 249 | + { | |
| 250 | + return new PEAR_Error('no permission to read folder'); | |
| 251 | + } | |
| 252 | + | |
| 253 | + $sql = "SELECT id, name FROM folders WHERE parent_id=$folderID ORDER BY name"; | |
| 254 | + $rs = DBUtil::getResultArray($sql); | |
| 255 | + if (PEAR::isError($rs)) | |
| 256 | + { | |
| 257 | + return $rs; | |
| 258 | + } | |
| 259 | + | |
| 260 | + $folders = array(); | |
| 261 | + | |
| 262 | + foreach($rs as $folder) | |
| 263 | + { | |
| 264 | + $fobj = Folder::get($folder['id']); | |
| 265 | + | |
| 266 | + if (Permission::userHasFolderReadPermission($fobj)) | |
| 267 | + { | |
| 268 | + $folders[] = $folder; | |
| 269 | + } | |
| 270 | + } | |
| 271 | + return $folders; | |
| 272 | + } | |
| 273 | + | |
| 274 | + public static function getFields($fieldsetID) | |
| 275 | + { | |
| 276 | + if ($fieldsetID < 0) | |
| 277 | + { | |
| 278 | + $documentTypeID = sanitizeForSQL(-$fieldsetID); | |
| 279 | + $sql = "SELECT | |
| 280 | + df.id, df.name, df.data_type, df.has_lookup, df.has_lookuptree, df.description | |
| 281 | + FROM | |
| 282 | + document_type_fields_link dtfl | |
| 283 | + INNER JOIN document_fields df on dtfl.field_id=df.id | |
| 284 | + WHERE | |
| 285 | + dtfl.document_type_id=$documentTypeID"; | |
| 286 | + | |
| 287 | + | |
| 288 | + } | |
| 289 | + else | |
| 290 | + { | |
| 291 | + $fieldsetID = sanitizeForSQL($fieldsetID); | |
| 292 | + $sql = "SELECT id, name, data_type, has_lookup, has_lookuptree, description FROM document_fields WHERE parent_fieldset=$fieldsetID"; | |
| 293 | + } | |
| 294 | + | |
| 295 | + $rs = DBUtil::getResultArray($sql); | |
| 296 | + if (PEAR::isError($rs)) | |
| 297 | + { | |
| 298 | + return $rs; | |
| 299 | + } | |
| 300 | + if (count($rs) == 0) | |
| 301 | + { | |
| 302 | + return new PEAR_Error('Fieldset was not found'); | |
| 303 | + } | |
| 304 | + | |
| 305 | + $result=array(); | |
| 306 | + foreach($rs as $item) | |
| 307 | + { | |
| 308 | + $fieldid=$item['id']; | |
| 309 | + $type='normal'; | |
| 310 | + $options = array(); | |
| 311 | + $haslookup =$item['has_lookup'] + 0 > 0; | |
| 312 | + $hastree = ($item['has_lookuptree']+0 > 1); | |
| 313 | + | |
| 314 | + if ($haslookup || $hastree) | |
| 315 | + { | |
| 316 | + $type = 'lookup'; | |
| 317 | + $sql = "select id, name from metadata_lookup where document_field_id=$fieldid"; | |
| 318 | + $options = DBUtil::getResultArray($sql); | |
| 319 | + | |
| 320 | + } | |
| 321 | + /*if ($hastree) | |
| 322 | + { | |
| 323 | + $type = 'lookup'; | |
| 324 | + $sql = "select id, name, metadata_lookup_tree_parent as parent from metadata_lookup_tree where document_field_id=$fieldid"; | |
| 325 | + $options = DBUtil::getResultArray($sql); | |
| 326 | + }*/ | |
| 327 | + | |
| 328 | + if ($item['data_type'] == 'USERLIST') | |
| 329 | + { | |
| 330 | + $type = 'lookup'; | |
| 331 | + $sql = "SELECT id, name from users WHERE disabled=0"; | |
| 332 | + $options = DBUtil::getResultArray($sql); | |
| 333 | + } | |
| 334 | + | |
| 335 | + $ritem = array( | |
| 336 | + 'id'=>$fieldid, | |
| 337 | + 'name'=>$item['name'], | |
| 338 | + 'description'=>$item['description'], | |
| 339 | + 'datatype'=>$item['data_type'], | |
| 340 | + 'control'=>$type, | |
| 341 | + 'options'=>$options | |
| 342 | + ); | |
| 343 | + | |
| 344 | + $result[]= $ritem; | |
| 345 | + } | |
| 346 | + return $result; | |
| 347 | + } | |
| 348 | + | |
| 349 | + public static function getFieldsets() | |
| 350 | + { | |
| 351 | + $sql = "SELECT id, name, description FROM fieldsets WHERE disabled=0"; | |
| 352 | + $rs = DBUtil::getResultArray($sql); | |
| 353 | + | |
| 354 | + return $rs; | |
| 355 | + } | |
| 356 | + | |
| 357 | + public static function getDocumentTypeFieldsets($documentTypeID) | |
| 358 | + { | |
| 359 | + $documentTypeID = sanitizeForSQL($documentTypeID); | |
| 360 | + $sql = "SELECT | |
| 361 | + fs.id, fs.name, fs.description | |
| 362 | + FROM | |
| 363 | + fieldsets fs LEFT JOIN document_type_fieldsets_link dtfl ON dtfl.fieldset_id=fs.id | |
| 364 | + WHERE | |
| 365 | + fs.disabled=0 AND (dtfl.document_type_id=$documentTypeID OR fs.is_generic=1)"; | |
| 366 | + $rs = DBUtil::getResultArray($sql); | |
| 367 | + | |
| 368 | + return $rs; | |
| 369 | + } | |
| 370 | + | |
| 371 | + public static function getDocumentTypes() | |
| 372 | + { | |
| 373 | + $sql = "SELECT id, name from document_types_lookup WHERE disabled=0"; | |
| 374 | + $rs = DBUtil::getResultArray($sql); | |
| 375 | + return $rs; | |
| 376 | + } | |
| 377 | + | |
| 378 | + public static function getMimeTypes() { | |
| 379 | + $sql = "SELECT DISTINCT mimetypes as name FROM mime_types order by mimetypes "; | |
| 380 | + $rs = DBUtil::getResultArray($sql); | |
| 381 | + return $rs; | |
| 382 | + } | |
| 383 | + | |
| 384 | + public static function getWorkflows() | |
| 385 | + { | |
| 386 | + $sql = "SELECT id, human_name as name FROM workflows WHERE enabled=1"; | |
| 387 | + $rs = DBUtil::getResultArray($sql); | |
| 388 | + return $rs; | |
| 389 | + } | |
| 390 | + | |
| 391 | + public static function getUsers() | |
| 392 | + { | |
| 393 | + $sql = "SELECT id, name FROM users WHERE disabled=0"; | |
| 394 | + $rs = DBUtil::getResultArray($sql); | |
| 395 | + return $rs; | |
| 396 | + } | |
| 397 | + | |
| 398 | + public static function getWorkflowStates($workflowid) | |
| 399 | + { | |
| 400 | + $sql = "SELECT id,human_name as name FROM workflow_states WHERE workflow_id=$workflowid"; | |
| 401 | + $rs = DBUtil::getResultArray($sql); | |
| 402 | + return $rs; | |
| 403 | + } | |
| 404 | + | |
| 405 | +} | |
| 406 | + | |
| 407 | + | |
| 408 | +function getExpressionLocalityString($expr_str, $locality, $length, $start_offset=10) | |
| 409 | +{ | |
| 410 | + if ($locality - $start_offset < 0) | |
| 411 | + { | |
| 412 | + $locality = 0; | |
| 413 | + } | |
| 414 | + else | |
| 415 | + { | |
| 416 | + $locality -= $start_offset; | |
| 417 | + } | |
| 418 | + | |
| 419 | + return substr($expr_str, $locality, $length); | |
| 420 | +} | |
| 421 | + | |
| 422 | +/** | |
| 423 | + * This parses a query. | |
| 424 | + * | |
| 425 | + * @param OpExpr $expr_str | |
| 426 | + * @return array of MatchResult | |
| 427 | + */ | |
| 428 | +function parseExpression($expr_str) | |
| 429 | +{ | |
| 430 | + $parser = new SearchCommandParser(); | |
| 431 | + $lexer = new SearchCommandLexer($expr_str); | |
| 432 | + | |
| 433 | +// $parser->PrintTrace(); | |
| 434 | + $use_internal=false; | |
| 435 | + | |
| 436 | + try | |
| 437 | + { | |
| 438 | + while ($lexer->yylex()) | |
| 439 | + { | |
| 440 | + //print "\n" . $lexer->value . "\n"; | |
| 441 | + | |
| 442 | + $parser->doParse($lexer->token, $lexer->value); | |
| 443 | + | |
| 444 | + if (!$parser->isExprOk()) | |
| 445 | + { | |
| 446 | + $use_internal=true; | |
| 447 | + $expr_str=getExpressionLocalityString($expr_str, $lexer->offset, 20); | |
| 448 | + throw new Exception("Parsing problem near '$lexer->value' in '$expr_str' of expression."); | |
| 449 | + } | |
| 450 | + } | |
| 451 | + | |
| 452 | + // we are now done | |
| 453 | + $parser->doParse(0, 0); | |
| 454 | + | |
| 455 | + if (!$parser->isExprOk()) | |
| 456 | + { | |
| 457 | + $use_internal=true; | |
| 458 | + $expr_str=getExpressionLocalityString($expr_str, $lexer->offset, 20); | |
| 459 | + throw new Exception("There is a problem parsing the expression '$expr_str'"); | |
| 460 | + } | |
| 461 | + | |
| 462 | + } | |
| 463 | + catch(ResolutionException $e) | |
| 464 | + { | |
| 465 | + throw $e; | |
| 466 | + } | |
| 467 | + catch(Exception $e) | |
| 468 | + { | |
| 469 | + if ($use_internal) | |
| 470 | + { | |
| 471 | + throw $e; | |
| 472 | + } | |
| 473 | + $expr_str=getExpressionLocalityString($expr_str, $lexer->offset, 20); | |
| 474 | + throw new Exception("Parsing problem near '$lexer->value' of expression '$expr_str'."); | |
| 475 | + } | |
| 476 | + | |
| 477 | + return $parser->getExprResult(); | |
| 478 | +} | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | +?> | |
| 0 | 483 | \ No newline at end of file | ... | ... |