Skip to content

Question: Upsert document based on multiple identifiers #3563

Closed
@bennettdams

Description

@bennettdams

Here's an easy question: I want to update or insert a document based on identifiers that together are a "logical ID".
What is the best way?

I asked this question on StackOverflow/-Exchange with more than 2k views, but no answers.

https://stackoverflow.com/questions/60869196/spring-data-mongodb-update-document-based-on-multiple-identifiers-with-composit

https://softwareengineering.stackexchange.com/questions/408145/spring-data-mongodb-update-document-based-on-multiple-identifiers-with-composit

We already have custom JPA methods for find etc. (e.g. findOneByColorAndBrand), which are "implemented" automatically by Spring. Is there any hope we get the same for save (=> saveByColorAndBrand)?

I'm so confused that this topic is not described anywhere in the internet, what am I doing differently than others? How are people saving their documents usually, if not with these "logical IDs"?


Example:

@Document(collection = "cars")
public class CarEntity {
    @Id
    private ObjectId id;
    private String color;
    private String brand;
    private Instant repairedAt;
        ...
}

Each car is identifiable with the combination of color and brand, which is the logical ID to upsert a document. I want to update the repairedAt field.

Now there are (to my knowledge) three good ways to handle that.

Option 1: Custom repository with custom upsertOneByColorAndBrand which either

1.a) uses the CarRepository internally:

public void upsertOneByColorAndBrand(final CarEntity carNew,
                                           final String color,
                                           final String brand) {
    // findOneByColorAndBrand is no custom implementation, just from the interface
    var carExisting = repo.findOneByColorAndBrand(color, brand);
    if (carExisting == null) {
        // write to DB if not existent
        repo.save(carNew);
    } else {
        // update document if existing
        carExisting.setRepairedAt(carNew.getRepairedAt());
        repo.save(carExisting);
    }
}

1.b) or uses MongoTemplate internally:

public void upsertOneByColorAndBrand(final CarEntity carNew,
                                           final String color,
                                           final String brand) {
    // define find query
    Query query = new Query();
    query.addCriteria(Criteria.where("color").is(color));
    query.addCriteria(Criteria.where("brand").is(brand));

    // create document from entity
    Document doc = new Document();
    mongoTemplate.getConverter().write(carNew, doc);
    Update update = Update.fromDocument(doc);

    // upsert
    mongoTemplate.upsert(query, update, CarEntity.class);
}

I compared the performance of option 1.a and option 1.b and the difference is in the milliseconds for a three digit amount of documents. I think that's because 1.a) splits up the "find" and "save" operation.

Option 2: You could also skip the whole custom repository implementation and just define the ID of the document yourself, based on the combination of color & brand, as the combination of these are unique in this example. When doing that, you can just use the native repo.save(car), as it will automagically use the new composite ID to operate, which skips the whole "find" part.

Are there huge downsides/benefits with option 2? In your experience, is using a composite ID in the Spring Data environment a good idea? Would you rather use compound indexes or "set" the ID manually when creating the entity?

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions