I’ve got a MongoDB collection with the following document structure:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Document
public class CustomerRouteManagementReportDocument implements TransactionDocument {
@Id
private String id;
@Indexed(unique = true)
private String customerId;
// ... other properties
private List<RouteHistoryEvent> routeHistoryEvents;
// ... other properties
}
Essentially, the following method should perform a bulk upsert: if a matching entry is found, it should append the first item from the routeHistoryEvents
item into the existing document’ routeHistoryEvents
array. Otherwise, a whole new CustomerRouteManagementReportDocument
should be inserted into the collection.
@Service
@Slf4j
public class CustomerRouteManagementReportRepositoryAdapter extends AbstractRepositoryAdapter<CustomerRouteManagementReport, CustomerRouteManagementReportDocument, CustomerRouteManagementReportDataRepository> implements CustomerRouteManagementReportRepository {
private final MongoTemplate mongoTemplate;
@Autowired
public CustomerRouteManagementReportRepositoryAdapter(ModelMapper mapper, CustomerRouteManagementReportDataRepository repository, MongoTemplate mongoTemplate) {
super(mapper, repository, CustomerRouteManagementReport.class, CustomerRouteManagementReportDocument.class);
this.mongoTemplate = mongoTemplate;
}
//... other methods
@Override
public Integer upsertTall(List<CustomerRouteManagementReportDocument> customerRouteManagementReports) {
BulkOperations bulkOps = mongoTemplate.bulkOps(BulkOperations.BulkMode.ORDERED, CustomerRouteManagementReportDocument.class);
try {
List<Pair<Query, Update>> updates = new ArrayList<>();
customerRouteManagementReports.stream().forEach(customerRouteManagementReport -> {
Query query = Query.query(Criteria.where("customerId").is(customerRouteManagementReport.getCustomerId()));
Update update = new Update();
update.addToSet("routeHistoryEvents").value(customerRouteManagementReport.getRouteHistoryEvents().get(0));
updates.add(Pair.of(query, update));
});
bulkOps.upsert(updates);
BulkWriteResult result = bulkOps.execute();
log.info("== BULK RESULT: {}, INSERTED: {}", result, result.getInsertedCount());
return result.getInsertedCount();
}
catch (Exception e) {
log.error("ERROR {}: IN UPSERTION {}", e.getClass().toGenericString(), e.getMessage());
return 0;
}
}
}
The problem is that no updates nor insertions are being made and no exception is raised either. I’ve tried with both an empty collection and a collection loaded with documents that will meet the query
condition and should be updated, but my log simply reads:
== BULK RESULT: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=4, removedCount=0, modifiedCount=4, upserts=[]}, INSERTED: 0
However, in debugging, the method is called and it shows the appropriate number of Pair
s for the number of objects that the usecase receives to process:
No MongoTemplate
logs are issued (not even for the find
operation that, I presume, it must perform in order to update my collection) which leads me to think that maybe my class doesn’t have a proper MongoTemplate
configured. However, there are no connection errors thrown either. This is our config class for MongoDB:
@Configuration
@ConditionalOnProperty(name = "spring.data.mongodb.uri-report")
@EnableReactiveMongoRepositories(basePackages = "package name",
reactiveMongoTemplateRef = ReportMongoConfig.REPORT_REACTIVE_MONGO_TEMPLATE)
public class ReportMongoConfig {
protected static final String REPORT_REACTIVE_MONGO_TEMPLATE = "reportReactiveMongoTemplate";
@Value("${spring.data.mongodb.uri-report}")
private String connectionStringReport;
@Bean
public ConnectionString getConnectionDataBaseReport() {
return new ConnectionString(connectionStringReport);
}
@Bean
public ReactiveMongoTemplate reportReactiveMongoTemplate(MappingMongoConverter mappingMongoConverter) {
return new ReactiveMongoTemplate(reactiveReportMongoDatabaseFactory(getConnectionDataBaseReport()), mappingMongoConverter);
}
@Bean
public MongoTemplate mongoTemplate(MappingMongoConverter mappingMongoConverter) {
return new MongoTemplate(reportMongoDBFactory(getConnectionDataBaseReport()), mappingMongoConverter);
}
@Bean
public ReactiveMongoDatabaseFactory reactiveReportMongoDatabaseFactory(ConnectionString getConnectionDataBaseExperience) {
return new SimpleReactiveMongoDatabaseFactory(getConnectionDataBaseExperience);
}
@Bean
public MongoDbFactory reportMongoDBFactory(ConnectionString connectionString) {
return new SimpleMongoClientDbFactory(connectionString);
}
}
I’m wondering if there’s an injection issue when providing the appropriate MongoTemplate
bean to the repository class or if there’s an issue with the queries built inside the upsertAll
method.
2
Answers
It turned out that somewhere else in a the application there's another
MongoTemplate
bean connected to a different collection. Organizing the beans using@Primary
and@Qualifier
solved the issue.Log
The above statement is wrong. As is clearly shown from the logs there are 4 updates ->
matchedCount=4 modifiedCount=4
.So you need to check why somehow there is a match that triggers an update instead of an insert during this upsert.