spring-projects / spring-data-couchbase

Provides support to increase developer productivity in Java when using Couchbase. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
https://spring.io/projects/spring-data-couchbase
Apache License 2.0
277 stars 191 forks source link

Set document expiration dynamically #1898

Closed jorgerod closed 8 months ago

jorgerod commented 9 months ago

Hi

We have a use case in which depending on some conditions, the expiration time of a document is variable, therefore, every time I save the document previously I have to dynamically calculate that expiration time.

For that case, as far as I have seen, at repository level, there is no option.

At the template level, I could do it by overwriting the save method and adding withExpiry when necessary (in fact, this method is defined but not used in the template.)

I think it would be interesting to add the feature at the repository level to make it easier to use.

How do you see it?

mikereiche commented 9 months ago

We have a use case in which depending on some conditions, the expiration time of a document is variable, therefore, every time I save the document previously I have to dynamically calculate that expiration time.

I'm not sure if you want to (a) set the document to expire at a different time every time it is updated; or (b) just to keep the same expiration time even when the document is updated [the sdk has a preserveExpiry for this]; or (c) just set expiration times of different documents to different values. All three can be done using the withOptions() below.

At the template level, I could do it by overwriting the save method and adding withExpiry when necessary (in fact, this method is defined but not used in the template.)

I'm not sure why you state "but not used in the template". It can be used if the application chooses to use it. (it is not used by the repository, as there is no api).

I think it would be interesting to add the feature at the repository level to make it easier to use.

This is such a good idea that it is already implemented. Make your repository interface extend DynamicProxyable

@Repository
public interface AirlineRepository extends CouchbaseRepository<Airline, String>, DynamicProxyable<AirlineRepository> {..}

Then an xxxxxOptions for the Couchbase SDK can be set with :

airlineRepository.withOptions(InsertOptions.insertOptions().withExpiry(....)).save(myAirline);

There is a little trickery here that repository.save() will sometimes use insert, sometimes upsert, and sometimes replace and the caller must determine which is going to be used and make the withOptions() argument match.

And the same technique can be used for any repository call, just ensure that the xxxxOptions matches the underlying Couchbase Java SDK call (findById does a get() so requires a GetOptions, all the other query methods use Query so a QueryOptions etc.

org.springframework.data.couchbase.repository.CouchbaseRepositoryQueryIntegrationTests

@Test
public void testExpiration() {
    Airport airport = new Airport("1", "iata21", "icao21");
    airportRepository.withOptions(InsertOptions.insertOptions().expiry(Duration.ofSeconds(10))).save(airport);
    Airport foundAirport = airportRepository.findByIata(airport.getIata());
    assertNotEquals(0, foundAirport.getExpiration());
    airportRepository.delete(airport);
}
jorgerod commented 9 months ago

Hi @mikereiche

I didn't know DynamicProxyable. Checking in detail, I don't think it is quite correct.

At the spring-data-couchbase user level, I don't think it would be intuitive or user friendly to have to know if the operation will be insert, replace or upsert. It is logic that spring-data-couchbase abstracts using the save method. I think it would be bringing couchbase-sdk concepts to the spring-data-couchbase layer.

I don't know if this could be done in a more user-friendly way.

In the meantime, I have added this issue to the couchbase sdk so that InsertOptions.java, ReplaceOptions.java and UpsertOptions.java extend from a new class and have the common properties.

How do you see it?

mikereiche commented 9 months ago

" I don't think it would be intuitive or user friendly to have to know if the operation will be insert, replace or upsert."

You can write a wrapper for save() that has the same logic as save() to produce the required xxxxOptions object. If you want to make that totally invisible, you could add this to save() where it selects one of insert, upsert or replace. However - if the customer had provided - for example - ReplaceOptions, but InsertOptions were required and the ReplaceOptions had some setting that did not exist in InsertOptions - you'd need to throw an exception.

Spring Data Repository methods don't have implementation-specific parameters.

mikereiche commented 8 months ago

I'm going to close this. If there are more question, please reopen.