Commit 7d29cf6e authored by Thomas Seidl's avatar Thomas Seidl
Browse files

Issue #3029582 by drunken monkey, RichardDavies: Fixed handling of Views...

Issue #3029582 by drunken monkey, RichardDavies: Fixed handling of Views contextual filters in combination with filter groups.
parent 7e661ca9
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
Search API 1.x, dev (xxxx-xx-xx):
---------------------------------
- #3029582 by drunken monkey, RichardDavies: Fixed handling of Views contextual
  filters in combination with filter groups.
- #3247840 by drunken monkey: Added a hidden datasource option to disable the
  direct SQL query "fast path" for initial entity tracking.

+18 −11
Original line number Diff line number Diff line
@@ -514,12 +514,13 @@ class SearchApiQuery extends QueryPluginBase {
      foreach ($this->where as $group_id => $group) {
        if (!empty($group['conditions']) || !empty($group['condition_groups'])) {
          $group += ['type' => 'AND'];
          // For filters without a group, we want to always add them directly to
          // the query.
          $conditions = ($group_id === '') ? $this->query : $this->query->createConditionGroup($group['type']);
          // Filters in the default group (used by arguments) should always be
          // added directly to the query.
          $default_group = $group_id == 0;
          $conditions = $default_group ? $this->query : $this->query->createConditionGroup($group['type']);
          if (!empty($group['conditions'])) {
            foreach ($group['conditions'] as $condition) {
              list($field, $value, $operator) = $condition;
              [$field, $value, $operator] = $condition;
              $conditions->addCondition($field, $value, $operator);
            }
          }
@@ -528,8 +529,8 @@ class SearchApiQuery extends QueryPluginBase {
              $conditions->addConditionGroup($nested_conditions);
            }
          }
          // If no group was given, the filters were already set on the query.
          if ($group_id !== '') {
          // For the default group, the filters were already set on the query.
          if (!$default_group) {
            $base->addConditionGroup($conditions);
          }
        }
@@ -1005,14 +1006,17 @@ class SearchApiQuery extends QueryPluginBase {
   *
   * @param \Drupal\search_api\Query\ConditionGroupInterface $condition_group
   *   A condition group that should be added.
   * @param string|null $group
   * @param int $group
   *   (optional) The Views query filter group to add this filter to.
   *
   * @return $this
   *
   * @see \Drupal\search_api\Query\QueryInterface::addConditionGroup()
   */
  public function addConditionGroup(ConditionGroupInterface $condition_group, $group = NULL) {
  public function addConditionGroup(ConditionGroupInterface $condition_group, $group = 0) {
    if (!is_int($group) && !(is_string($group) && ctype_digit($group))) {
      trigger_error('Passing a non-integer as the second parameter of \Drupal\search_api\Plugin\views\query\SearchApiQuery::addConditionGroup() is deprecated in search_api:8.x-1.24 and is removed from search_api:2.0.0. If passing NULL or an empty string, pass 0 instead (or omit the parameter entirely). See https://www.drupal.org/node/3029582', E_USER_DEPRECATED);
    }
    if (!$this->shouldAbort()) {
      // Ensure all variants of 0 are actually 0. Thus '', 0 and NULL are all
      // the default group.
@@ -1054,14 +1058,17 @@ class SearchApiQuery extends QueryPluginBase {
   *   respectively.
   *   If $value is NULL, $operator also can only be "=" or "<>", meaning the
   *   field must have no or some value, respectively.
   * @param string|null $group
   * @param int $group
   *   (optional) The Views query filter group to add this filter to.
   *
   * @return $this
   *
   * @see \Drupal\search_api\Query\QueryInterface::addCondition()
   */
  public function addCondition($field, $value, $operator = '=', $group = NULL) {
  public function addCondition($field, $value, $operator = '=', $group = 0) {
    if (!is_int($group) && !(is_string($group) && ctype_digit($group))) {
      trigger_error('Passing a non-integer as the fourth parameter of \Drupal\search_api\Plugin\views\query\SearchApiQuery::addCondition() is deprecated in search_api:8.x-1.24 and is removed from search_api:2.0.0. If passing NULL or an empty string, pass 0 instead (or omit the parameter entirely). See https://www.drupal.org/node/3029582', E_USER_DEPRECATED);
    }
    if (!$this->shouldAbort()) {
      // Ensure all variants of 0 are actually 0. Thus '', 0 and NULL are all
      // the default group.
+126 −2
Original line number Diff line number Diff line
@@ -465,11 +465,135 @@ display:
          empty_zero: false
          hide_alter_empty: true
          destination: false
  page_3:
    display_plugin: page
    id: page_3
    display_title: 'Page for regression test #3029582'
    position: 3
    display_options:
      path: search-api-test-3029582
      defaults:
        arguments: false
        filter_groups: false
        filters: false
      filters:
        id:
          plugin_id: search_api_numeric
          id: id
          table: search_api_index_database_search_index
          field: id
          relationship: none
          admin_label: ''
          operator: '='
          group: 1
          exposed: true
          expose:
            operator_id: id_op
            label: ''
            description: ''
            use_operator: true
            operator: id_op
            identifier: id
            required: false
            remember: false
            multiple: false
            remember_roles:
              authenticated: authenticated
              anonymous: '0'
              administrator: '0'
          is_grouped: false
        created:
          plugin_id: search_api_date
          id: created
          table: search_api_index_database_search_index
          field: created
          relationship: none
          admin_label: ''
          operator: '='
          group: 1
          exposed: true
          expose:
            operator_id: created_op
            label: ''
            description: ''
            use_operator: true
            operator: created_op
            identifier: created
            required: false
            remember: false
            multiple: false
            remember_roles:
              authenticated: authenticated
              anonymous: '0'
              administrator: '0'
          is_grouped: false
        type:
          plugin_id: search_api_string
          id: type
          table: search_api_index_database_search_index
          field: type
          relationship: none
          admin_label: ''
          operator: '='
          group: 2
          exposed: true
          expose:
            operator_id: type_op
            label: ''
            description: ''
            use_operator: true
            operator: type_op
            identifier: type
            required: false
            remember: false
            multiple: false
            remember_roles:
              authenticated: authenticated
              anonymous: '0'
              administrator: '0'
          is_grouped: false
        name:
          plugin_id: search_api_text
          id: name
          table: search_api_index_database_search_index
          field: name
          relationship: none
          admin_label: ''
          operator: '='
          group: 2
          exposed: true
          expose:
            operator_id: name_op
            label: ''
            description: ''
            use_operator: true
            operator: name_op
            identifier: name
            required: false
            remember: false
            multiple: false
            remember_roles:
              authenticated: authenticated
              anonymous: '0'
              administrator: '0'
          is_grouped: false
      filter_groups:
        operator: OR
        groups:
          1: AND
          2: AND
      arguments:
        keywords:
          plugin_id: search_api
          id: keywords
          table: search_api_index_database_search_index
          field: keywords
          break_phrase: true
  block_1:
    display_plugin: block
    id: block_1
    display_title: Block
    position: 2
    position: 4
    display_options:
      display_extenders: {  }
      defaults:
@@ -479,7 +603,7 @@ display:
    display_plugin: rest_export
    id: rest_export_1
    display_title: 'REST export'
    position: 1
    position: 5
    display_options:
      display_extenders: {  }
      path: search-api-rest-test
+144 −2
Original line number Diff line number Diff line
@@ -384,6 +384,7 @@ class ViewsTest extends SearchApiBrowserTestBase {
    $this->regressionTest2869121();
    $this->regressionTest3031991();
    $this->regressionTest3136277();
    $this->regressionTest3029582();
  }

  /**
@@ -492,6 +493,147 @@ class ViewsTest extends SearchApiBrowserTestBase {
    $block->delete();
  }

  /**
   * Tests that arguments play well with multiple filter groups combined by OR.
   *
   * @see https://www.drupal.org/node/3029582
   */
  protected function regressionTest3029582() {
    $yesterday = date('Y-m-d', strtotime('-1DAY'));

    // Should result in these filters:
    // [
    //   keywords = 'orange'
    // AND
    //   [
    //     [
    //       id = 5
    //     AND
    //       created > $yesterday
    //     ]
    //   OR
    //     [
    //       type = 'item'
    //     AND
    //       name = 'foo'
    //     ]
    //   ]
    // ]
    // Therefore, results 1, 2 and 5 should be returned.
    $this->checkResults(
      [
        'id[value]' => '5',
        'created[value]' => $yesterday,
        'created_op' => '>',
        'type[value]' => 'item',
        'name[value]' => 'foo',
      ],
      [1, 2, 5],
      'Regression Test #3029582 - Search 1',
      'orange',
      'search-api-test-3029582',
    );

    // Should result in these filters:
    // [
    //   keywords = 'orange'
    // AND
    //   [
    //     [
    //       id = 5
    //     AND
    //       created < $yesterday
    //     ]
    //   OR
    //     [
    //       type = 'item'
    //     AND
    //       name = 'foo'
    //     ]
    //   ]
    // ]
    // Therefore, results 1 and 2 should be returned.
    $this->checkResults(
      [
        'id[value]' => '5',
        'created[value]' => $yesterday,
        'created_op' => '<',
        'type[value]' => 'item',
        'name[value]' => 'foo',
      ],
      [1, 2],
      'Regression Test #3029582 - Search 2',
      'orange',
      'search-api-test-3029582',
    );

    // Should result in these filters:
    // [
    //   keywords = 'strawberry'
    // AND
    //   [
    //     [
    //       id = 5
    //     AND
    //       created < $yesterday
    //     ]
    //   OR
    //     [
    //       type = 'item'
    //     AND
    //       name = 'foo'
    //     ]
    //   ]
    // ]
    // Therefore, no results should be returned.
    $this->checkResults(
      [
        'id[value]' => '5',
        'created[value]' => $yesterday,
        'created_op' => '<',
        'type[value]' => 'item',
        'name[value]' => 'foo',
      ],
      [],
      'Regression Test #3029582 - Search 3',
      'strawberry',
      'search-api-test-3029582',
    );

    // Should result in these filters:
    // [
    //   keywords = 'strawberry'
    // AND
    //   [
    //     [
    //       id = 5
    //     AND
    //       created < $yesterday
    //     ]
    //   OR
    //     [
    //       type = 'article'
    //     AND
    //       name = 'foo'
    //     ]
    //   ]
    // ]
    // Therefore, result 4 should be returned.
    $this->checkResults(
      [
        'id[value]' => '5',
        'created[value]' => $yesterday,
        'created_op' => '<',
        'type[value]' => 'article',
        'name[value]' => 'foo',
      ],
      [4],
      'Regression Test #3029582 - Search 3',
      'strawberry',
      'search-api-test-3029582',
    );
  }

  /**
   * Tests that date range end dates can be displayed.
   *
@@ -610,8 +752,8 @@ class ViewsTest extends SearchApiBrowserTestBase {
   * @param string $arguments
   *   (optional) A string to append to the search path.
   */
  protected function checkResults(array $query, array $expected_results = NULL, $label = 'Search', $arguments = '') {
    $this->drupalGet('search-api-test/' . $arguments, ['query' => $query]);
  protected function checkResults(array $query, array $expected_results = NULL, string $label = 'Search', string $arguments = '', string $path = 'search-api-test'): void {
    $this->drupalGet($path . '/' . $arguments, ['query' => $query]);

    if (isset($expected_results)) {
      $count = count($expected_results);
+1 −1
Original line number Diff line number Diff line
@@ -118,7 +118,7 @@ class AllTermsArgumentTest extends UnitTestCase {
      });
    $query->method('addConditionGroup')
      ->willReturnCallback(function (ConditionGroupInterface $added_condition_group, $group = NULL) {
        Assert::assertNull($group);
        Assert::assertEquals(0, $group);
        Assert::assertSame($this->conditionGroup, $added_condition_group);
      });
    $query->method('abort')