diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 0a7b261..0b1c1af 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -1356,7 +1356,7 @@ function comment_node_predelete(Node $node) {
/**
* Implements hook_node_update_index().
*/
-function comment_node_update_index(Node $node) {
+function comment_node_update_index(Node $node, $langcode) {
$index_comments = &drupal_static(__FUNCTION__);
if ($index_comments === NULL) {
@@ -1384,7 +1384,7 @@ function comment_node_update_index(Node $node) {
if ($node->comment && $cids = comment_get_thread($node, $mode, $comments_per_page)) {
$comments = comment_load_multiple($cids);
comment_prepare_thread($comments);
- $build = comment_view_multiple($comments, $node);
+ $build = comment_view_multiple($comments, $node, $langcode);
return drupal_render($build);
}
}
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index f4fb5f5..4ac7f0e 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -658,6 +658,8 @@ function hook_node_prepare(Drupal\node\Node $node) {
*
* @param Drupal\node\Node $node
* The node being displayed in a search result.
+ * @param $langcode
+ * Language code of result being displayed.
*
* @return array
* Extra information to be displayed with search result. This information
@@ -670,7 +672,7 @@ function hook_node_prepare(Drupal\node\Node $node) {
*
* @ingroup node_api_hooks
*/
-function hook_node_search_result(Drupal\node\Node $node) {
+function hook_node_search_result(Drupal\node\Node $node, $langcode) {
$comments = db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array('nid' => $node->nid))->fetchField();
return array('comment' => format_plural($comments, '1 comment', '@count comments'));
}
@@ -722,13 +724,15 @@ function hook_node_update(Drupal\node\Node $node) {
*
* @param Drupal\node\Node $node
* The node being indexed.
+ * @param $langcode
+ * Language code of the variant of the node being indexed.
*
* @return string
* Additional node information to be indexed.
*
* @ingroup node_api_hooks
*/
-function hook_node_update_index(Drupal\node\Node $node) {
+function hook_node_update_index(Drupal\node\Node $node, $langcode) {
$text = '';
$comments = db_query('SELECT subject, comment, format FROM {comment} WHERE nid = :nid AND status = :status', array(':nid' => $node->nid, ':status' => COMMENT_PUBLISHED));
foreach ($comments as $comment) {
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index d4bd4a4..ddc391f 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1619,28 +1619,32 @@ function node_search_execute($keys = NULL, $conditions = NULL) {
// Load results.
$find = $query
+ // Add the language code of the indexed item to the result of the query, since the node
+ // will be rendered using the respective language.
+ ->fields('i', array('langcode'))
->limit(10)
->execute();
$results = array();
foreach ($find as $item) {
// Render the node.
$node = node_load($item->sid);
- $build = node_view($node, 'search_result');
+ $build = node_view($node, 'search_result', $item->langcode);
unset($build['#theme']);
$node->rendered = drupal_render($build);
// Fetch comments for snippet.
- $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
+ $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node, $item->langcode);
- $extra = module_invoke_all('node_search_result', $node);
+ $extra = module_invoke_all('node_search_result', $node, $item->langcode);
+ $language = language_load($item->langcode);
$uri = entity_uri('node', $node);
$results[] = array(
- 'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE))),
+ 'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE, 'language' => $language))),
'type' => check_plain(node_type_get_name($node)),
- 'title' => $node->label(),
+ 'title' => $node->label($item->langcode),
'user' => theme('username', array('account' => $node)),
- 'date' => $node->changed,
+ 'date' => $node->get('changed', $item->langcode),
'node' => $node,
'extra' => $extra,
'score' => $item->calculated_score,
@@ -2653,7 +2657,14 @@ function node_update_index() {
return;
}
+ // The indexing throttle should be aware of the number of language variants of a node.
+ $counter = 0;
foreach (node_load_multiple($nids) as $node) {
+ // Determine when the maximum number of indexable items is reached.
+ $counter += 1 + count($node->translations());
+ if ($counter > $limit) {
+ break;
+ }
_node_index_node($node);
}
}
@@ -2670,21 +2681,26 @@ function _node_index_node(Node $node) {
// results half-life calculation.
variable_set('node_cron_last', $node->changed);
- // Render the node.
- $build = node_view($node, 'search_index');
- unset($build['#theme']);
- $node->rendered = drupal_render($build);
+ $languages = array_merge(array(language_load($node->langcode)), $node->translations());
- $text = '
' . check_plain($node->label()) . '
' . $node->rendered;
+ foreach ($languages as $language) {
+ // Render the node.
+ $build = node_view($node, 'search_index', $language->langcode);
- // Fetch extra data normally not visible
- $extra = module_invoke_all('node_update_index', $node);
- foreach ($extra as $t) {
- $text .= $t;
- }
+ unset($build['#theme']);
+ $node->rendered = drupal_render($build);
+
+ $text = '' . check_plain($node->label($language->langcode)) . '
' . $node->rendered;
- // Update index
- search_index($node->nid, 'node', $text);
+ // Fetch extra data normally not visible.
+ $extra = module_invoke_all('node_update_index', $node, $language->langcode);
+ foreach ($extra as $t) {
+ $text .= $t;
+ }
+
+ // Update index.
+ search_index($node->nid, 'node', $text, $language->langcode);
+ }
}
/**
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchMatchTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchMatchTest.php
index 1253cb3..118cbf7 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchMatchTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchMatchTest.php
@@ -37,10 +37,10 @@ class SearchMatchTest extends SearchTestBase {
config('search.settings')->set('index.minimum_word_size', 3)->save();
for ($i = 1; $i <= 7; ++$i) {
- search_index($i, SEARCH_TYPE, $this->getText($i));
+ search_index($i, SEARCH_TYPE, $this->getText($i), LANGUAGE_NOT_SPECIFIED);
}
for ($i = 1; $i <= 5; ++$i) {
- search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i));
+ search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i), LANGUAGE_NOT_SPECIFIED);
}
// No getText builder function for Japanese text; just a simple array.
foreach (array(
@@ -48,7 +48,7 @@ class SearchMatchTest extends SearchTestBase {
14 => 'ドルーパルが大好きよ!',
15 => 'コーヒーとケーキ',
) as $i => $jpn) {
- search_index($i, SEARCH_TYPE_JPN, $jpn);
+ search_index($i, SEARCH_TYPE_JPN, $jpn, LANGUAGE_NOT_SPECIFIED);
}
search_update_totals();
}
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
new file mode 100644
index 0000000..5c5640b
--- /dev/null
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
@@ -0,0 +1,105 @@
+ 'Multilingual entities',
+ 'description' => 'Tests entities with multilingual fields.',
+ 'group' => 'Search',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('language', 'locale');
+
+ // Add two new languages.
+ $language = (object) array(
+ 'langcode' => 'hu',
+ 'name' => 'Hungarian',
+ );
+ language_save($language);
+ $language = (object) array(
+ 'langcode' => 'sv',
+ 'name' => 'Swedish',
+ );
+ language_save($language);
+
+ // Make the body field translatable.
+ // (The parent class has already created the article and page content types.)
+ $field = field_info_field('body');
+ $field['translatable'] = TRUE;
+ field_update_field($field);
+
+ // Create a few page nodes with multilingual body values.
+ $default_format = filter_default_format();
+ $nodes = array(
+ array(
+ 'type' => 'page',
+ 'body' => array(
+ 'en' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+ ),
+ 'langcode' => 'en',
+ ),
+ array(
+ 'type' => 'page',
+ 'body' => array(
+ 'en' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+ 'hu' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+ ),
+ 'langcode' => 'en',
+ ),
+ array(
+ 'type' => 'page',
+ 'body' => array(
+ 'en' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+ 'hu' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+ 'sv' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+ ),
+ 'langcode' => 'en',
+ ),
+ );
+ $this->searchable_nodes = array();
+ foreach ($nodes as $node) {
+ $this->searchable_nodes[] = $this->drupalCreateNode($node);
+ }
+ }
+
+ function testIndexingThrottle() {
+ // Index only 4 items per cron run.
+ config('search.settings')->set('index.cron_limit', 4)->save();
+ // Update the index and then run the shutdown method.
+ node_update_index();
+ search_update_totals();
+ $status = module_invoke('node', 'search_status');
+ $this->assertEqual($status['remaining'], 1, t('Remaining items after updating the search index is 1.'));
+ }
+
+ function testSearchingMultilingualFieldValues() {
+ // Update the index and then run the shutdown method.
+ node_update_index();
+ search_update_totals();
+ foreach ($this->searchable_nodes as $node) {
+ // Each searchable node that we created contains values in the body field in one or more
+ // languages. Let's pick the last language variant from the body array and execute a search
+ // using that as a search keyword.
+ $body_language_variant = end($node->body);
+ $search_result = node_search_execute($body_language_variant[0]['value']);
+ // See whether we get the same node as a result.
+ $this->assertEqual($search_result[0]['node']->nid, $node->nid, t('The search has resulted the correct node.'));
+ }
+ }
+}
+
diff --git a/core/modules/search/search.api.php b/core/modules/search/search.api.php
index 01b3d21..40ac0d5 100644
--- a/core/modules/search/search.api.php
+++ b/core/modules/search/search.api.php
@@ -206,7 +206,7 @@ function hook_search_execute($keys = NULL, $conditions = NULL) {
// Insert special keywords.
$query->setOption('type', 'n.type');
- $query->setOption('language', 'n.language');
+ $query->setOption('langcode', 'n.langcode');
if ($query->setOption('term', 'ti.tid')) {
$query->join('taxonomy_index', 'ti', 'n.nid = ti.nid');
}
@@ -224,28 +224,30 @@ function hook_search_execute($keys = NULL, $conditions = NULL) {
->execute();
$results = array();
foreach ($find as $item) {
- // Build the node body.
+ // Render the node.
$node = node_load($item->sid);
- node_build_content($node, 'search_result');
- $node->body = drupal_render($node->content);
+ $build = node_view($node, 'search_result', $item->langcode);
+ unset($build['#theme']);
+ $node->rendered = drupal_render($build);
// Fetch comments for snippet.
- $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
- // Fetch terms for snippet.
- $node->rendered .= ' ' . module_invoke('taxonomy', 'node_update_index', $node);
+ $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node, $item->langcode);
- $extra = module_invoke_all('node_search_result', $node);
+ $extra = module_invoke_all('node_search_result', $node, $item->langcode);
+ $language = language_load($item->langcode);
+ $uri = entity_uri('node', $node);
$results[] = array(
- 'link' => url('node/' . $item->sid, array('absolute' => TRUE)),
+ 'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE, 'language' => $language))),
'type' => check_plain(node_type_get_name($node)),
- 'title' => $node->label(),
+ 'title' => $node->label($item->langcode),
'user' => theme('username', array('account' => $node)),
- 'date' => $node->changed,
+ 'date' => $node->get('changed', $item->langcode),
'node' => $node,
'extra' => $extra,
'score' => $item->calculated_score,
- 'snippet' => search_excerpt($keys, $node->body),
+ 'snippet' => search_excerpt($keys, $node->rendered),
+ 'langcode' => $node->langcode,
);
}
return $results;
diff --git a/core/modules/search/search.install b/core/modules/search/search.install
index a561509..cc17523 100644
--- a/core/modules/search/search.install
+++ b/core/modules/search/search.install
@@ -19,6 +19,12 @@ function search_schema() {
'default' => 0,
'description' => 'Search item ID, e.g. node ID for nodes.',
),
+ 'langcode' => array(
+ 'type' => 'varchar',
+ 'length' => '12',
+ 'not null' => TRUE,
+ 'description' => 'The {languages}.langcode of the item variant.',
+ ),
'type' => array(
'type' => 'varchar',
'length' => 16,
@@ -39,7 +45,7 @@ function search_schema() {
'description' => 'Set to force node reindexing.',
),
),
- 'primary key' => array('sid', 'type'),
+ 'primary key' => array('sid', 'langcode', 'type'),
);
$schema['search_index'] = array(
@@ -59,6 +65,12 @@ function search_schema() {
'default' => 0,
'description' => 'The {search_dataset}.sid of the searchable item to which the word belongs.',
),
+ 'langcode' => array(
+ 'type' => 'varchar',
+ 'length' => '12',
+ 'not null' => TRUE,
+ 'description' => 'The {languages}.langcode of the item variant.',
+ ),
'type' => array(
'type' => 'varchar',
'length' => 16,
@@ -72,7 +84,7 @@ function search_schema() {
),
),
'indexes' => array(
- 'sid_type' => array('sid', 'type'),
+ 'sid_type' => array('sid', 'langcode', 'type'),
),
'foreign keys' => array(
'search_dataset' => array(
@@ -83,7 +95,7 @@ function search_schema() {
),
),
),
- 'primary key' => array('word', 'sid', 'type'),
+ 'primary key' => array('word', 'sid', 'langcode', 'type'),
);
$schema['search_total'] = array(
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index a77ccfd..1de3c46 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -315,20 +315,33 @@ function _search_menu_access($name) {
* @param $module
* (optional) The machine-readable name of the module for the item to remove
* from the search index.
+ * @param $reindex
+ * (optional) Boolean to specify whether reindexing happens.
+ * @param $langcode
+ * (optional) Language code for the operation. If not provided, all
+ * index records for the $sid will be deleted.
*/
-function search_reindex($sid = NULL, $module = NULL, $reindex = FALSE) {
+function search_reindex($sid = NULL, $module = NULL, $reindex = FALSE, $langcode = NULL) {
if ($module == NULL && $sid == NULL) {
module_invoke_all('search_reset');
}
else {
- db_delete('search_dataset')
+ $query = db_delete('search_dataset')
->condition('sid', $sid)
- ->condition('type', $module)
- ->execute();
- db_delete('search_index')
+ ->condition('type', $module);
+ if (!empty($langcode)) {
+ $query->condition('langcode', $langcode);
+ }
+ $query->execute();
+
+ $query = db_delete('search_index')
->condition('sid', $sid)
- ->condition('type', $module)
- ->execute();
+ ->condition('type', $module);
+ if (!empty($langcode)) {
+ $query->condition('langcode', $langcode);
+ }
+ $query->execute();
+
// Don't remove links if re-indexing.
if (!$reindex) {
db_delete('search_node_links')
@@ -557,10 +570,12 @@ function search_invoke_preprocess(&$text) {
* that implements hook_search_info()).
* @param $text
* The content of this item. Must be a piece of HTML or plain text.
+ * @param $langcode
+ * Language code for text being indexed.
*
* @ingroup search
*/
-function search_index($sid, $module, $text) {
+function search_index($sid, $module, $text, $langcode) {
$minimum_word_size = config('search.settings')->get('index.minimum_word_size');
// Link matching
@@ -690,12 +705,13 @@ function search_index($sid, $module, $text) {
$tag = !$tag;
}
- search_reindex($sid, $module, TRUE);
+ search_reindex($sid, $module, TRUE, $langcode);
// Insert cleaned up data into dataset
db_insert('search_dataset')
->fields(array(
'sid' => $sid,
+ 'langcode' => $langcode,
'type' => $module,
'data' => $accum,
'reindex' => 0,
@@ -711,6 +727,7 @@ function search_index($sid, $module, $text) {
->key(array(
'word' => $word,
'sid' => $sid,
+ 'langcode' => $langcode,
'type' => $module,
))
->fields(array('score' => $score))