Problem/Motivation

I've been playing around this the JSON:API module in D9 using the Umami Profile to have some content to query against. I noticed that when sorting by title on node queries they are not being returned in the correct alphabetical order (at least as I understand it).

For instance: /jsonapi/node/recipe?sort=title returns the list of recipes in the following order:

  1. Vegan chocolate and nut brownies
  2. Crema catalana
  3. Thai green curry
  4. Deep mediterranean quiche
  5. Fiery chili sauce
  6. Gluten free pizza
  7. Super easy vegetarian pasta bake
  8. Victoria sponge cake
  9. Watercress soup

interestingly, sorting in the reverse order with: /jsonapi/node/recipe?sort=-title seems to return the correct order:
Actually the reverse order is off as well. "Deep mediterranean quiche" should be above "Crema catalana" and "Gluten free pizza" should be above "Fiery chili sauce"

  1. Watercress soup
  2. Victoria sponge cake
  3. Vegan chocolate and nut brownies
  4. Thai green curry
  5. Super easy vegetarian pasta bake
  6. Fiery chili sauce
  7. Deep mediterranean quiche
  8. Gluten free pizza
  9. Crema catalana

the same thing appears to be happening on other content types such as articles: /jsonapi/node/article?sort=title

I'm using Umami as an example but I assume there is nothing particular about the Umami profile that is causing the issue.

Steps to reproduce

  1. Install Drupal 9 with the Umami Profile.
  2. Enable the JSON:API module and any associated modules.
  3. Got to the url: /jsonapi/node/recipe?sort=title

Expected Order:

  1. Crema catalana
  2. Deep mediterranean quiche
  3. Fiery chili sauce
  4. Gluten free pizza
  5. Super easy vegetarian pasta bake
  6. Thai green curry
  7. Vegan chocolate and nut brownies
  8. Victoria sponge cake
  9. Watercress soup

Actual Order:

  1. Vegan chocolate and nut brownies
  2. Crema catalana
  3. Thai green curry
  4. Deep mediterranean quiche
  5. Fiery chili sauce
  6. Gluten free pizza
  7. Super easy vegetarian pasta bake
  8. Victoria sponge cake
  9. Watercress soup

Proposed resolution

So far:
Get the language_code in EntityResource.php's getCollectionQuery method and pass to entity query

Remaining tasks

Review

User interface changes

N/A

API changes

N/A

Data model changes

N/A

CommentFileSizeAuthor
#62 3186834_62.patch12.93 KBxeniksp
#61 3186834-61.patch12.69 KB_utsavsharma
#61 interdiff_60-61.txt1.06 KB_utsavsharma
#60 3186834-60.patch12.72 KB_utsavsharma
#60 interdiff_D11.txt12.72 KB_utsavsharma
#59 interdiff_58-59.txt1.79 KBxeniksp
#59 3186834_59.patch12.87 KBxeniksp
#58 interdiff_57-58.txt1.12 KBxeniksp
#58 3186834_58.patch12.76 KBxeniksp
#57 interdiff_43-57.txt1.3 KBxeniksp
#57 3186834-57.patch12.46 KBxeniksp
#56 interdiff_43-56.txt1.28 KBxeniksp
#56 3186834-56.patch12.39 KBxeniksp
#49 interdiff_43-49.txt4.8 KBVidushi Mehta
#49 3186834-49.patch12.71 KBVidushi Mehta
#43 interdiff-3186834-41-43.txt2.21 KBmohit_aghera
#43 3186834-43.patch12.7 KBmohit_aghera
#41 interdiff-3186834-39-41.txt1.28 KBmohit_aghera
#41 3186834-41.patch12.62 KBmohit_aghera
#39 interdiff-3186834-37-39.txt11.56 KBmohit_aghera
#39 3186834-39.patch12.53 KBmohit_aghera
#37 3186834-37_no-test.patch3.39 KBGRcwolf
#17 interdiff-3186834-15-16.txt2.98 KBmohit_aghera
#17 3186834-16.patch7.87 KBmohit_aghera
#15 interdiff_8-15.txt582 byteskuldeep_mehra27
#15 3186834-15.patch5.96 KBkuldeep_mehra27
#8 interdiff-3186834-7-8.txt3.67 KBmohit_aghera
#8 3186834-8.patch5.97 KBmohit_aghera
#7 3186834-7.patch3.52 KBjibran
#6 3186834-6--test-only.patch2.35 KBmohit_aghera

Issue fork drupal-3186834

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

PCate created an issue. See original summary.

PCate’s picture

Issue summary: View changes
Kristen Pol’s picture

Issue tags: +Bug Smash Initiative

Thanks for reporting this bug and creating such a great issue summary! I was able to reproduce the issue when testing on 9.2 as well.

Kristen Pol’s picture

One thing I should have noted before is that I tried testing with specifying the langcode as en-US and also got a non-alphabetical ordering though I think it was different than in the issue summary. I might try that again.

jibran’s picture

JSON:API uses EFQ under the hood. See \Drupal\jsonapi\Controller\EntityResource::getCollectionQuery. One way to verify, that it's not a JSON:API issue, is to create an equivalent EFQ and check the results. JSON:API sort parameter is handled by \Drupal\jsonapi\Query\Sort and as per \Drupal\Tests\jsonapi\Unit\Query\SortTest::parameterProvider ?sort=title will be converted to following values

[[
  'path' => 'title', 
  'direction' => 'ASC', 
  'langcode' => NULL,
]]

You resultant EFQ will be:

$storage = \Drupal::entityTypeManager()
  ->getStorage('node');
$query = $storage
  ->getQuery()
  ->condition('type', 'article')
  ->sort('title', 'ASC', NULL);
$results = $query->execute();
$entities = $storage->loadMultiple($results);
foreach ($entities as $entity) {
 print $entity->title() . PHP EOL;
}

Can you please share the output of the above script?

mohit_aghera’s picture

@jibran
I am seeing different behavior.
I've attached a test only patch to see how it goes.

For me on local, entity queries are giving the correct results.
Somehow for my test case, when I request the translation URL like /ca/jsonapi/node/article, the sorting process is happening as per the english labels only.
Where as for translation sorting should happen as per translated titles.

Feel free to correct me if I am missing something here.

jibran’s picture

Can you try the following patch?

mohit_aghera’s picture

Status: Active » Needs review
FileSize
5.97 KB
3.67 KB

Merging the patches from #6 and #7

adalbertov’s picture

Hello, have just checked the patch #8, the list looks ordered properly, so I'm moving the issue to RTBC.

adalbertov’s picture

Status: Needs review » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 8: 3186834-8.patch, failed testing. View results

benjifisher’s picture

Please do not ask the testbot to try again until #3207086: [HEAD BROKEN] Consistent failure in MonthDatePluginTest is fixed.

alexpott’s picture

Status: Needs work » Reviewed & tested by the community
catch’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php
    @@ -322,4 +322,64 @@ public function testDeleteMultilingual() {
       }
     
    +  /**
    +   * Test to evaluate the multilingual json api calls.
    +   */
    

    Should be 'Tests multilingual JSON:API calls.' I think.

  2. +++ b/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php
    @@ -322,4 +322,64 @@ public function testDeleteMultilingual() {
    +  public function testMultilingualGet() {
    +    // Delete all the articles so we can start cleanly.
    +    $storage_handler = \Drupal::entityTypeManager()->getStorage('node');
    +    $entities = $storage_handler->loadByProperties(['type' => 'article']);
    +    $storage_handler->delete($entities);
    +
    +    $titles = [
    

    Should the article creation be moved out of ::setUp() into a helper method, so that we can avoid creating then deleting the nodes?

kuldeep_mehra27’s picture

mohit_aghera’s picture

Should the article creation be moved out of ::setUp() into a helper method, so that we can avoid creating then deleting the nodes?

@catch, I tried to refactor it. However, currently we are creating articles like,
$this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE); in setup() method.

Now, if we want to refactor it, we want to add a similar line in all the test cases of JsonApiFunctionalMultilingualTest class, so it will create the required nodes for all the test cases.

I found it a bit repetitive to do. Let me know if I should go ahead and refactor it.

mohit_aghera’s picture

Status: Needs work » Needs review
FileSize
7.87 KB
2.98 KB

@catch Updated the test cases as per discussion on slack and mentioned in #14

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

LuisPimentelLopes’s picture

This issue also occurs on Drupal 8.9.12. Thanks @kuldeep_mehra27 #15 works!

bbrala’s picture

Status: Needs review » Reviewed & tested by the community

The asked changes have been applies in #15 and #17, changed are minor. Setting it back to RTBC

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/jsonapi/src/Controller/EntityResource.php
@@ -176,8 +185,10 @@ class EntityResource {
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
...
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $field_manager, ResourceTypeRepositoryInterface $resource_type_repository, RendererInterface $renderer, EntityRepositoryInterface $entity_repository, IncludeResolver $include_resolver, EntityAccessChecker $entity_access_checker, FieldResolver $field_resolver, SerializerInterface $serializer, TimeInterface $time, AccountInterface $user) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $field_manager, ResourceTypeRepositoryInterface $resource_type_repository, RendererInterface $renderer, EntityRepositoryInterface $entity_repository, IncludeResolver $include_resolver, EntityAccessChecker $entity_access_checker, FieldResolver $field_resolver, SerializerInterface $serializer, TimeInterface $time, AccountInterface $user, LanguageManagerInterface $language_manager) {

@@ -189,6 +200,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Ent
+    $this->languageManager = $language_manager;

We need to make this change in BC compatible way. I.e We need to allow NULLs for $language_manager and trigger a deprecation error if language manager is NULL. See \Drupal\Core\Extension\ModuleInstaller::__construct() on 9.3.x for a couple of examples of how to do this.

mohit_aghera’s picture

Status: Needs work » Needs review

Thanks @alexpott
I've updated the changes and created a new fork for implementation.

paulocs made their first commit to this issue’s fork.

paulocs’s picture

I added test to test the deprecation message.

alexpott’s picture

I wonder if we should be fixing this problem at a lower level. It seems to me that the result of calling ->sort() with a NULL in \Drupal\jsonapi\Controller\EntityResource::getCollectionQuery() is unexpected. Why is providing a langcode so important? Is it ever correct to provide a NULL when the field is not a base field?

bbrala’s picture

I was wondering if there is something to be learned from this issue #2794431: [META] Formalize translations support in regards to this bug.

vikashsoni’s picture

Thanks for the patch now we able to sort title

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

bbrala’s picture

Status: Needs review » Needs work

Left a comment and #26 also doesn't seem addressed. Setting to needs work.

cleverhoods made their first commit to this issue’s fork.

andregp made their first commit to this issue’s fork.

andregp’s picture

As previous MR was unmergeable I created a new MR, where I applied the last patch (#17) and cherry picked the related-to-the-issue commits from the first MR.
Still needs work tough, to address the comment right before #30 by @bbrala

shouldn't this check for the entity type through the ResourceObject and make the language depend on what type of entiity this is?

And the question on #26 by @alexpott.

Edit: I believe I should have pushed the new branch before adding the commits, that way they would show here on the comments making clearer which commits I picked. So here is a copy of my log.

commit 0a900ba44c6effb16df6c0c440d3c1dffc3487d0 (cherry-picked from a281919c)
Author: Paulo Henrique Cota Starling <paulocs@ciandt.com>
Date:   Tue Aug 3 09:10:30 2021 -0300
    Changing to interface language instead of content language

commit c414f9b2b85014b083b53c83fc48c517e2036942 (cherry-picked from 9c55f6ad)
Author: Paulo Henrique Cota Starling <paulocs@ciandt.com>
Date:   Fri Jul 2 15:55:52 2021 -0300
    Adding 1 blank line after function

commit b6a6e57428840d7e784e5ccb54d7014e9c8475ea (cherry-picked from 0e15dffc)
Author: Paulo Henrique Cota Starling <paulocs@ciandt.com>
Date:   Fri Jul 2 13:58:01 2021 -0300
    Fix unknown word 'modulerenderer'

commit ee5a436dc307f43d8f6d17b36e5b4b98c9c098da (cherry-picked from 8f18c59d)
Author: Paulo Henrique Cota Starling <paulocs@ciandt.com>
Date:   Fri Jul 2 12:13:26 2021 -0300
    Adding test to the deprecation message

commit e5dab4de100b3a8f6600b016899268fc70bf9259
Author: andregp <andregp@ciandt.com>
Date:   Thu Apr 7 23:05:34 2022 -0300
    Applying patch #17

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

GRcwolf’s picture

FileSize
3.39 KB

I created a patch for Drupal 10.0, if anybody else needs to patch this.
It's the same as #15 but without the tests.

cbccharlie’s picture

Hi,

Current patches are not working with entities without translations (langcode column missing).

Column not found: 1054 Unknown column 'ENTITY.langcode' in 'on clause'.

mohit_aghera’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
12.53 KB
11.56 KB

- Combining both the PR's changes in single patch.
- Making it ready for 10.1.x's latest head
- Put back the original test cases from previous patches.

Addressing following feedback as well:

shouldn't this check for the entity type through the ResourceObject and make the language depend on what type of entiity this is?

Now, checking resource type and validating content/config entity. From there we are deciding the language type.

@cbccharlie
- I think it seems to be working fine for entities without translation. I tried to run `testRead()` in JsonApiFunctionalTest.
It worked fine for me.
Can you share if there are any specific things with your setup?
I tried following steps to validate the issue.
- Install standard profile.
- Enable jsonapi module
- Create a couple of page content
- Visit http://127.0.0.1:8080/jsonapi/node/page and I was able to see the results.

@alexpott
Can you please elaborate further #26
I am somewhat confused about it.

Status: Needs review » Needs work

The last submitted patch, 39: 3186834-39.patch, failed testing. View results

mohit_aghera’s picture

Status: Needs work » Needs review
FileSize
12.62 KB
1.28 KB

Fixing test case failures.
Ensuring that entity is translatable before passing the langcode.

@cbccharlie
I think this should fix the failures that you were experiencing.
Let me know if that helps.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative, +Needs change record

Changes look good. Only think I can think of is a change record that should also be added to the trigger_error.

mohit_aghera’s picture

Status: Needs work » Needs review
FileSize
12.7 KB
2.21 KB

Added a change record here https://www.drupal.org/node/3357049
Updated the patch accordingly.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs change record

CR short and to the point.

Changes look good to me.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 43: 3186834-43.patch, failed testing. View results

mohit_aghera’s picture

Triggering retest for #43 since failures seems random.

rpayanm’s picture

Status: Needs work » Reviewed & tested by the community

Left it back to RTBC per #44

bbrala’s picture

Status: Reviewed & tested by the community » Needs work

Great work everyone! Some small nits ;)

+++ b/core/modules/jsonapi/src/Controller/EntityResource.php
@@ -170,14 +181,16 @@ class EntityResource {
-   * @param \Symfony\Component\Serializer\SerializerInterface|\Symfony\Component\Serializer\Normalizer\DenormalizerInterface $serializer
+   * @param \Symfony\Component\Serializer\SerializerInterface $serializer

I don't understand this change, and do not see the resoning on this in the comments. That worries me.

+++ b/core/modules/jsonapi/src/Controller/EntityResource.php
@@ -189,6 +202,11 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Ent
+    if (!$language_manager) {
+      @trigger_error('The language_manager service must be passed to ' . __NAMESPACE__ . '\EntityResource::__construct(). It was added in drupal:10.1.x and will be required before drupal:11.0.0. See https://www.drupal.org/node/3357049', E_USER_DEPRECATED);

Can we please be more explicit here? Perhaps:

if ($language_manager === NULL) {
...
Vidushi Mehta’s picture

This patch only addressing the second point mentioned by #48

Vidushi Mehta’s picture

Status: Needs work » Needs review
bbrala’s picture

Thanks, your interdiff does look weird though, you sure you interdiffed the right patches?

First feedback in my issue is indeed not addressed yet.

smustgrave’s picture

Status: Needs review » Needs work

Should be moved to review when all points have been addressed or least attempted

Vidushi Mehta’s picture

Yes, created an interdiff between 43-49 patches

GRcwolf’s picture

I've used the patch #43 and encountered an issue with it.

Setup

I have a D10.0.x site. It is multilingual (DE/FR).
I use the json api to get a filtered/sorted list of custom content entities.
The entity type has a translateable name and an untranslatable weight (number).

I request a list of the entities sorted by their weight (primary) and their name (secondary).
My requests look like this:
/LANGCODE/jsonapi/my_entity/my_entity?fields[my_entity--my_entity]=...&filter[status]=1&page[limit]=12&page[offset]=0&sort=field_weight,name

Problem

Not using the patch means that the name sorting doesn't work as expected for the French (non primary language) translations.
However, applying the patch causes a new issue for me.
Filtering by field_weight doesn't work anymore for French but it still works for German (primary/default language).
For French, I simply get a list sorted by name.

The cause appears to be the value in the langcode column of the my_entity__field_weight table.
I went into the database and changed the value to "und" this caused the sorting to also break for German. Updating the column again and setting the values back to 'de' fixed it again for German.

Thoughts

My issue is that the proposed solution seems to break sorting for untranslatable fields.
I guess this didn't happen before as the json api always used the default translation of the field for sorting.

I also guess that the json api is now always using the field value for the current language for sorting.
As the field has no specific value for French, it treats the field as if it where empty. The json api probably doesn't get that the field is not supposed to have a French value as it is not translatable.

Those are just my initial thoughts on this. I haven't had time to dive into the code of the json api and explore how sorting, etc. is implemented.

Please let me know if i understood something completely wrong and this is not unexpected behaviour.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

xeniksp’s picture

Applying a patch with the changes mentioned on #48 comment in the #43 patch with the interdiff.

xeniksp’s picture

xeniksp’s picture

Had the same issue mentioned in #54 with untranslatable fields.

Updated the patch so the langcode passes to the query only when the sort field is translatable.

xeniksp’s picture

_utsavsharma’s picture

Patch for 11.x.

_utsavsharma’s picture

Tried to fix failure in #61.

xeniksp’s picture

Re-roll #59 for 10.2.x

mohit_aghera changed the visibility of the branch 3186834-sorting-nodes-by--reroll to hidden.

mohit_aghera changed the visibility of the branch 3186834-sorting-nodes-by to hidden.

mohit_aghera’s picture

Thanks for the fixes @xeniksp
I think test run should be green now.

Apart from your changes in the MR, I've addressed following thigns.

  • Fixing test case failures.
  • Fix the deprecation message formatting.
  • Add the necessary trait for expectDeprecation method.
  • Add additional test case to validate the sort order when sort happens on non translatable field. This case was raised in comment #54

Failing tests are passing on local now.

I'll move issue to Needs review after test run is successful.

mohit_aghera’s picture

Status: Needs work » Needs review
smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record updates

Left some small comments.

CR appears to be outdated slightly.

Hiding patches for clarity.