The Problem
There are two kinds of data when save data to Solr: fields that we will search on, other fields that we will never search on.
The
There are two kinds of data when save data to Solr: fields that we will search on, other fields that we will never search on.
Schemaless design
We can save all non-searchable fields to one Json string field into Solr, this way, we don't have to change Solr schema every time we add/remove a field.
We can save all non-searchable fields to one Json string field into Solr, this way, we don't have to change Solr schema every time we add/remove a field.
We want to use object in java class, serialize it to json stirng when save to Solr, and deserialize the json string to object when read from Solr.
Solution:
As we use Spring Solr to save and restore data to/from Solr, all we need to do is implement and register custom solr converters that will convert object to json string when save to Solr, convert json string to object when read data from Solr.
We store the following Message data in Solr:
We store the following Message data in Solr:
Class Message { long id; // searchable fields such as activeDate, city etc MessageDetails details; } MessageDetails implements Jsonable, Serializable { // all non-searchable fields define here. } //Mark interface: this instance will be serialized to json string when save to Solr. public interface Jsonable {}
The
Solr Converters
Then we will implement Solr Converters like below.
JsonableToStringConverter will convert the object into string; StringToMessageDetailsConverter will convert string instance to MessageDetails.
public class Converters { @WritingConverter public enum JsonableToStringConverter implements Converter<Jsonable, String> { INSTANCE; @Override public String convert(final Jsonable source) { if (source == null) { return null; } final ObjectMapper mapper = new ObjectMapper(); try { return mapper.writeValueAsString(source); } catch (final JsonProcessingException e) { logger.error(MessageFormat.format("Unable to serialize to json; source: {0}", source), e); throw new BusinessException(ErrorCode.INTERNAL_ERROR, "Unable to serialize to json."); } } } @ReadingConverter public enum StringToMessageDetailsConverter implements Converter<String, MessageDetails> { INSTANCE; @Override public MessageDetails convert(final String source) { if (source == null) { return null; } try { return new ObjectMapper().readValue(source, MessageDetails.class); } catch (final IOException e) { logger.error(MessageFormat.format("Unable to deserialize from json; source: {0}", source), e); throw new BusinessException(ErrorCode.INTERNAL_ERROR, "Unable to deserialize from json."); } } } }
At last we will register these customer converters into SolrConverter.
@Bean public static SolrConverter mappingSolrConverter() { final MappingContext<? extends SolrPersistentEntity<?>, SolrPersistentProperty> mappingContext = new SimpleSolrMappingContext(); final MappingSolrConverter converter = new MappingSolrConverter(mappingContext); final List<Object> converters = new ArrayList<Object>(); converters.add(JsonableToStringConverter.INSTANCE); converters.add(StringToMessageDetailsConverter.INSTANCE); converter.setCustomConversions(new CustomConversions(converters)); return converter; }