Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Returning mapped nested or empty property results in NullPointerException #264

Closed
bastiaan-dev opened this issue Jun 12, 2023 · 2 comments
Closed
Assignees
Labels
bug Something isn't working

Comments

@bastiaan-dev
Copy link

bastiaan-dev commented Jun 12, 2023

redis-om-spring.version: 0.8.3

The same NullPointer happens in 2 scenario's:

  1. One of the requested Return fields (either nested or on root level) is null
  2. The search stream does not compose the right query when using nested fields as mapped return values.

Having a nested object:

public class NestedObject {

    @Indexed
    private String nestedProperty;
}

Having a product with property on root and a nested property:

@Document
public class Product {
...

@Indexed
private String colorCode;

@Indexed
private NestedObject nestedObject;

...
}

When building a searchstream and mapping only 2 fields:

entityStream.of(Product.class)
    .map(Fields.of(Product$.NESTED_OBJECT_NESTED_PROPERTY, Product$.COLOR_CODE))
    .collect(Collectors.toList());

This results in a NullPointer exception while parsing the result:

java.lang.NullPointerException: Cannot read the array length because "bytes" is null
	at java.base/java.lang.String.<init>(String.java:1382)
	at redis.clients.jedis.util.SafeEncoder.encode(SafeEncoder.java:35)
	at com.redis.om.spring.search.stream.ReturnFieldsSearchStreamImpl.lambda$toResultTuple$5(ReturnFieldsSearchStreamImpl.java:308)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at com.redis.om.spring.search.stream.ReturnFieldsSearchStreamImpl.lambda$toResultTuple$6(ReturnFieldsSearchStreamImpl.java:306)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at com.redis.om.spring.search.stream.ReturnFieldsSearchStreamImpl.toResultTuple(ReturnFieldsSearchStreamImpl.java:301)
	at com.redis.om.spring.search.stream.ReturnFieldsSearchStreamImpl.resolveStream(ReturnFieldsSearchStreamImpl.java:290)
	at com.redis.om.spring.search.stream.ReturnFieldsSearchStreamImpl.collect(ReturnFieldsSearchStreamImpl.java:220)

SCENARIO 1:

This happens in scenario 1, when one of the products has a null value in Redis for one of the requested properties. In this case when one of the product.colorCode is NULL, the Nullpointer is is thrown while parsing to result tuples.

SCENARIO 2:

This happens in scenario 2 when the result from Redis does not contain the expected Product$.NESTED_OBJECT_NESTED_PROPERTY, field, instead only the root property is returned.

Outgoing query:

"FT.SEARCH" "nl.domain.products.ProductIdx" "LIMIT" "0" "10000" "RETURN" "2" "$.nestedObject_nestedProperty" "$.colorCode" "DIALECT" "1"

Partial Response:

4744) "nl.domain.products.Product:2372"
4745) 1) "$.colorCode"
   2) "90"

When I manualy fire a query but change the underscore '$.nestedObject_nestedProperty' to a dot '$.nestedObject.nestedProperty', the query does have expected result and null pointer disappears:

"FT.SEARCH" "nl.domain.products.ProductIdx" "LIMIT" "0" "10000" "RETURN" "2" "$.nestedObject.nestedProperty" "$.colorCode" "DIALECT" "1"

Partial Response:

4744) "nl.domain.products.Product:2372"
4745) 1) "$.nestedObject.nestedProperty"
   2) "Men"
   3) "$.colorCode"
   4) "90"

Additional note: when using the nested field within a search query, the field DOES work with an underscore in the query:

"FT.SEARCH" "nl.domain.products.ProductIdx" "(@nestedObject_nestedProperty:{MEN})"

So it seems the query should have underscore for the nested field when used as query param, but should not have the underscore when used as return value (which seems inconsistent from Redis). Scanning the code, in both cases the searchAlias of the searchField is used (containing underscore)

@bastiaan-dev bastiaan-dev changed the title Returning mapped nested property results in NullPointerException Returning mapped nested or empty property results in NullPointerException Jun 12, 2023
@bastiaan-dev
Copy link
Author

Two (failing) test cases, simulating the issues:

// issue gh-264 SCENARIO 1
  @Test
  void testMapEntityStreamsReturnNullValue() {
    Person personWithoutEmail = new Person();
    personWithoutEmail.setName("PersonWithoutEmail");
    personWithoutEmail.setEmail(null);
    personRepository.save(personWithoutEmail);

    var result = entityStream.of(Person.class)
            .filter(Person$.NAME.eq("PersonWithoutEmail"))
            .map(Fields.of(Person$.NAME, Person$.EMAIL)) // should handle empty email as mapped return value
            .collect(Collectors.toList()).stream().findFirst().get();
    assertThat(result.getFirst()).isEqualTo("PersonWithoutEmail");
    assertThat(result.getSecond()).isEqualTo(null);
  }
// issue gh-264 SCENARIO 2
  @Test
  void testMapEntityStreamsReturnNestedField() {
    var results = entityStream.of(DeepNest.class)
            .filter(DeepNest$.NEST_LEVEL1_NEST_LEVEL2_NAME.eq("nl-2-2"))
            .map(DeepNest$.NEST_LEVEL1_NEST_LEVEL2_NAME) // should handle nested property as mapped return value
            .collect(Collectors.toList());
    assertThat(results).containsOnly("nl-2-2");
  }

@bsbodden bsbodden self-assigned this Jun 13, 2023
@bsbodden bsbodden added the bug Something isn't working label Jun 13, 2023
@bsbodden
Copy link
Contributor

Thanks @bastiaan-dev !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants