Currently migrating from HS5 and trying to move away from index null as. We have followed the documentations example to use the exists predicate with negation as a null check, however this causes issues when the field we are checking is in a list of child objects.
If parent has two child records with a field called “name”, one of which is null and one of which is not null the above method will not match the parent record as parent.child.name exists, where as for our use case we would want it to.
I am struggling to see a way we could make this work with the existing boolean logic. Is there a way to make this work, or should we revert back to using index null as?
I can see two ways.
Using nested object fields
Make the “child” object field (the one holding the name) nested and wrap your exists()
predicate in a nested predicate.
E.g. something like this:
@Indexed
public class Parent {
@IndexedEmbedded(structure = NESTED)
List<Child> child;
}
public class Child {
@FullTextField
String name;
}
public List<Parent> search(...) {
return searchSession.search( Parent.class )
.where( f -> f.nested().objectField( "child" )
.nest( f.bool()
.mustNot( f.exists().field( "child.name" ) )
) )
.fetchHits( 20 );
}
Use a custom field and bridge
Perhaps simpler, define an additional boolean field next to your name
field, and use a match
predicate on that field (you don’t need a negation then):
@Indexed
public class Parent {
@IndexedEmbedded
List<Child> child;
}
public class Child {
@FullTextField
@GenericField(name = "is_unnamed", valueBridge = @ValueBridgeRef(type = IsEmptyOrNullBridge.class))
String name;
}
public class IsEmptyOrNullBridge implements ValueBridge<String, Boolean> {
@Override
public Boolean toIndexedValue(String value, ValueBridgeToIndexedValueContext context) {
return value == null || value.isEmpty();
}
}
public List<Parent> search(...) {
return searchSession.search( Parent.class )
.where( f -> f.match().field( "child.is_unnamed" ).matching( true ) )
.fetchHits( 20 );
}
Thanks again for the quick help, I’ll give these a go
No problem.
That being said, you can still use indexNullAs
if you want.
It’s just that now, you will have to handle the null token explicitly, i.e. decide of a string to index instead of null
(e.g. “NO_NAME”) and search for that string instead of null in your search query.
That’s the main change since 5.x: now it’s clear that indexNullAs
is just a hack, and it’s clearer that it has limitations (what if someone is named “NO_NAME”? ). But if it works for you, then it’s fine to use it.
EDIT: Ah, and also, indexNullAs
is not available on full-text fields, due to underlying technical constraints. So, if your field is a @FullTextField
, then indeed, you should look for alternatives.