We need to index different fields from one data property. This has been solved by implementing a FieldBridge, and adding the Fields there. As seen in this example from the hibernate search reference:
/**
* Store the date in 3 different fields - year, month, day - to ease the creation of RangeQuery per
* year, month or day (eg get all the elements of December for the last 5 years).
* @author Emmanuel Bernard
*/
public class DateSplitBridge implements FieldBridge {
private final static TimeZone GMT = TimeZone.getTimeZone("GMT");
public void set(String name, Object value, Document document,
LuceneOptions luceneOptions) {
Date date = (Date) value;
Calendar cal = GregorianCalendar.getInstance(GMT);
cal.setTime(date);
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) 1;
int day = cal.get(Calendar.DAY_OF_MONTH);
// set year
luceneOptions.addFieldToDocument(
name ".year",
String.valueOf( year ),
document );
// set month and pad it if needed
luceneOptions.addFieldToDocument(
name ".month",
month < 10 ? "0" : "" String.valueOf( month ),
document );
// set day and pad it if needed
luceneOptions.addFieldToDocument(
name ".day",
day < 10 ? "0" : "" String.valueOf( day ),
document );
}
}
//property
@FieldBridge(impl = DateSplitBridge.class)
private Date date;
Additionally to the example from above we set ours up as a twoWayFieldBridge:
public class DateSplitBridge implements TwoWayFieldBridge {
[...]
@Override
public Object get(String name, Document document) {
final IndexableField field = document.getField(name);
if (field != null) {
return field.stringValue();
} else {
return null;
}
}
@Override
public String objectToString(Object object) {
return object.toString();
}
The Problem is, when searching for one of the Fields defined via the FieldBridge, the search does not seem to know it:
Caused by: org.hibernate.search.exception.SearchException: Unable to find field date.day in [...] at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.objectToString(DocumentBuilderIndexedEntity.java:1052)
If the Fields are mapped (per annotation or API), the error does not occur:
//property
@Fields({@Field(name="date.day", bridge = @FieldBridge(impl=DateSplitBridge.class)),
@Field(name="date.month", bridge = @FieldBridge(impl=DateSplitBridge.class)),
@Field(name="date.year", bridge = @FieldBridge(impl=DateSplitBridge.class))})
private Date date;
But then an error comes up when indexing, because the Field is then defined in the mapping and the FieldBridge which causes this:
2021-09-08 08:42:50,063 ERROR org.hibernate.search.exception.impl.LogErrorHandler:71 - HSEARCH000058: HSEARCH000183: Unable to index instance of type [...] java.lang.IllegalArgumentException: DocValuesField "date.day" appears more than once in this document (only one value is allowed per field) at org.apache.lucene.index.SortedDocValuesWriter.addValue(SortedDocValuesWriter.java:62) ...
How should this be handled? How do we propagate the Fields from the FieldBridge to the search, to make them searchable in the first Place?
CodePudding user response:
The problem is in your query, not in the bridge. The field defined by the first bridge is not named day
, it's named <whatever your property name is>.day
. In your case, that would be date.day
.
See this part of the code:
luceneOptions.addFieldToDocument(
name ".day", // HERE
day < 10 ? "0" : "" String.valueOf( day ),
document );
Just change your query to use date.day
instead of day
.
Shameless plug: if you can, you should really consider upgrading to Hibernate Search 6, where bridge support is much more powerful and better documented.
CodePudding user response:
Hibernate Search is unable to turn the value you passed to the Search DSL into the value to use in the Lucene Query, because the field date.day
is not referenced anywhere in the Hibernate Search metadata, so it doesn't know its expected type.
Have your bridge implement MetadataProvidingFieldBridge
, so that Hibernate Search knows about the field.
If that doesn't work, you can always use .ignoreFieldBridge().ignoreAnalyzer()
when building your query using the Search DSL. For example:
queryBuilder.keyword()
.onField( "date.day" )
.ignoreFieldBridge().ignoreAnalyzer()
.matching( "10" )
.createQuery()
A reminder for other users who didn't see the other answer: if you can, you should really consider upgrading to Hibernate Search 6, where bridge support is much more powerful and better documented.