PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/titania/includes/tools/search.php

https://github.com/Lurttinen/customisation-db
PHP | 407 lines | 248 code | 63 blank | 96 comment | 24 complexity | 19905e85f35c97224433a97172e695cd MD5 | raw file
  1. <?php
  2. /**
  3. *
  4. * @package Titania
  5. * @version $Id$
  6. * @copyright (c) 2008 phpBB Customisation Database Team
  7. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  8. *
  9. */
  10. /**
  11. * @ignore
  12. */
  13. if (!defined('IN_TITANIA'))
  14. {
  15. exit;
  16. }
  17. // Include library in include path (for Zend)
  18. if (titania::$config->search_backend == 'zend')
  19. {
  20. set_include_path(get_include_path() . PATH_SEPARATOR . realpath(TITANIA_ROOT . 'includes/library/'));
  21. titania::_include('library/Zend/Search/Lucene', false, 'Zend_Search_Lucene');
  22. }
  23. // Using the phpBB ezcomponents loader
  24. titania::_include('library/ezcomponents/loader', false, 'phpbb_ezcomponents_loader');
  25. phpbb_ezcomponents_loader::load_component('search');
  26. class titania_search
  27. {
  28. /**
  29. * Path to store (for the Zend Search index files)
  30. */
  31. const store_path = 'store/search/';
  32. /**
  33. * Holds the indexer
  34. */
  35. private static $index = false;
  36. /**
  37. * Forcefully set the indexer to not index anything
  38. *
  39. * @var bool
  40. */
  41. public static $do_not_index = false;
  42. /**
  43. * Initialize the Search
  44. */
  45. public static function initialize()
  46. {
  47. if (self::$index === false)
  48. {
  49. // Initialize the ezc/Zend Search class
  50. if (titania::$config->search_backend == 'zend')
  51. {
  52. if (!is_writable(TITANIA_ROOT . self::store_path))
  53. {
  54. throw new exception(self::store_path . ' must be writable to use the Zend Lucene Search');
  55. }
  56. $handler = new ezcSearchZendLuceneHandler(TITANIA_ROOT . self::store_path);
  57. }
  58. else if (titania::$config->search_backend == 'solr')
  59. {
  60. $handler = new ezcSearchSolrHandler(titania::$config->search_backend_ip, titania::$config->search_backend_port);
  61. // In case Solr would happen to go down..
  62. if (!$handler->connection)
  63. {
  64. // Log this as an error
  65. titania::log(TITANIA_ERROR, 'Solr Server not responding');
  66. self::$do_not_index = true;
  67. return false;
  68. }
  69. }
  70. else
  71. {
  72. throw new exception('We need a proper search backend selected');
  73. }
  74. $manager = new ezcSearchEmbeddedManager;
  75. self::$index = new ezcSearchSession($handler, $manager);
  76. return true;
  77. }
  78. }
  79. /**
  80. * Index an item
  81. *
  82. * @param mixed $object_type The object_type (what this is set to is not entirely important, but must be the same for all items of that type)
  83. * @param int $object_id The object_id of an item (there can only be one of each id per object_type)
  84. * @param array $data Array of data (see titania_article)
  85. */
  86. public static function index($object_type, $object_id, $data)
  87. {
  88. if (self::initialize() === false)
  89. {
  90. return false;
  91. }
  92. $data['id'] = $object_type . '_' . $object_id;
  93. $data['type'] = $object_type;
  94. $article = new titania_article();
  95. // Set some defaults
  96. $data = array_merge(array(
  97. 'access_level' => TITANIA_ACCESS_PUBLIC,
  98. 'approved' => true,
  99. 'reported' => false,
  100. ), $data);
  101. $article->setState($data);
  102. // Run the update routine instead of the index, this way we should not ever run into issues with duplication
  103. self::$index->update($article);
  104. unset($article);
  105. }
  106. /**
  107. * Faster way to index multiple items
  108. *
  109. * @param array $data 2 dimensional array containing an array of the data needed to index. In the array for each item be sure to specify object_type and object_id
  110. */
  111. public static function mass_index($data)
  112. {
  113. if (self::initialize() === false)
  114. {
  115. return false;
  116. }
  117. self::$index->beginTransaction();
  118. foreach ($data as $row)
  119. {
  120. $object_type = $row['object_type'];
  121. $object_id = $row['object_id'];
  122. unset($row['object_type'], $row['object_id']);
  123. self::index($object_type, $object_id, $row);
  124. }
  125. self::$index->commit();
  126. }
  127. /**
  128. * Delete an item
  129. *
  130. * @param mixed $object_type The object_type (what this is set to is not entirely important, but must be the same for all items of that type)
  131. * @param int $object_id The object_id of an item (there can only be one of each id per object_type)
  132. */
  133. public static function delete($object_type, $object_id)
  134. {
  135. if (self::initialize() === false)
  136. {
  137. return false;
  138. }
  139. self::$index->deleteById($object_type . '_' . $object_id, 'titania_article');
  140. }
  141. /**
  142. * Truncate the entire search or a specific type
  143. *
  144. * @param mixed $object_type The object_type you would like to remove, false to truncate the entire search index
  145. */
  146. public static function truncate($object_type = false)
  147. {
  148. if (self::initialize() === false)
  149. {
  150. return false;
  151. }
  152. $query = self::$index->createDeleteQuery('titania_article');
  153. if ($object_type !== false)
  154. {
  155. $query->where(
  156. $query->eq('type', $object_type)
  157. );
  158. }
  159. self::$index->delete($query);
  160. }
  161. /**
  162. * Perform a normal search
  163. *
  164. * @param string $search_query The user input for a search query
  165. * @param object|bool $sort The sort class
  166. * @param array $fields The fields to search
  167. *
  168. * @return The documents of the result
  169. */
  170. public static function search($search_query, &$sort, $fields = array('text', 'title'))
  171. {
  172. if (self::initialize() === false)
  173. {
  174. return false;
  175. }
  176. self::clean_keywords($search_query);
  177. $query = self::$index->createFindQuery('titania_article');
  178. $qb = new ezcSearchQueryBuilder();
  179. $qb->parseSearchQuery($query, $search_query, $fields);
  180. unset($qb);
  181. return self::custom_search($query, $sort);
  182. }
  183. /**
  184. * Search by the author
  185. *
  186. * @param mixed $user_id
  187. * @param mixed $sort
  188. */
  189. public static function author_search($user_id, &$sort)
  190. {
  191. if (self::initialize() === false)
  192. {
  193. return false;
  194. }
  195. $query = self::$index->createFindQuery('titania_article');
  196. $query->where($query->eq('author', $user_id));
  197. return self::custom_search($query, $sort);
  198. }
  199. /**
  200. * Create a find query and return (to create our own custom searches)
  201. */
  202. public static function create_find_query()
  203. {
  204. if (self::initialize() === false)
  205. {
  206. return false;
  207. }
  208. return self::$index->createFindQuery('titania_article');
  209. }
  210. /**
  211. * Perform a custom search (must build a createFindQuery for the query)
  212. *
  213. * @param object $query self::$index->createFindQuery
  214. * @param object|bool $sort The sort class
  215. *
  216. * @return The documents of the result
  217. */
  218. public static function custom_search($query, &$sort)
  219. {
  220. if (self::initialize() === false)
  221. {
  222. return false;
  223. }
  224. // For those without moderator permissions do not display unapproved stuff
  225. if (!phpbb::$auth->acl_get('m_'))
  226. {
  227. $query->where($query->eq('approved', 1));
  228. }
  229. // Don't worry about authors level access...no search page that can search where a person would have authors access
  230. if (titania::$access_level != TITANIA_ACCESS_TEAMS)
  231. {
  232. $query->where($query->eq('access_level', TITANIA_ACCESS_PUBLIC));
  233. }
  234. $query->offset = $sort->start;
  235. $query->limit = $sort->limit;
  236. $search_results = self::$index->find($query);
  237. $sort->total = $search_results->resultCount;
  238. $results = array(
  239. 'user_ids' => array(),
  240. 'documents' => array(),
  241. );
  242. foreach ($search_results->documents as $result)
  243. {
  244. $results['user_ids'][] = $result->document->author;
  245. $results['documents'][] = $result->document;
  246. }
  247. return $results;
  248. }
  249. /**
  250. * Search in a set (similar to sql_in_set)
  251. *
  252. * @param object $query Search query
  253. * @param string $field The field name
  254. * @param array $array Array of values to search in
  255. */
  256. public static function in_set($query, $field, $array)
  257. {
  258. if (!sizeof($array))
  259. {
  260. throw new exception('No values specified for search in set');
  261. }
  262. $set = array();
  263. foreach ($array as $item)
  264. {
  265. $set[] = $query->eq($field, $item);
  266. }
  267. $query->where($query->lOr($set));
  268. }
  269. /**
  270. * Clean some keywords up
  271. *
  272. * @param string $keywords
  273. */
  274. public static function clean_keywords(&$keywords)
  275. {
  276. // Replace | with or
  277. $keywords = str_replace('|', ' or ', $keywords);
  278. }
  279. }
  280. class titania_article implements ezcBasePersistable, ezcSearchDefinitionProvider
  281. {
  282. public $id;
  283. public $parent_id;
  284. public $title;
  285. public $text;
  286. public $text_uid;
  287. public $text_bitfield;
  288. public $text_options;
  289. public $date;
  290. public $author;
  291. public $url;
  292. public $type;
  293. public $access_level;
  294. public $approved;
  295. public $reported;
  296. public function __construct() {}
  297. public function getState()
  298. {
  299. $state = array(
  300. 'id' => $this->id,
  301. 'parent_id' => (int) $this->parent_id,
  302. 'title' => $this->title,
  303. 'text' => $this->text,
  304. 'text_uid' => $this->text_uid,
  305. 'text_bitfield' => $this->text_bitfield,
  306. 'text_options' => (int) $this->text_options,
  307. 'author' => (int) $this->author,
  308. 'date' => (int) $this->date,
  309. 'url' => $this->url,
  310. 'type' => (int) $this->type,
  311. 'access_level' => (int) $this->access_level,
  312. 'approved' => ($this->approved) ? 1 : 0,
  313. 'reported' => ($this->reported) ? 1 : 0,
  314. );
  315. return $state;
  316. }
  317. public function setState(array $state)
  318. {
  319. foreach ($state as $key => $value)
  320. {
  321. $this->$key = $value;
  322. }
  323. }
  324. static public function getDefinition()
  325. {
  326. $doc = new ezcSearchDocumentDefinition( __CLASS__ );
  327. $doc->idProperty = 'id';
  328. $doc->fields['id'] = new ezcSearchDefinitionDocumentField('id', ezcSearchDocumentDefinition::TEXT);
  329. $doc->fields['parent_id'] = new ezcSearchDefinitionDocumentField('parent_id', ezcSearchDocumentDefinition::INT);
  330. $doc->fields['type'] = new ezcSearchDefinitionDocumentField('type', ezcSearchDocumentDefinition::INT);
  331. $doc->fields['title'] = new ezcSearchDefinitionDocumentField('title', ezcSearchDocumentDefinition::TEXT, 2, true, false, true);
  332. $doc->fields['text'] = new ezcSearchDefinitionDocumentField('text', ezcSearchDocumentDefinition::TEXT, 1, true, false, true);
  333. $doc->fields['text_uid'] = new ezcSearchDefinitionDocumentField('text_uid', ezcSearchDocumentDefinition::STRING, 0);
  334. $doc->fields['text_bitfield'] = new ezcSearchDefinitionDocumentField('text_bitfield', ezcSearchDocumentDefinition::STRING, 0);
  335. $doc->fields['text_options'] = new ezcSearchDefinitionDocumentField('text_options', ezcSearchDocumentDefinition::INT, 0);
  336. $doc->fields['author'] = new ezcSearchDefinitionDocumentField('author', ezcSearchDocumentDefinition::INT);
  337. $doc->fields['date'] = new ezcSearchDefinitionDocumentField('date', ezcSearchDocumentDefinition::INT);
  338. $doc->fields['url'] = new ezcSearchDefinitionDocumentField('url', ezcSearchDocumentDefinition::STRING, 0);
  339. $doc->fields['access_level'] = new ezcSearchDefinitionDocumentField('access_level', ezcSearchDocumentDefinition::INT);
  340. $doc->fields['approved'] = new ezcSearchDefinitionDocumentField('approved', ezcSearchDocumentDefinition::INT);
  341. $doc->fields['reported'] = new ezcSearchDefinitionDocumentField('reported', ezcSearchDocumentDefinition::INT);
  342. return $doc;
  343. }
  344. }