Issue #3075695 by kim.pepper, andypost, jhodgdon, larowlan, pwolanin, Sam152: Move search_index() and related functions to a service
(cherry picked from commit 7dc65c5c5d
)
merge-requests/55/head
parent
61590bffa7
commit
fbd5b07c1b
|
@ -18,6 +18,7 @@ use Drupal\help\HelpSectionManager;
|
|||
use Drupal\help_topics\SearchableHelpInterface;
|
||||
use Drupal\search\Plugin\SearchIndexingInterface;
|
||||
use Drupal\search\Plugin\SearchPluginBase;
|
||||
use Drupal\search\SearchIndexInterface;
|
||||
use Drupal\search\SearchQuery;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
|
@ -91,6 +92,13 @@ class HelpSearch extends SearchPluginBase implements AccessibleInterface, Search
|
|||
*/
|
||||
protected $helpSectionManager;
|
||||
|
||||
/**
|
||||
* The search index.
|
||||
*
|
||||
* @var \Drupal\search\SearchIndexInterface
|
||||
*/
|
||||
protected $searchIndex;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -105,7 +113,8 @@ class HelpSearch extends SearchPluginBase implements AccessibleInterface, Search
|
|||
$container->get('messenger'),
|
||||
$container->get('current_user'),
|
||||
$container->get('state'),
|
||||
$container->get('plugin.manager.help_section')
|
||||
$container->get('plugin.manager.help_section'),
|
||||
$container->get('search.index')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -132,8 +141,10 @@ class HelpSearch extends SearchPluginBase implements AccessibleInterface, Search
|
|||
* The state object.
|
||||
* @param \Drupal\help\HelpSectionManager $help_section_manager
|
||||
* The help section manager.
|
||||
* @param \Drupal\search\SearchIndexInterface $search_index
|
||||
* The search index.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, Config $search_settings, LanguageManagerInterface $language_manager, MessengerInterface $messenger, AccountInterface $account, StateInterface $state, HelpSectionManager $help_section_manager) {
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, Config $search_settings, LanguageManagerInterface $language_manager, MessengerInterface $messenger, AccountInterface $account, StateInterface $state, HelpSectionManager $help_section_manager, SearchIndexInterface $search_index) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->database = $database;
|
||||
$this->searchSettings = $search_settings;
|
||||
|
@ -142,6 +153,7 @@ class HelpSearch extends SearchPluginBase implements AccessibleInterface, Search
|
|||
$this->account = $account;
|
||||
$this->state = $state;
|
||||
$this->helpSectionManager = $help_section_manager;
|
||||
$this->searchIndex = $search_index;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -327,35 +339,41 @@ class HelpSearch extends SearchPluginBase implements AccessibleInterface, Search
|
|||
$language_list = $this->languageManager->getLanguages(LanguageInterface::STATE_CONFIGURABLE);
|
||||
$section_plugins = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
$section_plugin_id = $item->section_plugin_id;
|
||||
if (!isset($section_plugins[$section_plugin_id])) {
|
||||
$section_plugins[$section_plugin_id] = $this->getSectionPlugin($section_plugin_id);
|
||||
}
|
||||
$words = [];
|
||||
try {
|
||||
foreach ($items as $item) {
|
||||
$section_plugin_id = $item->section_plugin_id;
|
||||
if (!isset($section_plugins[$section_plugin_id])) {
|
||||
$section_plugins[$section_plugin_id] = $this->getSectionPlugin($section_plugin_id);
|
||||
}
|
||||
|
||||
if (!$section_plugins[$section_plugin_id]) {
|
||||
$this->removeItemsFromIndex($item->sid);
|
||||
continue;
|
||||
}
|
||||
if (!$section_plugins[$section_plugin_id]) {
|
||||
$this->removeItemsFromIndex($item->sid);
|
||||
continue;
|
||||
}
|
||||
|
||||
$section_plugin = $section_plugins[$section_plugin_id];
|
||||
search_index_clear($this->getType(), $item->sid);
|
||||
foreach ($language_list as $langcode => $language) {
|
||||
$topic = $section_plugin->renderTopicForSearch($item->topic_id, $language);
|
||||
if ($topic) {
|
||||
// Index the title plus body text.
|
||||
$text = '<h1>' . $topic['title'] . '</h1>' . "\n" . $topic['text'];
|
||||
search_index($this->getType(), $item->sid, $langcode, $text);
|
||||
$section_plugin = $section_plugins[$section_plugin_id];
|
||||
$this->searchIndex->clear($this->getType(), $item->sid);
|
||||
foreach ($language_list as $langcode => $language) {
|
||||
$topic = $section_plugin->renderTopicForSearch($item->topic_id, $language);
|
||||
if ($topic) {
|
||||
// Index the title plus body text.
|
||||
$text = '<h1>' . $topic['title'] . '</h1>' . "\n" . $topic['text'];
|
||||
$words += $this->searchIndex->index($this->getType(), $item->sid, $langcode, $text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$this->searchIndex->updateWordWeights($words);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function indexClear() {
|
||||
search_index_clear($this->getType());
|
||||
$this->searchIndex->clear($this->getType());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -419,7 +437,7 @@ class HelpSearch extends SearchPluginBase implements AccessibleInterface, Search
|
|||
*/
|
||||
public function markForReindex() {
|
||||
$this->updateTopicList();
|
||||
search_mark_for_reindex($this->getType());
|
||||
$this->searchIndex->markForReindex($this->getType());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -466,7 +484,7 @@ class HelpSearch extends SearchPluginBase implements AccessibleInterface, Search
|
|||
// Remove items from the search tables individually, as there is no bulk
|
||||
// function to delete items from the search index.
|
||||
foreach ($sids as $sid) {
|
||||
search_index_clear($this->getType(), $sid);
|
||||
$this->searchIndex->clear($this->getType(), $sid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1421,7 +1421,7 @@ function node_configurable_language_delete(ConfigurableLanguageInterface $langua
|
|||
function node_reindex_node_search($nid) {
|
||||
if (\Drupal::moduleHandler()->moduleExists('search')) {
|
||||
// Reindex node context indexed by the node module search plugin.
|
||||
search_mark_for_reindex('node_search', $nid);
|
||||
\Drupal::service('search.index')->markForReindex('node_search', $nid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -163,9 +163,11 @@ class Node extends EditorialContentEntityBase implements NodeInterface {
|
|||
parent::preDelete($storage, $entities);
|
||||
|
||||
// Ensure that all nodes deleted are removed from the search index.
|
||||
if (\Drupal::moduleHandler()->moduleExists('search')) {
|
||||
if (\Drupal::hasService('search.index')) {
|
||||
/** @var \Drupal\search\SearchIndexInterface $search_index */
|
||||
$search_index = \Drupal::service('search.index');
|
||||
foreach ($entities as $entity) {
|
||||
search_index_clear('node_search', $entity->nid->value);
|
||||
$search_index->clear('node_search', $entity->nid->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
namespace Drupal\node\Plugin\Search;
|
||||
|
||||
use Drupal\Core\Access\AccessibleInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Database\Query\SelectExtender;
|
||||
use Drupal\Core\Database\StatementInterface;
|
||||
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
|
||||
|
@ -15,14 +17,13 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Access\AccessibleInterface;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\search\Plugin\ConfigurableSearchPluginBase;
|
||||
use Drupal\search\Plugin\SearchIndexingInterface;
|
||||
use Drupal\search\SearchIndexInterface;
|
||||
use Drupal\Search\SearchQuery;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
|
@ -98,6 +99,13 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* The search index.
|
||||
*
|
||||
* @var \Drupal\search\SearchIndexInterface
|
||||
*/
|
||||
protected $searchIndex;
|
||||
|
||||
/**
|
||||
* An array of additional rankings from hook_ranking().
|
||||
*
|
||||
|
@ -153,7 +161,8 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
$container->get('renderer'),
|
||||
$container->get('messenger'),
|
||||
$container->get('current_user'),
|
||||
$container->get('database.replica')
|
||||
$container->get('database.replica'),
|
||||
$container->get('search.index')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -184,8 +193,10 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
* The $account object to use for checking for access to advanced search.
|
||||
* @param \Drupal\Core\Database\Connection|null $database_replica
|
||||
* (Optional) the replica database connection.
|
||||
* @param \Drupal\search\SearchIndexInterface $search_index
|
||||
* The search index.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler, Config $search_settings, LanguageManagerInterface $language_manager, RendererInterface $renderer, MessengerInterface $messenger, AccountInterface $account = NULL, Connection $database_replica = NULL) {
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler, Config $search_settings, LanguageManagerInterface $language_manager, RendererInterface $renderer, MessengerInterface $messenger, AccountInterface $account = NULL, Connection $database_replica = NULL, SearchIndexInterface $search_index = NULL) {
|
||||
$this->database = $database;
|
||||
$this->databaseReplica = $database_replica ?: $database;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
|
@ -198,6 +209,11 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->addCacheTags(['node_list']);
|
||||
if (!$search_index) {
|
||||
@trigger_error('Calling NodeSearch::__construct() without the $search_index argument is deprecated in drupal:8.8.0 and is required in drupal:9.0.0. See https://www.drupal.org/node/3075696', E_USER_DEPRECATED);
|
||||
$search_index = \Drupal::service('search.index');
|
||||
}
|
||||
$this->searchIndex = $search_index;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -478,8 +494,14 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
}
|
||||
|
||||
$node_storage = $this->entityTypeManager->getStorage('node');
|
||||
foreach ($node_storage->loadMultiple($nids) as $node) {
|
||||
$this->indexNode($node);
|
||||
$words = [];
|
||||
try {
|
||||
foreach ($node_storage->loadMultiple($nids) as $node) {
|
||||
$this->indexNode($node, $words);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$this->searchIndex->updateWordWeights($words);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -488,8 +510,10 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node to index.
|
||||
* @param array $words
|
||||
* Words that need updating after the index run.
|
||||
*/
|
||||
protected function indexNode(NodeInterface $node) {
|
||||
protected function indexNode(NodeInterface $node, array &$words) {
|
||||
$languages = $node->getTranslationLanguages();
|
||||
$node_render = $this->entityTypeManager->getViewBuilder('node');
|
||||
|
||||
|
@ -516,7 +540,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
}
|
||||
|
||||
// Update index, using search index "type" equal to the plugin ID.
|
||||
search_index($this->getPluginId(), $node->id(), $language->getId(), $text);
|
||||
$words += $this->searchIndex->index($this->getPluginId(), $node->id(), $language->getId(), $text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,7 +550,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
public function indexClear() {
|
||||
// All NodeSearch pages share a common search index "type" equal to
|
||||
// the plugin ID.
|
||||
search_index_clear($this->getPluginId());
|
||||
$this->searchIndex->clear($this->getPluginId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -535,7 +559,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
public function markForReindex() {
|
||||
// All NodeSearch pages share a common search index "type" equal to
|
||||
// the plugin ID.
|
||||
search_mark_for_reindex($this->getPluginId());
|
||||
$this->searchIndex->markForReindex($this->getPluginId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,13 +5,11 @@
|
|||
* Enables site-wide keyword searching.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Matches all 'N' Unicode character classes (numbers)
|
||||
|
@ -140,35 +138,15 @@ function search_preprocess_block(&$variables) {
|
|||
* @param string|null $langcode
|
||||
* (optional) Language code of the item to remove from the search index. If
|
||||
* omitted, all items matching $sid and $type are cleared.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use
|
||||
* \Drupal\search\SearchIndex::clear() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3075696
|
||||
*/
|
||||
function search_index_clear($type = NULL, $sid = NULL, $langcode = NULL) {
|
||||
$connection = \Drupal::database();
|
||||
$query_index = $connection->delete('search_index');
|
||||
$query_dataset = $connection->delete('search_dataset');
|
||||
if ($type) {
|
||||
$query_index->condition('type', $type);
|
||||
$query_dataset->condition('type', $type);
|
||||
if ($sid) {
|
||||
$query_index->condition('sid', $sid);
|
||||
$query_dataset->condition('sid', $sid);
|
||||
if ($langcode) {
|
||||
$query_index->condition('langcode', $langcode);
|
||||
$query_dataset->condition('langcode', $langcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query_index->execute();
|
||||
$query_dataset->execute();
|
||||
|
||||
if ($type) {
|
||||
// Invalidate all render cache items that contain data from this index.
|
||||
Cache::invalidateTags(['search_index:' . $type]);
|
||||
}
|
||||
else {
|
||||
// Invalidate all render cache items that contain data from any index.
|
||||
Cache::invalidateTags(['search_index']);
|
||||
}
|
||||
@trigger_error("search_index_clear() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\search\SearchIndex::clear() instead. See https://www.drupal.org/node/3075696", E_USER_DEPRECATED);
|
||||
\Drupal::service('search.index')->clear($type, $sid, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,14 +154,17 @@ function search_index_clear($type = NULL, $sid = NULL, $langcode = NULL) {
|
|||
*
|
||||
* This is used during indexing (cron). Words that are dirty have outdated
|
||||
* total counts in the search_total table, and need to be recounted.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use custom
|
||||
* implementation of \Drupal\search\SearchIndexInterface instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3075696
|
||||
*/
|
||||
function search_dirty($word = NULL) {
|
||||
$dirty = &drupal_static(__FUNCTION__, []);
|
||||
if ($word !== NULL) {
|
||||
$dirty[$word] = TRUE;
|
||||
}
|
||||
else {
|
||||
return $dirty;
|
||||
@trigger_error("search_dirty() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use custom implementation of \Drupal\search\SearchIndexInterface instead. See https://www.drupal.org/node/3075696", E_USER_DEPRECATED);
|
||||
// Keep return result type for backward compatibility.
|
||||
if ($word === NULL) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,14 +173,8 @@ function search_dirty($word = NULL) {
|
|||
*
|
||||
* Fires updateIndex() in the plugins for all indexable active search pages,
|
||||
* and cleans up dirty words.
|
||||
*
|
||||
* @see search_dirty()
|
||||
*/
|
||||
function search_cron() {
|
||||
// We register a shutdown function to ensure that search_total is always up
|
||||
// to date.
|
||||
drupal_register_shutdown_function('search_update_totals');
|
||||
|
||||
/** @var $search_page_repository \Drupal\search\SearchPageRepositoryInterface */
|
||||
$search_page_repository = \Drupal::service('search.search_page_repository');
|
||||
foreach ($search_page_repository->getIndexableSearchPages() as $entity) {
|
||||
|
@ -212,34 +187,14 @@ function search_cron() {
|
|||
*
|
||||
* This function is called on shutdown to ensure that {search_total} is always
|
||||
* up to date (even if cron times out or otherwise fails).
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use custom
|
||||
* implementation of \Drupal\search\SearchIndexInterface instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3075696
|
||||
*/
|
||||
function search_update_totals() {
|
||||
$connection = \Drupal::database();
|
||||
$replica = \Drupal::service('database.replica');
|
||||
// Update word IDF (Inverse Document Frequency) counts for new/changed words.
|
||||
foreach (search_dirty() as $word => $dummy) {
|
||||
// Get total count
|
||||
$total = $replica->query("SELECT SUM(score) FROM {search_index} WHERE word = :word", [':word' => $word])->fetchField();
|
||||
// Apply Zipf's law to equalize the probability distribution.
|
||||
$total = log10(1 + 1 / (max(1, $total)));
|
||||
$connection->merge('search_total')
|
||||
->key('word', $word)
|
||||
->fields(['count' => $total])
|
||||
->execute();
|
||||
}
|
||||
// Find words that were deleted from search_index, but are still in
|
||||
// search_total. We use a LEFT JOIN between the two tables and keep only the
|
||||
// rows which fail to join.
|
||||
$result = $replica->query("SELECT t.word AS realword, i.word FROM {search_total} t LEFT JOIN {search_index} i ON t.word = i.word WHERE i.word IS NULL");
|
||||
$or = new Condition('OR');
|
||||
foreach ($result as $word) {
|
||||
$or->condition('word', $word->realword);
|
||||
}
|
||||
if (count($or) > 0) {
|
||||
$connection->delete('search_total')
|
||||
->condition($or)
|
||||
->execute();
|
||||
}
|
||||
@trigger_error("search_update_totals() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use custom implementation of \Drupal\search\SearchIndexInterface instead. See https://www.drupal.org/node/3075696", E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -439,137 +394,15 @@ function search_invoke_preprocess(&$text, $langcode = NULL) {
|
|||
* The content of this item. Must be a piece of HTML or plain text.
|
||||
*
|
||||
* @ingroup search
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use
|
||||
* \Drupal\search\SearchIndex::index() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3075696
|
||||
*/
|
||||
function search_index($type, $sid, $langcode, $text) {
|
||||
$minimum_word_size = \Drupal::config('search.settings')->get('index.minimum_word_size');
|
||||
|
||||
// Multipliers for scores of words inside certain HTML tags. The weights are
|
||||
// stored in config so that modules can overwrite the default weights.
|
||||
// Note: 'a' must be included for link ranking to work.
|
||||
$tags = \Drupal::config('search.settings')->get('index.tag_weights');
|
||||
|
||||
// Strip off all ignored tags to speed up processing, but insert space before
|
||||
// and after them to keep word boundaries.
|
||||
$text = str_replace(['<', '>'], [' <', '> '], $text);
|
||||
$text = strip_tags($text, '<' . implode('><', array_keys($tags)) . '>');
|
||||
|
||||
// Split HTML tags from plain text.
|
||||
$split = preg_split('/\s*<([^>]+?)>\s*/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
// Note: PHP ensures the array consists of alternating delimiters and literals
|
||||
// and begins and ends with a literal (inserting $null as required).
|
||||
|
||||
// Odd/even counter. Tag or no tag.
|
||||
$tag = FALSE;
|
||||
// Starting score per word.
|
||||
$score = 1;
|
||||
// Accumulator for cleaned up data.
|
||||
$accum = ' ';
|
||||
// Stack with open tags.
|
||||
$tagstack = [];
|
||||
// Counter for consecutive words.
|
||||
$tagwords = 0;
|
||||
// Focus state.
|
||||
$focus = 1;
|
||||
|
||||
// Accumulator for words for index.
|
||||
$scored_words = [];
|
||||
|
||||
foreach ($split as $value) {
|
||||
if ($tag) {
|
||||
// Increase or decrease score per word based on tag
|
||||
list($tagname) = explode(' ', $value, 2);
|
||||
$tagname = mb_strtolower($tagname);
|
||||
// Closing or opening tag?
|
||||
if ($tagname[0] == '/') {
|
||||
$tagname = substr($tagname, 1);
|
||||
// If we encounter unexpected tags, reset score to avoid incorrect boosting.
|
||||
if (!count($tagstack) || $tagstack[0] != $tagname) {
|
||||
$tagstack = [];
|
||||
$score = 1;
|
||||
}
|
||||
else {
|
||||
// Remove from tag stack and decrement score
|
||||
$score = max(1, $score - $tags[array_shift($tagstack)]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isset($tagstack[0]) && $tagstack[0] == $tagname) {
|
||||
// None of the tags we look for make sense when nested identically.
|
||||
// If they are, it's probably broken HTML.
|
||||
$tagstack = [];
|
||||
$score = 1;
|
||||
}
|
||||
else {
|
||||
// Add to open tag stack and increment score
|
||||
array_unshift($tagstack, $tagname);
|
||||
$score += $tags[$tagname];
|
||||
}
|
||||
}
|
||||
// A tag change occurred, reset counter.
|
||||
$tagwords = 0;
|
||||
}
|
||||
else {
|
||||
// Note: use of PREG_SPLIT_DELIM_CAPTURE above will introduce empty values
|
||||
if ($value != '') {
|
||||
$words = search_index_split($value, $langcode);
|
||||
foreach ($words as $word) {
|
||||
// Add word to accumulator
|
||||
$accum .= $word . ' ';
|
||||
// Check word length.
|
||||
if (is_numeric($word) || mb_strlen($word) >= $minimum_word_size) {
|
||||
if (!isset($scored_words[$word])) {
|
||||
$scored_words[$word] = 0;
|
||||
}
|
||||
$scored_words[$word] += $score * $focus;
|
||||
// Focus is a decaying value in terms of the amount of unique words up to this point.
|
||||
// From 100 words and more, it decays, to e.g. 0.5 at 500 words and 0.3 at 1000 words.
|
||||
$focus = min(1, .01 + 3.5 / (2 + count($scored_words) * .015));
|
||||
}
|
||||
$tagwords++;
|
||||
// Too many words inside a single tag probably mean a tag was accidentally left open.
|
||||
if (count($tagstack) && $tagwords >= 15) {
|
||||
$tagstack = [];
|
||||
$score = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$tag = !$tag;
|
||||
}
|
||||
|
||||
// Remove the item $sid from the search index, and invalidate the relevant
|
||||
// cache tags.
|
||||
search_index_clear($type, $sid, $langcode);
|
||||
|
||||
$connection = \Drupal::database();
|
||||
// Insert cleaned up data into dataset
|
||||
$connection->insert('search_dataset')
|
||||
->fields([
|
||||
'sid' => $sid,
|
||||
'langcode' => $langcode,
|
||||
'type' => $type,
|
||||
'data' => $accum,
|
||||
'reindex' => 0,
|
||||
])
|
||||
->execute();
|
||||
|
||||
// Insert results into search index
|
||||
foreach ($scored_words as $word => $score) {
|
||||
// If a word already exists in the database, its score gets increased
|
||||
// appropriately. If not, we create a new record with the appropriate
|
||||
// starting score.
|
||||
$connection->merge('search_index')
|
||||
->keys([
|
||||
'word' => $word,
|
||||
'sid' => $sid,
|
||||
'langcode' => $langcode,
|
||||
'type' => $type,
|
||||
])
|
||||
->fields(['score' => $score])
|
||||
->expression('score', 'score + :score', [':score' => $score])
|
||||
->execute();
|
||||
search_dirty($word);
|
||||
}
|
||||
@trigger_error("search_index() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\search\SearchIndex::index() instead. See https://www.drupal.org/node/3075696", E_USER_DEPRECATED);
|
||||
\Drupal::service('search.index')->index($type, $sid, $langcode, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -589,25 +422,15 @@ function search_index($type, $sid, $langcode, $text) {
|
|||
* @param string $langcode
|
||||
* (optional) The language code to clear. If omitted, everything matching
|
||||
* $type and $sid is marked.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use
|
||||
* \Drupal\search\SearchIndex::markForReindex() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3075696
|
||||
*/
|
||||
function search_mark_for_reindex($type = NULL, $sid = NULL, $langcode = NULL) {
|
||||
$query = \Drupal::database()->update('search_dataset')
|
||||
->fields(['reindex' => REQUEST_TIME])
|
||||
// Only mark items that were not previously marked for reindex, so that
|
||||
// marked items maintain their priority by request time.
|
||||
->condition('reindex', 0);
|
||||
|
||||
if ($type) {
|
||||
$query->condition('type', $type);
|
||||
if ($sid) {
|
||||
$query->condition('sid', $sid);
|
||||
if ($langcode) {
|
||||
$query->condition('langcode', $langcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
@trigger_error("search_mark_for_reindex() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\search\SearchIndex::markForReindex() instead. See https://www.drupal.org/node/3075696", E_USER_DEPRECATED);
|
||||
\Drupal::service('search.index')->markForReindex($type, $sid, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,3 +6,7 @@ services:
|
|||
search.search_page_repository:
|
||||
class: Drupal\search\SearchPageRepository
|
||||
arguments: ['@config.factory', '@entity_type.manager']
|
||||
|
||||
search.index:
|
||||
class: Drupal\search\SearchIndex
|
||||
arguments: ['@config.factory', '@database','@database.replica', '@cache_tags.invalidator']
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\search\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown for search index errors.
|
||||
*/
|
||||
class SearchIndexException extends \RuntimeException {}
|
|
@ -30,8 +30,8 @@ interface SearchIndexingInterface {
|
|||
* This method is called every cron run if the plugin has been set as
|
||||
* an active search module on the Search settings page
|
||||
* (admin/config/search/pages). It allows your module to add items to the
|
||||
* built-in search index using search_index(), or to add them to your module's
|
||||
* own indexing mechanism.
|
||||
* built-in search index by calling the index() method on the search.index
|
||||
* service class, or to add them to your module's own indexing mechanism.
|
||||
*
|
||||
* When implementing this method, your module should index content items that
|
||||
* were modified or added since the last run. There is a time limit for cron,
|
||||
|
@ -49,10 +49,10 @@ interface SearchIndexingInterface {
|
|||
*
|
||||
* When a request is made to clear all items from the search index related to
|
||||
* this plugin, this method will be called. If this plugin uses the default
|
||||
* search index, this method can call search_index_clear($type) to remove
|
||||
* indexed items from the search database.
|
||||
* search index, this method can call clear($type) method on the search.index
|
||||
* service class to remove indexed items from the search database.
|
||||
*
|
||||
* @see search_index_clear()
|
||||
* @see \Drupal\search\SearchIndexInterface::clear()
|
||||
*/
|
||||
public function indexClear();
|
||||
|
||||
|
@ -61,11 +61,11 @@ interface SearchIndexingInterface {
|
|||
*
|
||||
* When a request is made to mark all items from the search index related to
|
||||
* this plugin for reindexing, this method will be called. If this plugin uses
|
||||
* the default search index, this method can call
|
||||
* search_mark_for_reindex($type) to mark the items in the search database for
|
||||
* reindexing.
|
||||
* the default search index, this method can call markForReindex($type) method
|
||||
* on the search.index service class to mark the items in the search database
|
||||
* for reindexing.
|
||||
*
|
||||
* @see search_mark_for_reindex()
|
||||
* @see \Drupal\search\SearchIndexInterface::markForReindex()
|
||||
*/
|
||||
public function markForReindex();
|
||||
|
||||
|
|
|
@ -66,8 +66,8 @@ interface SearchInterface extends PluginInspectionInterface {
|
|||
* The type used by this search plugin in the search index, or NULL if this
|
||||
* plugin does not use the search index.
|
||||
*
|
||||
* @see search_index()
|
||||
* @see search_index_clear()
|
||||
* @see \Drupal\search\SearchIndexInterface::index()
|
||||
* @see \Drupal\search\SearchIndexInterface::clear()
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\search;
|
||||
|
||||
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\search\Exception\SearchIndexException;
|
||||
|
||||
/**
|
||||
* Provides search index management functions.
|
||||
*/
|
||||
class SearchIndex implements SearchIndexInterface {
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The database replica connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $replica;
|
||||
|
||||
/**
|
||||
* The cache tags invalidator.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
|
||||
*/
|
||||
protected $cacheTagsInvalidator;
|
||||
|
||||
/**
|
||||
* SearchIndex constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
* @param \Drupal\Core\Database\Connection $replica
|
||||
* The database replica connection.
|
||||
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_invalidator
|
||||
* The cache tags invalidator.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, Connection $connection, Connection $replica, CacheTagsInvalidatorInterface $cache_tags_invalidator) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->connection = $connection;
|
||||
$this->replica = $replica;
|
||||
$this->cacheTagsInvalidator = $cache_tags_invalidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index($type, $sid, $langcode, $text, $update_weights = TRUE) {
|
||||
$settings = $this->configFactory->get('search.settings');
|
||||
$minimum_word_size = $settings->get('index.minimum_word_size');
|
||||
|
||||
// Keep track of the words that need to have their weights updated.
|
||||
$current_words = [];
|
||||
|
||||
// Multipliers for scores of words inside certain HTML tags. The weights are
|
||||
// stored in config so that modules can overwrite the default weights.
|
||||
// Note: 'a' must be included for link ranking to work.
|
||||
$tags = $settings->get('index.tag_weights');
|
||||
|
||||
// Strip off all ignored tags to speed up processing, but insert space
|
||||
// before and after them to keep word boundaries.
|
||||
$text = str_replace(['<', '>'], [' <', '> '], $text);
|
||||
$text = strip_tags($text, '<' . implode('><', array_keys($tags)) . '>');
|
||||
|
||||
// Split HTML tags from plain text.
|
||||
$split = preg_split('/\s*<([^>]+?)>\s*/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
// Note: PHP ensures the array consists of alternating delimiters and
|
||||
// literals and begins and ends with a literal (inserting $null as
|
||||
// required).
|
||||
// Odd/even counter. Tag or no tag.
|
||||
$tag = FALSE;
|
||||
// Starting score per word.
|
||||
$score = 1;
|
||||
// Accumulator for cleaned up data.
|
||||
$accum = ' ';
|
||||
// Stack with open tags.
|
||||
$tagstack = [];
|
||||
// Counter for consecutive words.
|
||||
$tagwords = 0;
|
||||
// Focus state.
|
||||
$focus = 1;
|
||||
|
||||
// Accumulator for words for index.
|
||||
$scored_words = [];
|
||||
|
||||
foreach ($split as $value) {
|
||||
if ($tag) {
|
||||
// Increase or decrease score per word based on tag.
|
||||
list($tagname) = explode(' ', $value, 2);
|
||||
$tagname = mb_strtolower($tagname);
|
||||
// Closing or opening tag?
|
||||
if ($tagname[0] == '/') {
|
||||
$tagname = substr($tagname, 1);
|
||||
// If we encounter unexpected tags, reset score to avoid incorrect
|
||||
// boosting.
|
||||
if (!count($tagstack) || $tagstack[0] != $tagname) {
|
||||
$tagstack = [];
|
||||
$score = 1;
|
||||
}
|
||||
else {
|
||||
// Remove from tag stack and decrement score.
|
||||
$score = max(1, $score - $tags[array_shift($tagstack)]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isset($tagstack[0]) && $tagstack[0] == $tagname) {
|
||||
// None of the tags we look for make sense when nested identically.
|
||||
// If they are, it's probably broken HTML.
|
||||
$tagstack = [];
|
||||
$score = 1;
|
||||
}
|
||||
else {
|
||||
// Add to open tag stack and increment score.
|
||||
array_unshift($tagstack, $tagname);
|
||||
$score += $tags[$tagname];
|
||||
}
|
||||
}
|
||||
// A tag change occurred, reset counter.
|
||||
$tagwords = 0;
|
||||
}
|
||||
else {
|
||||
// Note: use of PREG_SPLIT_DELIM_CAPTURE above will introduce empty
|
||||
// values.
|
||||
if ($value != '') {
|
||||
$words = search_index_split($value, $langcode);
|
||||
foreach ($words as $word) {
|
||||
// Add word to accumulator.
|
||||
$accum .= $word . ' ';
|
||||
// Check word length.
|
||||
if (is_numeric($word) || mb_strlen($word) >= $minimum_word_size) {
|
||||
if (!isset($scored_words[$word])) {
|
||||
$scored_words[$word] = 0;
|
||||
}
|
||||
$scored_words[$word] += $score * $focus;
|
||||
// Focus is a decaying value in terms of the amount of unique
|
||||
// words up to this point. From 100 words and more, it decays, to
|
||||
// e.g. 0.5 at 500 words and 0.3 at 1000 words.
|
||||
$focus = min(1, .01 + 3.5 / (2 + count($scored_words) * .015));
|
||||
}
|
||||
$tagwords++;
|
||||
// Too many words inside a single tag probably mean a tag was
|
||||
// accidentally left open.
|
||||
if (count($tagstack) && $tagwords >= 15) {
|
||||
$tagstack = [];
|
||||
$score = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$tag = !$tag;
|
||||
}
|
||||
|
||||
// Remove the item $sid from the search index, and invalidate the relevant
|
||||
// cache tags.
|
||||
$this->clear($type, $sid, $langcode);
|
||||
|
||||
try {
|
||||
// Insert cleaned up data into dataset.
|
||||
$this->connection->insert('search_dataset')
|
||||
->fields([
|
||||
'sid' => $sid,
|
||||
'langcode' => $langcode,
|
||||
'type' => $type,
|
||||
'data' => $accum,
|
||||
'reindex' => 0,
|
||||
])
|
||||
->execute();
|
||||
|
||||
// Insert results into search index.
|
||||
foreach ($scored_words as $word => $score) {
|
||||
// If a word already exists in the database, its score gets increased
|
||||
// appropriately. If not, we create a new record with the appropriate
|
||||
// starting score.
|
||||
$this->connection->merge('search_index')
|
||||
->keys([
|
||||
'word' => $word,
|
||||
'sid' => $sid,
|
||||
'langcode' => $langcode,
|
||||
'type' => $type,
|
||||
])
|
||||
->fields(['score' => $score])
|
||||
->expression('score', 'score + :score', [':score' => $score])
|
||||
->execute();
|
||||
$current_words[$word] = TRUE;
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
throw new SearchIndexException("Failed to insert dataset in index for type '$type', sid '$sid' and langcode '$langcode'", 0, $e);
|
||||
}
|
||||
finally {
|
||||
if ($update_weights) {
|
||||
$this->updateWordWeights($current_words);
|
||||
}
|
||||
}
|
||||
return $current_words;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clear($type = NULL, $sid = NULL, $langcode = NULL) {
|
||||
|
||||
try {
|
||||
$query_index = $this->connection->delete('search_index');
|
||||
$query_dataset = $this->connection->delete('search_dataset');
|
||||
if ($type) {
|
||||
$query_index->condition('type', $type);
|
||||
$query_dataset->condition('type', $type);
|
||||
if ($sid) {
|
||||
$query_index->condition('sid', $sid);
|
||||
$query_dataset->condition('sid', $sid);
|
||||
if ($langcode) {
|
||||
$query_index->condition('langcode', $langcode);
|
||||
$query_dataset->condition('langcode', $langcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
$query_index->execute();
|
||||
$query_dataset->execute();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
throw new SearchIndexException("Failed to clear index for type '$type', sid '$sid' and langcode '$langcode'", 0, $e);
|
||||
}
|
||||
if ($type) {
|
||||
// Invalidate all render cache items that contain data from this index.
|
||||
$this->cacheTagsInvalidator->invalidateTags(['search_index:' . $type]);
|
||||
}
|
||||
else {
|
||||
// Invalidate all render cache items that contain data from any index.
|
||||
$this->cacheTagsInvalidator->invalidateTags(['search_index']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function markForReindex($type = NULL, $sid = NULL, $langcode = NULL) {
|
||||
|
||||
try {
|
||||
$query = $this->connection->update('search_dataset')
|
||||
->fields(['reindex' => REQUEST_TIME])
|
||||
// Only mark items that were not previously marked for reindex, so that
|
||||
// marked items maintain their priority by request time.
|
||||
->condition('reindex', 0);
|
||||
if ($type) {
|
||||
$query->condition('type', $type);
|
||||
if ($sid) {
|
||||
$query->condition('sid', $sid);
|
||||
if ($langcode) {
|
||||
$query->condition('langcode', $langcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
throw new SearchIndexException("Failed to mark index for re-indexing for type '$type', sid '$sid' and langcode '$langcode'", 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateWordWeights(array $words) {
|
||||
try {
|
||||
// Update word IDF (Inverse Document Frequency) counts for new/changed
|
||||
// words.
|
||||
$words = array_keys($words);
|
||||
foreach ($words as $word) {
|
||||
// Get total count.
|
||||
$total = $this->replica->query("SELECT SUM(score) FROM {search_index} WHERE word = :word", [':word' => $word])
|
||||
->fetchField();
|
||||
// Apply Zipf's law to equalize the probability distribution.
|
||||
$total = log10(1 + 1 / (max(1, $total)));
|
||||
$this->connection->merge('search_total')
|
||||
->key('word', $word)
|
||||
->fields(['count' => $total])
|
||||
->execute();
|
||||
}
|
||||
// Find words that were deleted from search_index, but are still in
|
||||
// search_total. We use a LEFT JOIN between the two tables and keep only
|
||||
// the rows which fail to join.
|
||||
$result = $this->replica->query("SELECT t.word AS realword, i.word FROM {search_total} t LEFT JOIN {search_index} i ON t.word = i.word WHERE i.word IS NULL");
|
||||
$or = new Condition('OR');
|
||||
foreach ($result as $word) {
|
||||
$or->condition('word', $word->realword);
|
||||
}
|
||||
if (count($or) > 0) {
|
||||
$this->connection->delete('search_total')
|
||||
->condition($or)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
throw new SearchIndexException("Failed to update totals for index words.", 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\search;
|
||||
|
||||
/**
|
||||
* Provides search index management functions.
|
||||
*
|
||||
* @ingroup search
|
||||
*/
|
||||
interface SearchIndexInterface {
|
||||
|
||||
/**
|
||||
* Updates the full-text search index for a particular item.
|
||||
*
|
||||
* @param string $type
|
||||
* The plugin ID or other machine-readable type of this item,
|
||||
* which should be less than 64 bytes.
|
||||
* @param int $sid
|
||||
* An ID number identifying this particular item (e.g., node ID).
|
||||
* @param string $langcode
|
||||
* Language code for the language of the text being indexed.
|
||||
* @param string $text
|
||||
* The content of this item. Must be a piece of HTML or plain text.
|
||||
* @param bool $update_weights
|
||||
* (optional) TRUE if word weights should be updated. FALSE otherwise.
|
||||
*
|
||||
* @return string[]
|
||||
* The words to be updated.
|
||||
*
|
||||
* @throws \Drupal\search\Exception\SearchIndexException
|
||||
* If there is an error indexing the text.
|
||||
*/
|
||||
public function index($type, $sid, $langcode, $text, $update_weights = TRUE);
|
||||
|
||||
/**
|
||||
* Clears either a part of, or the entire search index.
|
||||
*
|
||||
* This function is meant for use by search page plugins, or for building a
|
||||
* user interface that lets users clear all or parts of the search index.
|
||||
*
|
||||
* @param string|null $type
|
||||
* (optional) The plugin ID or other machine-readable type for the items to
|
||||
* remove from the search index. If omitted, $sid and $langcode are ignored
|
||||
* and the entire search index is cleared.
|
||||
* @param int|array|null $sid
|
||||
* (optional) The ID or array of IDs of the items to remove from the search
|
||||
* index. If omitted, all items matching $type are cleared, and $langcode
|
||||
* is ignored.
|
||||
* @param string|null $langcode
|
||||
* (optional) Language code of the item to remove from the search index. If
|
||||
* omitted, all items matching $sid and $type are cleared.
|
||||
*
|
||||
* @throws \Drupal\search\Exception\SearchIndexException
|
||||
* If there is an error clearing the index.
|
||||
*/
|
||||
public function clear($type = NULL, $sid = NULL, $langcode = NULL);
|
||||
|
||||
/**
|
||||
* Changes the timestamp on indexed items to 'now' to force reindexing.
|
||||
*
|
||||
* This function is meant for use by search page plugins, or for building a
|
||||
* user interface that lets users mark all or parts of the search index for
|
||||
* reindexing.
|
||||
*
|
||||
* @param string $type
|
||||
* (optional) The plugin ID or other machine-readable type of this item. If
|
||||
* omitted, the entire search index is marked for reindexing, and $sid and
|
||||
* $langcode are ignored.
|
||||
* @param int $sid
|
||||
* (optional) An ID number identifying this particular item (e.g., node ID).
|
||||
* If omitted, everything matching $type is marked, and $langcode is
|
||||
* ignored.
|
||||
* @param string $langcode
|
||||
* (optional) The language code to mark. If omitted, everything matching
|
||||
* $type and $sid is marked.
|
||||
*
|
||||
* @throws \Drupal\search\Exception\SearchIndexException
|
||||
* If there is an error marking the index for re-indexing.
|
||||
*/
|
||||
public function markForReindex($type = NULL, $sid = NULL, $langcode = NULL);
|
||||
|
||||
/**
|
||||
* Updates the {search_total} database table.
|
||||
*
|
||||
* @param array $words
|
||||
* An array whose keys are words from self::index() whose total weights
|
||||
* need to be updated.
|
||||
*
|
||||
* @throws \Drupal\search\Exception\SearchIndexException
|
||||
* If there is an error updating the totals.
|
||||
*/
|
||||
public function updateWordWeights(array $words);
|
||||
|
||||
}
|
|
@ -43,6 +43,13 @@ class SearchPageListBuilder extends DraggableListBuilder implements FormInterfac
|
|||
*/
|
||||
protected $searchManager;
|
||||
|
||||
/**
|
||||
* The search index.
|
||||
*
|
||||
* @var \Drupal\search\SearchIndexInterface
|
||||
*/
|
||||
protected $searchIndex;
|
||||
|
||||
/**
|
||||
* The messenger.
|
||||
*
|
||||
|
@ -63,12 +70,19 @@ class SearchPageListBuilder extends DraggableListBuilder implements FormInterfac
|
|||
* The factory for configuration objects.
|
||||
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
|
||||
* The messenger.
|
||||
* @param \Drupal\search\SearchIndexInterface $search_index
|
||||
* The search index.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, SearchPluginManager $search_manager, ConfigFactoryInterface $config_factory, MessengerInterface $messenger) {
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, SearchPluginManager $search_manager, ConfigFactoryInterface $config_factory, MessengerInterface $messenger, SearchIndexInterface $search_index = NULL) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
$this->configFactory = $config_factory;
|
||||
$this->searchManager = $search_manager;
|
||||
$this->messenger = $messenger;
|
||||
if (!$search_index) {
|
||||
@trigger_error('Calling SearchPageListBuilder::__construct() without the $search_index argument is deprecated in drupal:8.8.0 and is required in drupal:9.0.0. See https://www.drupal.org/node/3075696', E_USER_DEPRECATED);
|
||||
$search_index = \Drupal::service('search.index');
|
||||
}
|
||||
$this->searchIndex = $search_index;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,7 +94,8 @@ class SearchPageListBuilder extends DraggableListBuilder implements FormInterfac
|
|||
$container->get('entity_type.manager')->getStorage($entity_type->id()),
|
||||
$container->get('plugin.manager.search'),
|
||||
$container->get('config.factory'),
|
||||
$container->get('messenger')
|
||||
$container->get('messenger'),
|
||||
$container->get('search.index')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -344,9 +359,9 @@ class SearchPageListBuilder extends DraggableListBuilder implements FormInterfac
|
|||
$search_settings->set('index.minimum_word_size', $form_state->getValue('minimum_word_size'));
|
||||
$search_settings->set('index.overlap_cjk', $form_state->getValue('overlap_cjk'));
|
||||
// Specifically mark items in the default index for reindexing, since
|
||||
// these settings are used in the search_index() function.
|
||||
// these settings are used in the SearchIndex::index() function.
|
||||
$this->messenger->addStatus($this->t('The default search index will be rebuilt.'));
|
||||
search_mark_for_reindex();
|
||||
$this->searchIndex->markForReindex();
|
||||
}
|
||||
|
||||
$search_settings
|
||||
|
|
|
@ -38,11 +38,6 @@ class SearchAdvancedSearchFormTest extends BrowserTestBase {
|
|||
|
||||
// First update the index. This does the initial processing.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
|
||||
// Then, run the shutdown function. Testing is a unique case where indexing
|
||||
// and searching has to happen in the same request, so running the shutdown
|
||||
// function manually is needed to finish the indexing process.
|
||||
search_update_totals();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -70,11 +70,6 @@ class SearchCommentCountToggleTest extends BrowserTestBase {
|
|||
|
||||
// First update the index. This does the initial processing.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
|
||||
// Then, run the shutdown function. Testing is a unique case where indexing
|
||||
// and searching has to happen in the same request, so running the shutdown
|
||||
// function manually is needed to finish the indexing process.
|
||||
search_update_totals();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -294,7 +294,7 @@ class SearchCommentTest extends BrowserTestBase {
|
|||
*/
|
||||
public function assertCommentAccess($assume_access, $message) {
|
||||
// Invoke search index update.
|
||||
search_mark_for_reindex('node_search', $this->node->id());
|
||||
\Drupal::service('search.index')->markForReindex('node_search', $this->node->id());
|
||||
$this->cronRun();
|
||||
|
||||
// Search for the comment subject.
|
||||
|
|
|
@ -53,7 +53,6 @@ class SearchConfigSettingsFormTest extends BrowserTestBase {
|
|||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Enable the search block.
|
||||
$this->drupalPlaceBlock('search_form_block');
|
||||
|
|
|
@ -56,7 +56,6 @@ class SearchDateIntervalTest extends BrowserTestBase {
|
|||
// Update the index.
|
||||
$plugin = $this->container->get('plugin.manager.search')->createInstance('node_search');
|
||||
$plugin->updateIndex();
|
||||
search_update_totals();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,7 +42,6 @@ class SearchEmbedFormTest extends BrowserTestBase {
|
|||
$this->node = $this->drupalCreateNode();
|
||||
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Set up a dummy initial count of times the form has been submitted.
|
||||
$this->submitCount = \Drupal::state()->get('search_embedded_form.submit_count');
|
||||
|
|
|
@ -46,7 +46,6 @@ class SearchExactTest extends BrowserTestBase {
|
|||
|
||||
// Update the search index.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Refresh variables after the treatment.
|
||||
$this->refreshVariables();
|
||||
|
|
|
@ -85,7 +85,6 @@ class SearchLanguageTest extends BrowserTestBase {
|
|||
// Update the index and then run the shutdown method.
|
||||
$plugin = $this->container->get('plugin.manager.search')->createInstance('node_search');
|
||||
$plugin->updateIndex();
|
||||
search_update_totals();
|
||||
}
|
||||
|
||||
public function testLanguages() {
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Drupal\Tests\search\Functional;
|
|||
use Drupal\Core\Database\Database;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\search\SearchIndexInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
|
@ -128,7 +129,8 @@ class SearchMultilingualEntityTest extends BrowserTestBase {
|
|||
// Run the shutdown function. Testing is a unique case where indexing
|
||||
// and searching has to happen in the same request, so running the shutdown
|
||||
// function manually is needed to finish the indexing process.
|
||||
search_update_totals();
|
||||
$search_index = \Drupal::service('search.index');
|
||||
assert($search_index instanceof SearchIndexInterface);
|
||||
$this->assertIndexCounts(6, 8, 'after updating partially');
|
||||
$this->assertDatabaseCounts(2, 0, 'after updating partially');
|
||||
|
||||
|
@ -140,7 +142,6 @@ class SearchMultilingualEntityTest extends BrowserTestBase {
|
|||
$this->plugin = $this->container->get('plugin.manager.search')->createInstance('node_search');
|
||||
|
||||
$this->plugin->updateIndex();
|
||||
search_update_totals();
|
||||
$this->assertIndexCounts(0, 8, 'after updating fully');
|
||||
$this->assertDatabaseCounts(8, 0, 'after updating fully');
|
||||
|
||||
|
@ -150,7 +151,6 @@ class SearchMultilingualEntityTest extends BrowserTestBase {
|
|||
$this->assertIndexCounts(8, 8, 'after reindex');
|
||||
$this->assertDatabaseCounts(8, 0, 'after reindex');
|
||||
$this->plugin->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Test search results.
|
||||
|
||||
|
@ -190,13 +190,12 @@ class SearchMultilingualEntityTest extends BrowserTestBase {
|
|||
|
||||
// Mark one of the nodes for reindexing, using the API function, and
|
||||
// verify indexing status.
|
||||
search_mark_for_reindex('node_search', $this->searchableNodes[0]->id());
|
||||
$search_index->markForReindex('node_search', $this->searchableNodes[0]->id());
|
||||
$this->assertIndexCounts(1, 8, 'after marking one node to reindex via API function');
|
||||
|
||||
// Update the index and verify the totals again.
|
||||
$this->plugin = $this->container->get('plugin.manager.search')->createInstance('node_search');
|
||||
$this->plugin->updateIndex();
|
||||
search_update_totals();
|
||||
$this->assertIndexCounts(0, 8, 'after indexing again');
|
||||
|
||||
// Mark one node for reindexing by saving it, and verify indexing status.
|
||||
|
@ -227,32 +226,32 @@ class SearchMultilingualEntityTest extends BrowserTestBase {
|
|||
// Add a bogus entry to the search index table using a different search
|
||||
// type. This will not appear in the index status, because it is not
|
||||
// managed by a plugin.
|
||||
search_index('foo', $this->searchableNodes[0]->id(), 'en', 'some text');
|
||||
$search_index->index('foo', $this->searchableNodes[0]->id(), 'en', 'some text');
|
||||
$this->assertIndexCounts(1, 8, 'after adding a different index item');
|
||||
|
||||
// Mark just this "foo" index for reindexing.
|
||||
search_mark_for_reindex('foo');
|
||||
$search_index->markForReindex('foo');
|
||||
$this->assertIndexCounts(1, 8, 'after reindexing the other search type');
|
||||
|
||||
// Mark everything for reindexing.
|
||||
search_mark_for_reindex();
|
||||
$search_index->markForReindex();
|
||||
$this->assertIndexCounts(8, 8, 'after reindexing everything');
|
||||
|
||||
// Clear one item from the index, but with wrong language.
|
||||
$this->assertDatabaseCounts(8, 1, 'before clear');
|
||||
search_index_clear('node_search', $this->searchableNodes[0]->id(), 'hu');
|
||||
$search_index->clear('node_search', $this->searchableNodes[0]->id(), 'hu');
|
||||
$this->assertDatabaseCounts(8, 1, 'after clear with wrong language');
|
||||
// Clear using correct language.
|
||||
search_index_clear('node_search', $this->searchableNodes[0]->id(), 'en');
|
||||
$search_index->clear('node_search', $this->searchableNodes[0]->id(), 'en');
|
||||
$this->assertDatabaseCounts(7, 1, 'after clear with right language');
|
||||
// Don't specify language.
|
||||
search_index_clear('node_search', $this->searchableNodes[1]->id());
|
||||
$search_index->clear('node_search', $this->searchableNodes[1]->id());
|
||||
$this->assertDatabaseCounts(6, 1, 'unspecified language clear');
|
||||
// Clear everything in 'foo'.
|
||||
search_index_clear('foo');
|
||||
$search_index->clear('foo');
|
||||
$this->assertDatabaseCounts(6, 0, 'other index clear');
|
||||
// Clear everything.
|
||||
search_index_clear();
|
||||
$search_index->clear();
|
||||
$this->assertDatabaseCounts(0, 0, 'complete clear');
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ class SearchNodeDiacriticsTest extends BrowserTestBase {
|
|||
|
||||
// Update the search index.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Refresh variables after the treatment.
|
||||
$this->refreshVariables();
|
||||
|
|
|
@ -43,7 +43,6 @@ class SearchNodePunctuationTest extends BrowserTestBase {
|
|||
|
||||
// Update the search index.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Refresh variables after the treatment.
|
||||
$this->refreshVariables();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\Tests\search\Functional;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\search\SearchIndexInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
|
@ -48,7 +49,8 @@ class SearchNodeUpdateAndDeletionTest extends BrowserTestBase {
|
|||
$node_search_plugin = $this->container->get('plugin.manager.search')->createInstance('node_search');
|
||||
// Update the search index.
|
||||
$node_search_plugin->updateIndex();
|
||||
search_update_totals();
|
||||
$search_index = \Drupal::service('search.index');
|
||||
assert($search_index instanceof SearchIndexInterface);
|
||||
|
||||
// Search the node to verify it appears in search results
|
||||
$edit = ['keys' => 'knights'];
|
||||
|
@ -61,7 +63,6 @@ class SearchNodeUpdateAndDeletionTest extends BrowserTestBase {
|
|||
|
||||
// Run indexer again
|
||||
$node_search_plugin->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Search again to verify the new text appears in test results.
|
||||
$edit = ['keys' => 'shrubbery'];
|
||||
|
@ -83,7 +84,6 @@ class SearchNodeUpdateAndDeletionTest extends BrowserTestBase {
|
|||
$node_search_plugin = $this->container->get('plugin.manager.search')->createInstance('node_search');
|
||||
// Update the search index.
|
||||
$node_search_plugin->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Search the node to verify it appears in search results
|
||||
$edit = ['keys' => 'dragons'];
|
||||
|
|
|
@ -55,7 +55,6 @@ class SearchPageCacheTagsTest extends BrowserTestBase {
|
|||
$this->node->setOwner($this->searchingUser);
|
||||
$this->node->save();
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,7 +173,6 @@ class SearchPageCacheTagsTest extends BrowserTestBase {
|
|||
|
||||
// Refresh the search index.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Log in with searching user again.
|
||||
$this->drupalLogin($this->searchingUser);
|
||||
|
|
|
@ -47,11 +47,6 @@ class SearchPreprocessLangcodeTest extends BrowserTestBase {
|
|||
// First update the index. This does the initial processing.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
|
||||
// Then, run the shutdown function. Testing is a unique case where indexing
|
||||
// and searching has to happen in the same request, so running the shutdown
|
||||
// function manually is needed to finish the indexing process.
|
||||
search_update_totals();
|
||||
|
||||
// Search for the additional text that is added by the preprocess
|
||||
// function. If you search for text that is in the node, preprocess is
|
||||
// not invoked on the node during the search excerpt generation.
|
||||
|
@ -76,11 +71,6 @@ class SearchPreprocessLangcodeTest extends BrowserTestBase {
|
|||
// First update the index. This does the initial processing.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
|
||||
// Then, run the shutdown function. Testing is a unique case where indexing
|
||||
// and searching has to happen in the same request, so running the shutdown
|
||||
// function manually is needed to finish the indexing process.
|
||||
search_update_totals();
|
||||
|
||||
// Search for the title of the node with a POST query.
|
||||
$edit = ['or' => 'testing'];
|
||||
$this->drupalPostForm('search/node', $edit, 'edit-submit--2');
|
||||
|
|
|
@ -41,7 +41,6 @@ class SearchQueryAlterTest extends BrowserTestBase {
|
|||
|
||||
// Update the search index.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Search for the body keyword 'pizza'.
|
||||
$this->drupalPostForm('search/node', ['keys' => 'pizza'], t('Search'));
|
||||
|
|
|
@ -9,6 +9,7 @@ use Drupal\Core\Link;
|
|||
use Drupal\Core\Url;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\search\Entity\SearchPage;
|
||||
use Drupal\search\SearchIndexInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\Traits\Core\CronRunTrait;
|
||||
|
||||
|
@ -239,7 +240,8 @@ class SearchRankingTest extends BrowserTestBase {
|
|||
|
||||
// Update the search index.
|
||||
$this->nodeSearch->getPlugin()->updateIndex();
|
||||
search_update_totals();
|
||||
$search_index = \Drupal::service('search.index');
|
||||
assert($search_index instanceof SearchIndexInterface);
|
||||
|
||||
$this->nodeSearch->getPlugin()->setSearch('rocks', [], []);
|
||||
// Do the search and assert the results.
|
||||
|
@ -264,7 +266,6 @@ class SearchRankingTest extends BrowserTestBase {
|
|||
|
||||
// Update the search index.
|
||||
$this->nodeSearch->getPlugin()->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
$this->nodeSearch->getPlugin()->setSearch('rocks', [], []);
|
||||
// Do the search and assert the results.
|
||||
|
|
|
@ -34,7 +34,6 @@ class SearchSetLocaleTest extends BrowserTestBase {
|
|||
$this->drupalCreateNode(['body' => [['value' => 'tapir']]]);
|
||||
// Update the search index.
|
||||
$this->nodeSearchPlugin->updateIndex();
|
||||
search_update_totals();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\search\Kernel;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests deprecated search methods.
|
||||
*
|
||||
* @group legacy
|
||||
* @group search
|
||||
*/
|
||||
class SearchDeprecationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['search'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('search', [
|
||||
'search_index',
|
||||
'search_dataset',
|
||||
'search_total',
|
||||
]);
|
||||
$this->installConfig(['search']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedDeprecation search_index() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\search\SearchIndex::index() instead. See https://www.drupal.org/node/3075696
|
||||
*/
|
||||
public function testIndex() {
|
||||
$this->assertNull(search_index('_test_', 1, LanguageInterface::LANGCODE_NOT_SPECIFIED, "foo"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedDeprecation search_index_clear() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\search\SearchIndex::clear() instead. See https://www.drupal.org/node/3075696
|
||||
*/
|
||||
public function testClear() {
|
||||
$this->assertNull(search_index_clear());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedDeprecation search_dirty() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use custom implementation of \Drupal\search\SearchIndexInterface instead. See https://www.drupal.org/node/3075696
|
||||
*/
|
||||
public function testDirty() {
|
||||
$this->assertNull(search_dirty("foo"));
|
||||
$this->assertEqual([], search_dirty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedDeprecation search_update_totals() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use custom implementation of \Drupal\search\SearchIndexInterface instead. See https://www.drupal.org/node/3075696
|
||||
*/
|
||||
public function testUpdateTotals() {
|
||||
$this->assertNull(search_update_totals());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedDeprecation search_mark_for_reindex() is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\search\SearchIndex::markForReindex() instead. See https://www.drupal.org/node/3075696
|
||||
*/
|
||||
public function testMarkForReindex() {
|
||||
$this->assertNull(search_mark_for_reindex('_test_', 1, LanguageInterface::LANGCODE_NOT_SPECIFIED));
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace Drupal\Tests\search\Kernel;
|
|||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\search\SearchIndexInterface;
|
||||
|
||||
/**
|
||||
* Indexes content and queries it.
|
||||
|
@ -49,11 +50,13 @@ class SearchMatchTest extends KernelTestBase {
|
|||
public function _setup() {
|
||||
$this->config('search.settings')->set('index.minimum_word_size', 3)->save();
|
||||
|
||||
$search_index = \Drupal::service('search.index');
|
||||
assert($search_index instanceof SearchIndexInterface);
|
||||
for ($i = 1; $i <= 7; ++$i) {
|
||||
search_index(static::SEARCH_TYPE, $i, LanguageInterface::LANGCODE_NOT_SPECIFIED, $this->getText($i));
|
||||
$search_index->index(static::SEARCH_TYPE, $i, LanguageInterface::LANGCODE_NOT_SPECIFIED, $this->getText($i));
|
||||
}
|
||||
for ($i = 1; $i <= 5; ++$i) {
|
||||
search_index(static::SEARCH_TYPE_2, $i + 7, LanguageInterface::LANGCODE_NOT_SPECIFIED, $this->getText2($i));
|
||||
$search_index->index(static::SEARCH_TYPE_2, $i + 7, LanguageInterface::LANGCODE_NOT_SPECIFIED, $this->getText2($i));
|
||||
}
|
||||
// No getText builder function for Japanese text; just a simple array.
|
||||
foreach ([
|
||||
|
@ -61,9 +64,8 @@ class SearchMatchTest extends KernelTestBase {
|
|||
14 => 'ドルーパルが大好きよ!',
|
||||
15 => 'コーヒーとケーキ',
|
||||
] as $i => $jpn) {
|
||||
search_index(static::SEARCH_TYPE_JPN, $i, LanguageInterface::LANGCODE_NOT_SPECIFIED, $jpn);
|
||||
$search_index->index(static::SEARCH_TYPE_JPN, $i, LanguageInterface::LANGCODE_NOT_SPECIFIED, $jpn);
|
||||
}
|
||||
search_update_totals();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue