So i'm a novice in both ElasticSearch and Spring Data, and i've got an assignment to store the html contents of all outgoing e-mails.
@Document(indexName = "email")
@Mapping(mappingPath = "elastic/mappings/email.json")
public class EsOutboundEmail extends PartitionedDocument {
I've been told to index it by the month, i think it's for freezing old data later on, so i came up with something like this.
public String indexByMonth(AbstractDocument source) {
final IndexCoordinates cos = elasticSearchMappingInitializer.getOrCreateDatedIndex(source.getClass());
IndexQueryBuilder queryBuilder = new IndexQueryBuilder()
.withId(source.getId())
.withObject(source);
return elasticsearchOperations.index(queryBuilder.build(), cos);
}
And the naming goes like this (ignore the "initializer" bean name, that's gonna change):
public IndexCoordinates getOrCreateDatedIndex(Class<? extends AbstractDocument> clazz) {
String indexName = getIndexName(clazz);
indexName = String.format("%s-%s", indexName, now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
final IndexOperations indexOperations = elasticsearchOperations.indexOps(IndexCoordinates.of(indexName));
if (!indexOperations.exists()) {
indexOperations.create();
indexOperations.createMapping(clazz);
}
return IndexCoordinates.of(indexName);
}
public String getIndexName(Class<? extends AbstractDocument> clazz) {
if (!clazz.isAnnotationPresent(Document.class)) {
throw new IllegalStateException("Elasticsearch domain must have @Document annotation!");
}
Document annotation = clazz.getDeclaredAnnotation(Document.class);
return annotation.indexName();
}
So the result would be email-2020-06
.
Now, searching in this feels a little off:
public <T extends AbstractDocument> T searchOneDated(Query query, Class<T> clazz) {
String indexName = elasticSearchMappingInitializer.getIndexName(clazz);
indexName = format("%s-*", indexName);
return ofNullable(elasticsearchOperations.searchOne(query, clazz, IndexCoordinates.of(indexName)))
.map(SearchHit::getContent)
.orElse(null);
}
I can't shake the feeling that there's a way to do this in a much simpler way, but like i said, i'm a novice, and the reference documentation for 4.0.0 did not help with this case, so i'd appreciate any input from someone who knows more about this. Thanks in advance.
You can use a SpEL expression in the @Document
annotation, for your case this would be for example:
@Document(indexName="email-#{T(java.time.LocalDate).now().format(T(java.time.format.DateTimeFormatter).ofPattern(\"yyyy-MM\"))}")
public class EsOutboundEmail extends PartitionedDocument {
// ...
}
For saving an entity:
ESOutboundEmail email = ...;
elasticsearchOperations.save(email);
and for searching:
elasticsearchOperations.searchOne(query, clazz)
You still would need to make sure that the index is created if it does not exist (note that I added the call to putMapping(Document)
, you missed that):
final IndexOperations indexOperations = elasticsearchOperations.indexOps(EsOutboundEmail.class);
if (!indexOperations.exists()) {
indexOperations.create();
Document mapping = indexOperations.createMapping(clazz);
indexOperations.putMapping(mapping);
}
Edit 2020-06-16:
I missed the wildcard in the search:
elasticsearchOperations.searchOne(query, clazz, IndexCoordinates.of("email-*"))