Search code examples
javaelasticsearchspring-data-elasticsearch

Spring Data ElasticSearch 4.0.0 - Using wildcards to search in multiple indexes of the same class


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.


Solution

  • 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-*"))