Get mongodb documents with certain fields(projections) using Spring-Integration (annotations only)

I am trying to get all documents from a mongodb collection which were modfied in last 5 minutes with certain fields only (say field1, field2, field3 and so on). How to write a LiteralExpression to get specific fields (projections)?

My current Literal Expression return documents containing all fields (_id is timestamp of document creation in my collection):

public String getLiteralExpression(){
        long innerBoundary = Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli();
        long outerBoundary = Instant.now().toEpochMilli();
        String expression = new StringBuilder()
                .append("{'_id': {'$gt': ")
                .append(innerBoundary)
                .append(", '$lt' : ")
                .append(outerBoundary)
                .append("}}")
                .toString();
        return expression;
    }
}

Which is being invoked in InboundChannelAdapter as

@Bean
@InboundChannelAdapter(value = "pubSubChannel", poller = @Poller(fixedRate = "30000"))
public MessageSource<Object> DbReadingMessageSource() {

    Expression expression = new SpelExpressionParser().parseExpression("@myBean.getLiteralExpression()");

    MongoDbMessageSource messageSource = new MongoDbMessageSource(mongoTemplate, expression);
    messageSource.setCollectionNameExpression(new LiteralExpression(mongoTemplate.getCollectionName(MyEntity.class)));
    IntegrationFlows.from(messageSource);
    return messageSource;
}

Is there a way where I can just use MongoTemplate or MongoDbFactory instead of a LiteralExpression to fetch only certain fields (projection) in form of MongoDbMessageSource or any other format which can be fed to my pubsubChannel pipeline.

Answer

It’s a fact that the expression as a second MongoDbMessageSource argument can be resolved to the org.springframework.data.mongodb.core.query.Query object. So, it might not be just a plain literal expression. For your projection use-case you may write something like:

new BasicQuery([QUERY_STRING], [FIELD_STRING])

to be returned from your @myBean.getLiteralExpression().

That Query API is pretty flexible and provides a lot of fluent hooks to be configured for the final MongoDB query. For example it has a fields() for include/exclude callbacks for specific fields you would like to be returned.

More info about Query API in the Spring Data MongoDB manual: https://docs.spring.io/spring-data/mongodb/docs/2.1.5.RELEASE/reference/html/#mongodb-template-query

If you would like to use MongoTemplate directly instead, you need to write a custom code which should be called from the MethodInvokingMessageSource wrapper with the same @InboundChannelAdapter configuration. In that code you still need to build such a Query object to be able to delegate to the MongoTemplate.find(). That is exactly what is done in the MongoDbMessageSource.

Out of question: your DbReadingMessageSource() configuration is slightly wrong. You can’t call IntegrationFlows.from(messageSource); from that bean definition. The MongoDbMessageSource must be configured as a separate @Bean and already without @InboundChannelAdapter annotation. The IntegrationFlow has to be another @Bean and there you really can use your DbReadingMessageSource() from that from(). But again: without @InboundChannelAdapter. See Reference Manual: https://docs.spring.io/spring-integration/docs/current/reference/html/#java-dsl-inbound-adapters

Leave a Reply

Your email address will not be published. Required fields are marked *