We are migrating from Solr 4(part of DataStax) to Solr Cloud 5(without Datastax).
In previous code, we are using Spring Data Solr to talk with Solr, we are using SolrCrudRepository and also map closed set value to java enum class(not use solr.EnumField), map multiple value field to Set.
As Spring Data Solr doesn't work with Solr Cloud 5, we have to replace the DAO layer(SolrCrudRepository) to Use SolrJ directly.
But SolrJ doesn't support map field to Java Enum or map multiple value field to Set.
We don't want to change the Enum to String, change set to List, as if so, we need change a lot of code.
We want keep the Model class untouched. So I decided to mix them: Use Spring Data Solr to convert between SolrInputDocument and Java object, use SolrJ to update or query Solr Server.
This works in most case. But there is one issue: when it stores enum to solr, it saves something like xx.enums.UserType:TypeA, when it reads from solr server, tries to convert the string to enum, it fails with the following error.
Java.lang.IllegalArgumentException: No enum constant xx.enums.UserType:TypeA
at java.lang.Enum.valueOf(Enum.java:236)
To fix this, I have to copy Spring org.springframework.core.convert.support.EnumToStringConverter and register it in SolrConverter in the configuration class like below:
In previous code, we are using Spring Data Solr to talk with Solr, we are using SolrCrudRepository and also map closed set value to java enum class(not use solr.EnumField), map multiple value field to Set.
As Spring Data Solr doesn't work with Solr Cloud 5, we have to replace the DAO layer(SolrCrudRepository) to Use SolrJ directly.
But SolrJ doesn't support map field to Java Enum or map multiple value field to Set.
We don't want to change the Enum to String, change set to List, as if so, we need change a lot of code.
We want keep the Model class untouched. So I decided to mix them: Use Spring Data Solr to convert between SolrInputDocument and Java object, use SolrJ to update or query Solr Server.
public abstract class XModelRepository<T extends XModel> implements IXModelRepository<T> { @Value("${solr.server.core.collection_name}") private String collection; @Autowired protected SolrClient solrServer; @Autowired protected SolrConverter converter; public final void save(final XModel model) { try { final Iterable<SolrInputDocument> docs = converter.write(Lists.newArrayList(model)); final UpdateResponse response = solrServer.add(getCollection(), docs.iterator()); softCommit(); } catch (IOException | SolrServerException e) { throw new WrappedException(ErrorCode.INTERNAL_ERROR, e); } } public XModel findOne(final String id) { try { final SolrDocument doc = solrServer.getById(collection, id); return converter.read(XModel.class, doc); } catch (SolrServerException | IOException e) { throw new WrappedException(ErrorCode.INTERNAL_ERROR, e); } } }
This works in most case. But there is one issue: when it stores enum to solr, it saves something like xx.enums.UserType:TypeA, when it reads from solr server, tries to convert the string to enum, it fails with the following error.
Java.lang.IllegalArgumentException: No enum constant xx.enums.UserType:TypeA
at java.lang.Enum.valueOf(Enum.java:236)
To fix this, I have to copy Spring org.springframework.core.convert.support.EnumToStringConverter and register it in SolrConverter in the configuration class like below:
@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>(); final DefaultConversionService conversionService = new DefaultConversionService(); converters.add(new EnumToStringConverter(conversionService)); final CustomConversions customConversions = new CustomConversions(converters) { @Override public boolean isSimpleType(final Class<?> clazz) { if (Enum.class.equals(clazz) || Enum.class.isAssignableFrom(clazz)) { return false; } return super.isSimpleType(clazz); } }; converter.setCustomConversions(customConversions); return converter; } // Copied from Spring org.springframework.core.convert.support.EnumToStringConverter final class EnumToStringConverter implements Converter<Enum<?>, String>, ConditionalConverter { private final ConversionService conversionService; public EnumToStringConverter(final ConversionService conversionService) { this.conversionService = conversionService; } public boolean matches(final TypeDescriptor sourceType, final TypeDescriptor targetType) { for (@SuppressWarnings("rawtypes") final Class interfaceType : ClassUtils.getAllInterfacesForClass(sourceType.getType())) { if (this.conversionService.canConvert(TypeDescriptor.valueOf(interfaceType), targetType)) { return false; } } return true; } public String convert(final Enum<?> source) { return source.name(); } }