PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/titania/includes/tools/search.php

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