question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Query Projection do not group all the children under same parent id when used with order by parent name

See original GitHub issue

I have below query to read a list of objects containing parent child relationship

 OrderSpecifier<String> orderBy;
    if (SortOrder.ASC.equals(order)) {
      orderBy = definition.id.upper().asc();
    } else {
      orderBy = definition.id.upper().desc();
    }

newQuery().distinct().from(definition)
        .where(definition.clientId.in(clientId))
        .where(definition.id.in(ids))
        .orderBy(orderBy)
       .leftJoin(definition.aliases, alias)
        .transform(
            groupBy(definition.id).list(
                Projections.fields(
                    GroupDefinitionEntity.class,
                    definition.id,
                    definition.clientId,
                    definition.name,
                    definition.groupType,
                    definition.mnemonic,
                    definition.opaqueId,
                    list(Projections.fields(
                        AliasEntity.class,
                        alias.clientId,
                        alias.aliasId,
                        alias.aliasType,
                        alias.assigningAuthority,
                        alias.source).skipNulls())
                        .as(definition.aliases),
                    definition.createdDtTm,
                    definition.updatedDtTm)
                    .skipNulls()));
though when I execute this it doesn’t group all the children together and I get 2 objects with the same parent id
{
      "id": "958f-ba7f8897b890",
      "client_id": "9346-666d6ff39329",
      "name": " 00Testgrouppleaseignore-1",
      "group_type": "MULTIVALUED",
      "mnemonic": "UNKNOWN",
      "members": [],
      "aliases": [
        {
          "alias_id": "3214345",
          "assigning_authority": "9346-666d6ff39329",
          "source_type": "43db-b2ae-361aba8da88f",
          "alias_type": "EXTERNAL"
        },
        {
          "alias_id": "543114",
          "assigning_authority": "9346-666d6ff39329",
          "source_type": "43db-b2ae-361aba8da88f",
          "alias_type": "EXTERNAL"
        },
        {
          "alias_id": "98765431148",
          "assigning_authority": "9346-666d6ff39329",
          "source_type": "43db-b2ae-361aba8da88f",
          "alias_type": "EXTERNAL"
        },
        {
          "alias_id": "9876543114123",
          "assigning_authority": "9346-666d6ff39329",
          "source_type": "43db-b2ae-361aba8da88f",
          "alias_type": "EXTERNAL"
        }
      ],
      "tags": [],
      "created_at": "2018-06-28T21:15:17Z",
      "updated_at": "2018-06-28T21:15:17Z"
    }

and

{
      "id": "18420125-3f71-44f8-958f-ba7f8897b890",
      "client_id": "9346-666d6ff39329",
      "name": " 00Testgrouppleaseignore-1",
      "group_type": "MULTIVALUED",
      "mnemonic": "UNKNOWN",
      "members": [],
      "aliases": [
        {
          "alias_id": "9876543214",
          "assigning_authority": "451a-9346-666d6ff39329",
          "source_type": "43db-b2ae-361aba8da88f",
          "alias_type": "EXTERNAL"
        },
        {
          "alias_id": "98765432149",
          "assigning_authority": "9346-666d6ff39329",
          "source_type": "43db-b2ae-361aba8da88f",
          "alias_type": "EXTERNAL"
        }
      ],
      "tags": [],
      "created_at": "2018-06-28T21:15:17Z",
      "updated_at": "2018-06-28T21:15:17Z"
    }

I think the issue is with order by clause. I am ordering by on definition name which is a string. is this a known issue?

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:8 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
shrdscommented, Jul 5, 2018

Update on this issue.

I have verified by adding this unit test

add a DummyFetchableQuery to AbstractGroupByTest.java

  protected static final DummyFetchableQuery<Tuple> USERS_W_UNORDERED_ROWS = projectable(
        row(null, "null post", 7, "comment 7"),
        row(1, "post 1", 1, "comment 1"),
        row(1, "post 1", 2, "comment 2"),
        row(1, "post 1", 3, "comment 3"),
        row(null, "null post", 8, "comment 8"),
        row(2, "post 2", 4, "comment 4"),
        row(2, "post 2", 5, "comment 5"),
        row(3, "post 3", 6, "comment 6")
    );

Then add below to GroupByListTest.java

@Test
    public void group_order_with_unordered_rows() {
        List<Group> results = USERS_W_UNORDERED_ROWS
            .transform(groupBy(postId).list(postName, set(commentId)));

        assertEquals(4, results.size());
    }

The test fails with below error

java.lang.AssertionError: 
Expected :4
Actual   :5
 <Click to see difference>


	at org.junit.Assert.fail(Assert.java:88)
	at org.junit.Assert.failNotEquals(Assert.java:834)
	at org.junit.Assert.assertEquals(Assert.java:645)
	at org.junit.Assert.assertEquals(Assert.java:631)
	at com.querydsl.core.group.GroupByListTest.group_order_with_unordered_rows(GroupByListTest.java:56)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)


Process finished with exit code 255

There are two solutions to resolve the issue.

  1. Ideal fix is to update the querydsl code to preserve the previously read row before creating a new Group object
 @Override
    public List<V> transform(FetchableQuery<?,?> query) {
        // create groups
        FactoryExpression<Tuple> expr = FactoryExpressionUtils.wrap(Projections.tuple(expressions));
        boolean hasGroups = false;
        for (Expression<?> e : expr.getArgs()) {
            hasGroups |= e instanceof GroupExpression;
        }
        if (hasGroups) {
            expr = withoutGroupExpressions(expr);
        }
        final CloseableIterator<Tuple> iter = query.select(expr).iterate();

        List<V> list = Lists.newArrayList();
        List<K> previouslyReadIds = Lists.newArrayList();
        GroupImpl group = null;
        K groupId = null;
        while (iter.hasNext()) {
            @SuppressWarnings("unchecked") //This type is mandated by the key type
            K[] row = (K[]) iter.next().toArray();
            if (group == null) {
                group = new GroupImpl(groupExpressions, maps);
                groupId = row[0];
                previouslyReadIds.add(row[0]);
            } else if (!Objects.equal(groupId, row[0]) && !previouslyReadIds.contains(row[0])) {
                list.add(transform(group));
                group = new GroupImpl(groupExpressions, maps);
                groupId = row[0];
                previouslyReadIds.add(row[0]);
            }
            group.add(row);
        }
        if (group != null) {
            list.add(transform(group));
        }
        iter.close();
        return list;
    }

this fixes the tests. screen shot 2018-07-05 at 9 29 48 am

OR 2. Add another OrderBy in your query to order by ids with the field you are sorting the results.

OrderSpecifier<String> orderBy = null;
    boolean descending = order.equals(SortOrder.DESC);
    if (sort != null) {
      switch (sort) {
        case ID:
          orderBy = descending ? group.id.desc() : group.id.asc();
          break;
        case MNEMONIC:
          orderBy = descending ? group.mnemonic.desc() : group.mnemonic.asc();
          break;
        case NAME:
          orderBy = descending ? group.name.lower().desc() : group.name.lower().asc();
      }
    } else {
      orderBy = descending ? group.id.desc() : group.id.asc();
    }

List<GroupEntity> groupEntities = newQuery().distinct().from(group)
        .where(group.clientId.in(clientId))
        .where(group.id.in(ids))
        .orderBy(orderBy)
        .leftJoin(group.aliases, alias)
        .orderBy(group.id
            .asc()) //this is necessary to maintain the resultset order when its grouped by field other than id
        .transform(
            groupBy(group.id).list(
                Projections.fields(
                    GroupEntity.class,
                    group.id,
                    group.rawGroupId,
                    group.definitionGroupId,
                    group.groupViewId,
                    group.name,
                    group.clientId,
                    group.groupType,
                    group.mnemonic,
                    group.opaqueId,
                    group.groupTypeUniquenessId,
                    list(Projections.fields(
                        AliasEntity.class,
                        alias.clientId,
                        alias.aliasId,
                        alias.aliasType,
                        alias.assigningAuthority,
                        alias.source).skipNulls())
                        .as(group.aliases),
                    group.createdDtTm,
                    group.updatedDtTm)
                    .skipNulls()));
0reactions
jwgmeligmeylingcommented, Jul 29, 2020

This issue is supposedly fixed by https://github.com/querydsl/querydsl/pull/2330 (according to the author of this issue). That pull request is now merged. Please reopen or file an new issue if the issue remains.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Linq group by parent property order by child - Stack Overflow
If I've understood your requirement correctly you want to group all of the parents by type and then chose only one parent from...
Read more >
How to Query a Parent-Child Tree in SQL | LearnSQL.com
Here, the column id shows the child's ID. To find out who that child's parent is, you have to look at the column...
Read more >
Query domain-specific language (DSL) :: SIREN ... - Siren Platform
Both the parent and child fields must be of the same type. For hash and broadcast joins, all join fields must be aggregatable....
Read more >
Querying Hierarchical Data - Snowflake Documentation
This topic described hierarchies and how parent-child relationships can be used by recursive CTEs (common table expressions) and CONNECT BY clauses. In all...
Read more >
Working with Collections | Defining and Using Classes
Default Projection of Array Properties. By default, an array property is projected as a child table, which is in the same package as...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found