datasource.inc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. <?php
  2. /**
  3. * @file
  4. * Contains the SearchApiDataSourceControllerInterface as well as a default base class.
  5. */
  6. /**
  7. * Interface for all data source controllers for Search API indexes.
  8. *
  9. * Data source controllers encapsulate all operations specific to an item type.
  10. * They are used for loading items, extracting item data, keeping track of the
  11. * item status, etc.
  12. *
  13. * Modules providing implementations of this interface that use a different way
  14. * (either different table or different method altogether) of keeping track of
  15. * indexed/dirty items than SearchApiAbstractDataSourceController should be
  16. * aware that indexes' numerical IDs can change due to feature reverts. It is
  17. * therefore recommended to use search_api_index_update_datasource(), or similar
  18. * code, in a hook_search_api_index_update() implementation.
  19. *
  20. * All methods of the data source may throw exceptions of type
  21. * SearchApiDataSourceException if any exception or error state is encountered.
  22. */
  23. interface SearchApiDataSourceControllerInterface {
  24. /**
  25. * Constructor for a data source controller.
  26. *
  27. * @param $type
  28. * The item type for which this controller is created.
  29. */
  30. public function __construct($type);
  31. /**
  32. * Return information on the ID field for this controller's type.
  33. *
  34. * @return array
  35. * An associative array containing the following keys:
  36. * - key: The property key for the ID field, as used in the item wrapper.
  37. * - type: The type of the ID field. Has to be one of the types from
  38. * search_api_field_types(). List types ("list<*>") are not allowed.
  39. */
  40. public function getIdFieldInfo();
  41. /**
  42. * Load items of the type of this data source controller.
  43. *
  44. * @param array $ids
  45. * The IDs of the items to laod.
  46. *
  47. * @return array
  48. * The loaded items, keyed by ID.
  49. */
  50. public function loadItems(array $ids);
  51. /**
  52. * Get a metadata wrapper for the item type of this data source controller.
  53. *
  54. * @param $item
  55. * Unless NULL, an item of the item type for this controller to be wrapped.
  56. * @param array $info
  57. * Optionally, additional information that should be used for creating the
  58. * wrapper. Uses the same format as entity_metadata_wrapper().
  59. *
  60. * @return EntityMetadataWrapper
  61. * A wrapper for the item type of this data source controller, according to
  62. * the info array, and optionally loaded with the given data.
  63. *
  64. * @see entity_metadata_wrapper()
  65. */
  66. public function getMetadataWrapper($item = NULL, array $info = array());
  67. /**
  68. * Get the unique ID of an item.
  69. *
  70. * @param $item
  71. * An item of this controller's type.
  72. *
  73. * @return
  74. * Either the unique ID of the item, or NULL if none is available.
  75. */
  76. public function getItemId($item);
  77. /**
  78. * Get a human-readable label for an item.
  79. *
  80. * @param $item
  81. * An item of this controller's type.
  82. *
  83. * @return
  84. * Either a human-readable label for the item, or NULL if none is available.
  85. */
  86. public function getItemLabel($item);
  87. /**
  88. * Get a URL at which the item can be viewed on the web.
  89. *
  90. * @param $item
  91. * An item of this controller's type.
  92. *
  93. * @return
  94. * Either an array containing the 'path' and 'options' keys used to build
  95. * the URL of the item, and matching the signature of url(), or NULL if the
  96. * item has no URL of its own.
  97. */
  98. public function getItemUrl($item);
  99. /**
  100. * Initialize tracking of the index status of items for the given indexes.
  101. *
  102. * All currently known items of this data source's type should be inserted
  103. * into the tracking table for the given indexes, with status "changed". If
  104. * items were already present, these should also be set to "changed" and not
  105. * be inserted again.
  106. *
  107. * @param array $indexes
  108. * The SearchApiIndex objects for which item tracking should be initialized.
  109. *
  110. * @throws SearchApiDataSourceException
  111. * If any of the indexes doesn't use the same item type as this controller.
  112. */
  113. public function startTracking(array $indexes);
  114. /**
  115. * Stop tracking of the index status of items for the given indexes.
  116. *
  117. * The tracking tables of the given indexes should be completely cleared.
  118. *
  119. * @param array $indexes
  120. * The SearchApiIndex objects for which item tracking should be stopped.
  121. *
  122. * @throws SearchApiDataSourceException
  123. * If any of the indexes doesn't use the same item type as this controller.
  124. */
  125. public function stopTracking(array $indexes);
  126. /**
  127. * Start tracking the index status for the given items on the given indexes.
  128. *
  129. * @param array $item_ids
  130. * The IDs of new items to track.
  131. * @param array $indexes
  132. * The indexes for which items should be tracked.
  133. *
  134. * @throws SearchApiDataSourceException
  135. * If any of the indexes doesn't use the same item type as this controller.
  136. */
  137. public function trackItemInsert(array $item_ids, array $indexes);
  138. /**
  139. * Set the tracking status of the given items to "changed"/"dirty".
  140. *
  141. * Unless $dequeue is set to TRUE, this operation is ignored for items whose
  142. * status is not "indexed".
  143. *
  144. * @param $item_ids
  145. * Either an array with the IDs of the changed items. Or FALSE to mark all
  146. * items as changed for the given indexes.
  147. * @param array $indexes
  148. * The indexes for which the change should be tracked.
  149. * @param $dequeue
  150. * If set to TRUE, also change the status of queued items.
  151. *
  152. * @throws SearchApiDataSourceException
  153. * If any of the indexes doesn't use the same item type as this controller.
  154. */
  155. public function trackItemChange($item_ids, array $indexes, $dequeue = FALSE);
  156. /**
  157. * Set the tracking status of the given items to "queued".
  158. *
  159. * Queued items are not marked as "dirty" even when they are changed, and they
  160. * are not returned by the getChangedItems() method.
  161. *
  162. * @param $item_ids
  163. * Either an array with the IDs of the queued items. Or FALSE to mark all
  164. * items as queued for the given indexes.
  165. * @param SearchApiIndex $index
  166. * The index for which the items were queued.
  167. *
  168. * @throws SearchApiDataSourceException
  169. * If any of the indexes doesn't use the same item type as this controller.
  170. */
  171. public function trackItemQueued($item_ids, SearchApiIndex $index);
  172. /**
  173. * Set the tracking status of the given items to "indexed".
  174. *
  175. * @param array $item_ids
  176. * The IDs of the indexed items.
  177. * @param SearchApiIndex $indexes
  178. * The index on which the items were indexed.
  179. *
  180. * @throws SearchApiDataSourceException
  181. * If the index doesn't use the same item type as this controller.
  182. */
  183. public function trackItemIndexed(array $item_ids, SearchApiIndex $index);
  184. /**
  185. * Stop tracking the index status for the given items on the given indexes.
  186. *
  187. * @param array $item_ids
  188. * The IDs of the removed items.
  189. * @param array $indexes
  190. * The indexes for which the deletions should be tracked.
  191. *
  192. * @throws SearchApiDataSourceException
  193. * If any of the indexes doesn't use the same item type as this controller.
  194. */
  195. public function trackItemDelete(array $item_ids, array $indexes);
  196. /**
  197. * Get a list of items that need to be indexed.
  198. *
  199. * If possible, completely unindexed items should be returned before items
  200. * that were indexed but later changed. Also, items that were changed longer
  201. * ago should be favored.
  202. *
  203. * @param SearchApiIndex $index
  204. * The index for which changed items should be returned.
  205. * @param $limit
  206. * The maximum number of items to return. Negative values mean "unlimited".
  207. *
  208. * @return array
  209. * The IDs of items that need to be indexed for the given index.
  210. */
  211. public function getChangedItems(SearchApiIndex $index, $limit = -1);
  212. /**
  213. * Get information on how many items have been indexed for a certain index.
  214. *
  215. * @param SearchApiIndex $index
  216. * The index whose index status should be returned.
  217. *
  218. * @return array
  219. * An associative array containing two keys (in this order):
  220. * - indexed: The number of items already indexed in their latest version.
  221. * - total: The total number of items that have to be indexed for this
  222. * index.
  223. *
  224. * @throws SearchApiDataSourceException
  225. * If the index doesn't use the same item type as this controller.
  226. */
  227. public function getIndexStatus(SearchApiIndex $index);
  228. /**
  229. * Get the entity type of items from this datasource.
  230. *
  231. * @return string|null
  232. * An entity type string if the items provided by this datasource are
  233. * entities; NULL otherwise.
  234. */
  235. public function getEntityType();
  236. }
  237. /**
  238. * Default base class for the SearchApiDataSourceControllerInterface.
  239. *
  240. * Contains default implementations for a number of methods which will be
  241. * similar for most data sources. Concrete data sources can decide to extend
  242. * this base class to save time, but can also implement the interface directly.
  243. *
  244. * A subclass will still have to provide implementations for the following
  245. * methods:
  246. * - getIdFieldInfo()
  247. * - loadItems()
  248. * - getMetadataWrapper() or getPropertyInfo()
  249. * - startTracking() or getAllItemIds()
  250. *
  251. * The table used by default for tracking the index status of items is
  252. * {search_api_item}. This can easily be changed, for example when an item type
  253. * has non-integer IDs, by changing the $table property.
  254. */
  255. abstract class SearchApiAbstractDataSourceController implements SearchApiDataSourceControllerInterface {
  256. /**
  257. * The item type for this controller instance.
  258. */
  259. protected $type;
  260. /**
  261. * The entity type for this controller instance.
  262. *
  263. * @var string|null
  264. *
  265. * @see getEntityType()
  266. */
  267. protected $entityType = NULL;
  268. /**
  269. * The info array for the item type, as specified via
  270. * hook_search_api_item_type_info().
  271. *
  272. * @var array
  273. */
  274. protected $info;
  275. /**
  276. * The table used for tracking items. Set to NULL on subclasses to disable
  277. * the default tracking for an item type, or change the property to use a
  278. * different table for tracking.
  279. *
  280. * @var string
  281. */
  282. protected $table = 'search_api_item';
  283. /**
  284. * When using the default tracking mechanism: the name of the column on
  285. * $this->table containing the item ID.
  286. *
  287. * @var string
  288. */
  289. protected $itemIdColumn = 'item_id';
  290. /**
  291. * When using the default tracking mechanism: the name of the column on
  292. * $this->table containing the index ID.
  293. *
  294. * @var string
  295. */
  296. protected $indexIdColumn = 'index_id';
  297. /**
  298. * When using the default tracking mechanism: the name of the column on
  299. * $this->table containing the indexing status.
  300. *
  301. * @var string
  302. */
  303. protected $changedColumn = 'changed';
  304. /**
  305. * Constructor for a data source controller.
  306. *
  307. * @param $type
  308. * The item type for which this controller is created.
  309. */
  310. public function __construct($type) {
  311. $this->type = $type;
  312. $this->info = search_api_get_item_type_info($type);
  313. if (!empty($this->info['entity_type'])) {
  314. $this->entityType = $this->info['entity_type'];
  315. }
  316. }
  317. /**
  318. * Get the entity type of items from this datasource.
  319. *
  320. * @return string|null
  321. * An entity type string if the items provided by this datasource are
  322. * entities; NULL otherwise.
  323. */
  324. public function getEntityType() {
  325. return $this->entityType;
  326. }
  327. /**
  328. * Get a metadata wrapper for the item type of this data source controller.
  329. *
  330. * @param $item
  331. * Unless NULL, an item of the item type for this controller to be wrapped.
  332. * @param array $info
  333. * Optionally, additional information that should be used for creating the
  334. * wrapper. Uses the same format as entity_metadata_wrapper().
  335. *
  336. * @return EntityMetadataWrapper
  337. * A wrapper for the item type of this data source controller, according to
  338. * the info array, and optionally loaded with the given data.
  339. *
  340. * @see entity_metadata_wrapper()
  341. */
  342. public function getMetadataWrapper($item = NULL, array $info = array()) {
  343. $info += $this->getPropertyInfo();
  344. return entity_metadata_wrapper($this->entityType ? $this->entityType : $this->type, $item, $info);
  345. }
  346. /**
  347. * Get the property info for this item type.
  348. *
  349. * This is a helper method for getMetadataWrapper() that can be used by
  350. * subclasses to specify the property information to use when creating a
  351. * metadata wrapper.
  352. *
  353. * The data structure uses largely the format specified in
  354. * hook_entity_property_info(). However, the first level of keys (containing
  355. * the entity types) is omitted, and the "property" key is called
  356. * "property info" instead. So, an example return value would look like this:
  357. *
  358. * @code
  359. * return array(
  360. * 'property info' => array(
  361. * 'foo' => array(
  362. * 'label' => t('Foo'),
  363. * 'type' => 'text',
  364. * ),
  365. * 'bar' => array(
  366. * 'label' => t('Bar'),
  367. * 'type' => 'list<integer>',
  368. * ),
  369. * ),
  370. * );
  371. * @endcode
  372. *
  373. * SearchApiExternalDataSourceController::getPropertyInfo() contains a working
  374. * example of this method.
  375. *
  376. * If the item type is an entity type, no additional property information is
  377. * required, the method will thus just return an empty array. You can still
  378. * use this to append additional properties to the entities, or the like,
  379. * though.
  380. *
  381. * @return array
  382. * Property information as specified by entity_metadata_wrapper().
  383. *
  384. * @see getMetadataWrapper()
  385. * @see hook_entity_property_info()
  386. */
  387. protected function getPropertyInfo() {
  388. // If this is an entity type, no additional property info is needed.
  389. if ($this->entityType) {
  390. return array();
  391. }
  392. throw new SearchApiDataSourceException(t('No known property information for type @type.', array('@type' => $this->type)));
  393. }
  394. /**
  395. * Get the unique ID of an item.
  396. *
  397. * @param $item
  398. * An item of this controller's type.
  399. *
  400. * @return
  401. * Either the unique ID of the item, or NULL if none is available.
  402. */
  403. public function getItemId($item) {
  404. $id_info = $this->getIdFieldInfo();
  405. $field = $id_info['key'];
  406. $wrapper = $this->getMetadataWrapper($item);
  407. if (!isset($wrapper->$field)) {
  408. return NULL;
  409. }
  410. $id = $wrapper->$field->value();
  411. return $id ? $id : NULL;
  412. }
  413. /**
  414. * Get a human-readable label for an item.
  415. *
  416. * @param $item
  417. * An item of this controller's type.
  418. *
  419. * @return
  420. * Either a human-readable label for the item, or NULL if none is available.
  421. */
  422. public function getItemLabel($item) {
  423. $label = $this->getMetadataWrapper($item)->label();
  424. return $label ? $label : NULL;
  425. }
  426. /**
  427. * Get a URL at which the item can be viewed on the web.
  428. *
  429. * @param $item
  430. * An item of this controller's type.
  431. *
  432. * @return
  433. * Either an array containing the 'path' and 'options' keys used to build
  434. * the URL of the item, and matching the signature of url(), or NULL if the
  435. * item has no URL of its own.
  436. */
  437. public function getItemUrl($item) {
  438. return NULL;
  439. }
  440. /**
  441. * Initialize tracking of the index status of items for the given indexes.
  442. *
  443. * All currently known items of this data source's type should be inserted
  444. * into the tracking table for the given indexes, with status "changed". If
  445. * items were already present, these should also be set to "changed" and not
  446. * be inserted again.
  447. *
  448. * @param array $indexes
  449. * The SearchApiIndex objects for which item tracking should be initialized.
  450. *
  451. * @throws SearchApiDataSourceException
  452. * If any of the indexes doesn't use the same item type as this controller.
  453. */
  454. public function startTracking(array $indexes) {
  455. if (!$this->table) {
  456. return;
  457. }
  458. // We first clear the tracking table for all indexes, so we can just insert
  459. // all items again without any key conflicts.
  460. $this->stopTracking($indexes);
  461. // Insert all items as new.
  462. $this->trackItemInsert($this->getAllItemIds(), $indexes);
  463. }
  464. /**
  465. * Helper method that can be used by subclasses instead of implementing startTracking().
  466. *
  467. * Returns the IDs of all items that are known for this controller's type.
  468. *
  469. * @return array
  470. * An array containing all item IDs for this type.
  471. */
  472. protected function getAllItemIds() {
  473. throw new SearchApiDataSourceException(t('Items not known for type @type.', array('@type' => $this->type)));
  474. }
  475. /**
  476. * Stop tracking of the index status of items for the given indexes.
  477. *
  478. * The tracking tables of the given indexes should be completely cleared.
  479. *
  480. * @param array $indexes
  481. * The SearchApiIndex objects for which item tracking should be stopped.
  482. *
  483. * @throws SearchApiDataSourceException
  484. * If any of the indexes doesn't use the same item type as this controller.
  485. */
  486. public function stopTracking(array $indexes) {
  487. if (!$this->table) {
  488. return;
  489. }
  490. // We could also use a single query with "IN" operator, but this method
  491. // will mostly be called with only one index.
  492. foreach ($indexes as $index) {
  493. $this->checkIndex($index);
  494. $query = db_delete($this->table)
  495. ->condition($this->indexIdColumn, $index->id)
  496. ->execute();
  497. }
  498. }
  499. /**
  500. * Start tracking the index status for the given items on the given indexes.
  501. *
  502. * @param array $item_ids
  503. * The IDs of new items to track.
  504. * @param array $indexes
  505. * The indexes for which items should be tracked.
  506. *
  507. * @throws SearchApiDataSourceException
  508. * If any of the indexes doesn't use the same item type as this controller.
  509. */
  510. public function trackItemInsert(array $item_ids, array $indexes) {
  511. if (!$this->table) {
  512. return;
  513. }
  514. // Since large amounts of items can overstrain the database, only add items
  515. // in chunks.
  516. foreach (array_chunk($item_ids, 1000) as $chunk) {
  517. $insert = db_insert($this->table)
  518. ->fields(array($this->itemIdColumn, $this->indexIdColumn, $this->changedColumn));
  519. foreach ($chunk as $item_id) {
  520. foreach ($indexes as $index) {
  521. $this->checkIndex($index);
  522. $insert->values(array(
  523. $this->itemIdColumn => $item_id,
  524. $this->indexIdColumn => $index->id,
  525. $this->changedColumn => 1,
  526. ));
  527. }
  528. }
  529. $insert->execute();
  530. }
  531. }
  532. /**
  533. * Set the tracking status of the given items to "changed"/"dirty".
  534. *
  535. * Unless $dequeue is set to TRUE, this operation is ignored for items whose
  536. * status is not "indexed".
  537. *
  538. * @param $item_ids
  539. * Either an array with the IDs of the changed items. Or FALSE to mark all
  540. * items as changed for the given indexes.
  541. * @param array $indexes
  542. * The indexes for which the change should be tracked.
  543. * @param $dequeue
  544. * If set to TRUE, also change the status of queued items.
  545. *
  546. * @throws SearchApiDataSourceException
  547. * If any of the indexes doesn't use the same item type as this controller.
  548. */
  549. public function trackItemChange($item_ids, array $indexes, $dequeue = FALSE) {
  550. if (!$this->table) {
  551. return;
  552. }
  553. $index_ids = array();
  554. foreach ($indexes as $index) {
  555. $this->checkIndex($index);
  556. $index_ids[] = $index->id;
  557. }
  558. $update = db_update($this->table)
  559. ->fields(array(
  560. $this->changedColumn => REQUEST_TIME,
  561. ))
  562. ->condition($this->indexIdColumn, $index_ids, 'IN')
  563. ->condition($this->changedColumn, 0, $dequeue ? '<=' : '=');
  564. if ($item_ids !== FALSE) {
  565. $update->condition($this->itemIdColumn, $item_ids, 'IN');
  566. }
  567. $update->execute();
  568. }
  569. /**
  570. * Set the tracking status of the given items to "queued".
  571. *
  572. * Queued items are not marked as "dirty" even when they are changed, and they
  573. * are not returned by the getChangedItems() method.
  574. *
  575. * @param $item_ids
  576. * Either an array with the IDs of the queued items. Or FALSE to mark all
  577. * items as queued for the given indexes.
  578. * @param SearchApiIndex $index
  579. * The index for which the items were queued.
  580. *
  581. * @throws SearchApiDataSourceException
  582. * If any of the indexes doesn't use the same item type as this controller.
  583. */
  584. public function trackItemQueued($item_ids, SearchApiIndex $index) {
  585. if (!$this->table) {
  586. return;
  587. }
  588. $update = db_update($this->table)
  589. ->fields(array(
  590. $this->changedColumn => -1,
  591. ))
  592. ->condition($this->indexIdColumn, $index->id);
  593. if ($item_ids !== FALSE) {
  594. $update->condition($this->itemIdColumn, $item_ids, 'IN');
  595. }
  596. $update->execute();
  597. }
  598. /**
  599. * Set the tracking status of the given items to "indexed".
  600. *
  601. * @param array $item_ids
  602. * The IDs of the indexed items.
  603. * @param SearchApiIndex $indexes
  604. * The index on which the items were indexed.
  605. *
  606. * @throws SearchApiDataSourceException
  607. * If the index doesn't use the same item type as this controller.
  608. */
  609. public function trackItemIndexed(array $item_ids, SearchApiIndex $index) {
  610. if (!$this->table) {
  611. return;
  612. }
  613. $this->checkIndex($index);
  614. db_update($this->table)
  615. ->fields(array(
  616. $this->changedColumn => 0,
  617. ))
  618. ->condition($this->itemIdColumn, $item_ids, 'IN')
  619. ->condition($this->indexIdColumn, $index->id)
  620. ->execute();
  621. }
  622. /**
  623. * Stop tracking the index status for the given items on the given indexes.
  624. *
  625. * @param array $item_ids
  626. * The IDs of the removed items.
  627. * @param array $indexes
  628. * The indexes for which the deletions should be tracked.
  629. *
  630. * @throws SearchApiDataSourceException
  631. * If any of the indexes doesn't use the same item type as this controller.
  632. */
  633. public function trackItemDelete(array $item_ids, array $indexes) {
  634. if (!$this->table) {
  635. return;
  636. }
  637. $index_ids = array();
  638. foreach ($indexes as $index) {
  639. $this->checkIndex($index);
  640. $index_ids[] = $index->id;
  641. }
  642. db_delete($this->table)
  643. ->condition($this->itemIdColumn, $item_ids, 'IN')
  644. ->condition($this->indexIdColumn, $index_ids, 'IN')
  645. ->execute();
  646. }
  647. /**
  648. * Get a list of items that need to be indexed.
  649. *
  650. * If possible, completely unindexed items should be returned before items
  651. * that were indexed but later changed. Also, items that were changed longer
  652. * ago should be favored.
  653. *
  654. * @param SearchApiIndex $index
  655. * The index for which changed items should be returned.
  656. * @param $limit
  657. * The maximum number of items to return. Negative values mean "unlimited".
  658. *
  659. * @return array
  660. * The IDs of items that need to be indexed for the given index.
  661. */
  662. public function getChangedItems(SearchApiIndex $index, $limit = -1) {
  663. if ($limit == 0) {
  664. return array();
  665. }
  666. $this->checkIndex($index);
  667. $select = db_select($this->table, 't');
  668. $select->addField('t', 'item_id');
  669. $select->condition($this->indexIdColumn, $index->id);
  670. $select->condition($this->changedColumn, 0, '>');
  671. $select->orderBy($this->changedColumn, 'ASC');
  672. if ($limit > 0) {
  673. $select->range(0, $limit);
  674. }
  675. return $select->execute()->fetchCol();
  676. }
  677. /**
  678. * Get information on how many items have been indexed for a certain index.
  679. *
  680. * @param SearchApiIndex $index
  681. * The index whose index status should be returned.
  682. *
  683. * @return array
  684. * An associative array containing two keys (in this order):
  685. * - indexed: The number of items already indexed in their latest version.
  686. * - total: The total number of items that have to be indexed for this
  687. * index.
  688. */
  689. public function getIndexStatus(SearchApiIndex $index) {
  690. if (!$this->table) {
  691. return array('indexed' => 0, 'total' => 0);
  692. }
  693. $this->checkIndex($index);
  694. $indexed = db_select($this->table, 'i')
  695. ->condition($this->indexIdColumn, $index->id)
  696. ->condition($this->changedColumn, 0)
  697. ->countQuery()
  698. ->execute()
  699. ->fetchField();
  700. $total = db_select($this->table, 'i')
  701. ->condition($this->indexIdColumn, $index->id)
  702. ->countQuery()
  703. ->execute()
  704. ->fetchField();
  705. return array('indexed' => $indexed, 'total' => $total);
  706. }
  707. /**
  708. * Helper method for ensuring that an index uses the same item type as this controller.
  709. *
  710. * @param SearchApiIndex $index
  711. * The index to check.
  712. *
  713. * @throws SearchApiDataSourceException
  714. * If the index doesn't use the same type as this controller.
  715. */
  716. protected function checkIndex(SearchApiIndex $index) {
  717. if ($index->item_type != $this->type) {
  718. $index_type = search_api_get_item_type_info($index->item_type);
  719. $index_type = empty($index_type['name']) ? $index->item_type : $index_type['name'];
  720. $msg = t(
  721. 'Invalid index @index of type @index_type passed to data source controller for type @this_type.',
  722. array('@index' => $index->name, '@index_type' => $index_type, '@this_type' => $this->info['name'])
  723. );
  724. throw new SearchApiDataSourceException($msg);
  725. }
  726. }
  727. }