AEM: Changes in GraphQL handling of null values

Last update: 2024-01-30

The first change affects filter conditions leveraging the CONTAINS_NOT operator, the other two concern null values in filter conditions on multi-values fields.

Description

Environment
Adobe Experience Manager (AEM) as a Cloud Service

Issue/Symptoms
In the release 2023.02.11382, some improvements have been made in the GraphQL (Open-Source Query Language) implementation that might cause unexpected behavior in the application code, in case when it relied on the erroneous behavior of the older releases. The below mentioned resolution helps achieve that Adobe Experience Manager (AEM) GraphQL now returns content with null values consistently across all filter operations.

Resolution

CONTAINS_NOT and null values
 
The first change affects filter conditions leveraging the CONTAINS_NOT operator. In previous releases, this CONTAINS_NOT did not return content fragments containing null values in the field to which the filter condition was applied. This is inconsistent with similar operators like EQUALS_NOT or NOT_AT.
Please ensure your client application is able to handle null values that are returned by AEM when using CONTAINS_NOT.
 
Example:

Filter fragment Data Result before 2023.02.11382 Result starting with 2023.02.11382
myField: {
    value: “frag”, 
    _op: CONTAINS_NOT
}
[
    {
        myField: null
    }, 
    {
        myField: “Some text”
    },
    {
        myField: “Text fragment”
    }
]
[
    {
        myField: “Some text”
    }
]
[
    {
        myField: null
    },
    {
        myField: “Some text”
    }
]

Apply modes ALL_OR_EMPTY/ALL and null values
Another issue that was fixed in 2023.02.11382 is related to null values in filter conditions on multi-values fields. Apply mode ALL_OR_EMPTY (on arrays/multi-value fields) didn’t return fragments with null values for the field in question, therefore it was actually working as you would expect it for the ALL apply method. At the same time, filtering a multi-value field with apply mode ALL would include fragments with empty content for that field, but it shouldn’t. It is recommended to test the queries that use one of these apply modes and change them if they don’t return the expected result set in 2023.02.11382 anymore.

Note that the problem only existed for some operators (pre 2023.02.11382).
For example: EQUALS_NOT worked as expected for apply method ALL_OR_EMPTY, whereas CONTAINS did not.
Example:

Filter fragment Data Result before 2023.02.11382 Result starting with 2023.02.11382
myArray: {
  _expressions: {
    value: “1.3”
    _operator: EQUALS_NOT
    _apply: ALL
  }
}
[
  {
    myArray: null
  },
  {
    myArray: [ “1.1”, “1.2”, “1.3” ]
  },
  {
    myArray: [ “2.1”, “2.2”, “2.3” ]
  }
]
[
  {
    myArray: null
  },
  {
    myArray: [ “2.1”, “2.2”, “2.3” ]
  }
]
[
  {
    myArray: [ “2.1”, “2.2”, “2.3” ]
  }
]
myArray: {
  _expressions: {
    value: “2.”
    _operator: CONTAINS
    _apply: ALL_OR_EMPTY
  }
}
[
  {
    myArray: [ “2.1”, “2.2”, “2.3” ]
  }
]
[
  {
    myArray: null
  },
  {
    myArray: [ “2.1”, “2.2”, “2.3” ]
  }
]

Apply modes: INSTANCES, _instances: 0 and null values
The last issue is also related to null values in filter conditions on multi-value fields. Apply mode INSTANCES with_instances: 0 didn’t match the content fragments that contain null values in the respective field before 2023.02.11382, but it will be starting with 2023.02.11382.
It is recommended to test the queries that use this constellation and change them if they don’t return the expected result set in 2023.02.11382 anymore.
Example:

Filter fragment Data Result before 2023.02.11382 Result starting with 2023.02.11382
myArray: {
  _expressions: {
    value: “2.”
    _operator: CONTAINS
    _apply: INSTANCES
    _instances: 0
  }
}
[
  {
    myArray: null
  },
  {
    myArray: [ “1.1”, “1.2”, “1.3” ]
  },
  {
    myArray: [ “2.1”, “2.2”, “2.3” ]
  }
]
[
  {
    myArray: [ “1.1”, “1.2”, “1.3” ]
  }
]
[
  {
    myArray: null
  },
  {
    myArray: [ “1.1”, “1.2”, “1.3” ]
  }
]

Improvements that may affect customers with ft-sites-97 enabled
Customers who had the ft-sites-97(GraphQL pagination & optimized filtering) feature flag enabled beforerelease 2023.02.11382 might experience one or more of the following changes in behavior after upgrading to 2023.02.11382.
Customers that don’t have this feature flag enabled, are not affected by this section.
null handling with conditions that check for non-equality
Fragment fields with null values were intermittently not matched by the filters that were checking for non-equality.

If your code relies on these fragments not being returned, you will have to adjust your code to accommodate for the expected behavior.

Filter fragment Data Result from intermediate implementation Result from final implementation
myField: {
  value: “Some text”,
  _op: EQUALS_NOT
}
[
  {
    myField: null
  },
  {
    myField: “Some text”
  },
  {
    myField: “Text fragment”
  }
]
[
  {
    myField: “Some text”
  }
]
[
  {
    myField: null
  },
  {
    myField: “Some text”
  }
]

Filter operator NOT_AT with date/time values
Filter operator NOT_AT was intermittently not working correctly with data/times values. Instead, it was working more like the AFTER condition.
If your code relies on the incorrect behavior, you should switch the filter condition to AFTER.
Arrays: INSTANCES with _instances: 0 not working correctly
Filter condition INSTANCES (on arrays/multivalue fields) with _instances set to 0 intermittently didn’t work as expected, it didn’t return anything where is actually should have.

Filter fragments affected by this issue would look like: doubleArray: { _expressions: { value: 0, _operator: GREATER, _apply: INSTANCES, _instances: 0 } }

If you were relying on the erroneous behavior, you need to rewrite your query to return the same results as before.

Filter fragment Data Result from intermediate implementation Result from final implementation
myArray: {
  _expressions: {
    value: 20
    _operator: EQUAL
    _apply: INSTANCES
    _instances: 0
  }
}
[
  {
    myArray: null
  },
  {
    myArray: [ 10, 20, 30 ]
  },
  {
    myArray: [ 11, 12, 13 ]
  }
]
[ ] [
  {
    myArray: null
  },
  {
    myArray: [ 11, 12, 13 ]
  }
]

On this page