Skip to content

Conversation

@mikadamczyk
Copy link
Contributor

@mikadamczyk mikadamczyk commented Nov 20, 2025

🎫 Issue IBX-10494

Description:

  • Sorting uses the main location when needed and reuses an existing location join if it is already there. This keeps ordering stable even when the content has extra locations.
  • Sort builders now order by the same aliases they select (content id/published/name, section identifiers/names, location_* aliases), cutting ambiguity.
  • The filtering gateway runs the query directly with limit/offset; the old wrapper subquery was dropped as redundant.
  • Location criteria skip extra joins when location is already in the query, and section/content sort builders add clear aliases.
  • Tests now check location sorting with main vs. secondary locations and use getContentInfo()->getMainLocationId() everywhere.

For QA:

Documentation:

): void {
/** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause */
$queryBuilder->addOrderBy('content.modified', $sortClause->direction);
$queryBuilder->addOrderBy('content_modified', $sortClause->direction);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of unexpected if you got rid of the wrapped query. AFAIR this notation was used by the wrapped query, wasn't it? How does the SQL look like after the changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SELECT DISTINCT
      content.id AS content_id,
      content.contentclass_id AS content_type_id,
      content.current_version AS content_current_version,
      content.initial_language_id AS content_initial_language_id,
      content.language_mask AS content_language_mask,
      content.modified AS content_modified,
      content.name AS content_name,
      content.owner_id AS content_owner_id,
      content.published AS content_published,
      content.remote_id AS content_remote_id,
      content.section_id AS content_section_id,
      content.status AS content_status,
      content.is_hidden AS content_is_hidden,
      version.id AS content_version_id,
      version.version AS content_version_no,
      version.creator_id AS content_version_creator_id,
      version.created AS content_version_created,
      version.modified AS content_version_modified,
      version.status AS content_version_status,
      version.language_mask AS content_version_language_mask,
      version.initial_language_id AS content_version_initial_language_id,
      main_location.main_node_id AS content_main_location_id,
      ibexa_sort_location.is_invisible AS ibexa_filter_sort_location_visibility
  FROM ezcontentobject content
  INNER JOIN ezcontentobject_version version
    ON (content.id = version.contentobject_id)
   AND (content.current_version = version.version)
   AND (version.status = 1)
  LEFT JOIN ezcontentobject_tree main_location
    ON (content.id = main_location.contentobject_id)
   AND (main_location.main_node_id = main_location.node_id)
  INNER JOIN ezcontentobject_tree ibexa_sort_location
    ON (content.id = ibexa_sort_location.contentobject_id)
   AND (ibexa_sort_location.node_id = ibexa_sort_location.main_node_id)
  WHERE content.id IN (57)
  ORDER BY ibexa_filter_sort_location_visibility DESC;

and

SELECT DISTINCT
      content.id AS content_id,
      content.contentclass_id AS content_type_id,
      content.current_version AS content_current_version,
      content.initial_language_id AS content_initial_language_id,
      content.language_mask AS content_language_mask,
      content.modified AS content_modified,
      content.name AS content_name,
      content.owner_id AS content_owner_id,
      content.published AS content_published,
      content.remote_id AS content_remote_id,
      content.section_id AS content_section_id,
      content.status AS content_status,
      content.is_hidden AS content_is_hidden,
      version.id AS content_version_id,
      version.version AS content_version_no,
      version.creator_id AS content_version_creator_id,
      version.created AS content_version_created,
      version.modified AS content_version_modified,
      version.status AS content_version_status,
      version.language_mask AS content_version_language_mask,
      version.initial_language_id AS content_version_initial_language_id,
      main_location.main_node_id AS content_main_location_id,
      ibexa_sort_location.depth AS ibexa_filter_sort_location_depth
    ON (content.id = version.contentobject_id)
   AND (content.current_version = version.version)
   AND (version.status = 1)
  LEFT JOIN ezcontentobject_tree main_location
    ON (content.id = main_location.contentobject_id)
   AND (main_location.main_node_id = main_location.node_id)
  INNER JOIN ezcontentobject_tree ibexa_sort_location
    ON (content.id = ibexa_sort_location.contentobject_id)
   AND (ibexa_sort_location.node_id = ibexa_sort_location.main_node_id)
  INNER JOIN ezcontent_language language
    ON language.id & version.language_mask = language.id
  WHERE (content.id IN (63, 61))
    AND ((language.locale IN ('eng-US', 'eng-GB', 'ger-DE')) OR (version.language_mask & 1 = 1))
  ORDER BY ibexa_filter_sort_location_depth ASC;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikadamczyk but that means that any custom sort clause will stop working because we've changed field name format from alias.field to alias_field, doesn't it? Which makes it a BC break.

Copy link
Contributor

@konradoboza konradoboza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good but please evaluate @alongosz remark. I guess we don't have other way to distinguish queries apart from location alias? The current one looks a bit hacky but can go as-is I guess.

@mikadamczyk mikadamczyk changed the title Sort fix IBX-10494: Refactored location filtering logic Dec 3, 2025
@sonarqubecloud
Copy link

sonarqubecloud bot commented Dec 5, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
6.3% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants