morphia icon indicating copy to clipboard operation
morphia copied to clipboard

Dynamic collection NamingStrategy

Open agiannone opened this issue 11 months ago • 4 comments

Is your feature request related to a problem? Please describe. We use different collections based on the tenant. Previously (v1.x) it was possible to override the getCollectionName method in the Mapper. We would like to be able to use a dynamic NamingStrategy based on the current tenant. However, the current NamingStrategy sets the collectionName in the EntityModel on load and it cannot be changed after that.

Describe the solution you'd like The NamingStrategy should be called when the getCollectionName() method is called on the EntityModel. Or at least, there should be an option to make it dynamic, so that it's called on getCollectionName().

Describe alternatives you've considered Can't see any alternative options at the moment.

agiannone avatar Mar 13 '24 16:03 agiannone

You can define your own NamingStrategy and configure Morphia to use that instead. Simply pass the class name for your custom strategy rather than the named type.

evanchooly avatar Mar 13 '24 17:03 evanchooly

We have implemented the NamingStrategy but it's applied when the EntityModel is first mapped and cannot be changed after that.

In our case, we have multiple collections for the same object, one for each tenant. Which collection the object is written to depends on which tenant is currently saving the object. For example, we have tenanta.objects, tenantb.objects, tenantc.objects and so on.

The NamingStrategy allows us to manipulate the objects part of the collection name, but not to dynamically add the tenant part.

Before we used to override the getCollectionName method of the Mapper which was called when the collection name was needed. However, with the NamingStrategy that's no longer possible as it's set upfront.

agiannone avatar Mar 14 '24 06:03 agiannone

Multitenancy isn't directly supported in 2.x though it's on the roadmap for 3.x. If you need something like that now, your best bet is to create a Datastore per tenant which is less than ideal, I would agree, but as of now it's the only real option.

evanchooly avatar Mar 16 '24 00:03 evanchooly

I've made the change locally to support this use case.

I've added a new method to the EntityModelBuilder which exposes the NamingStrategy. Then in the EntityModel constructor I store a copy of the NamingStrategy in the EntityModel itself. That way the NamingStrategy can be called directly when getCollectionName is called.

@NonNull
public String getCollectionName() {
    if (namingStrategy == null) {
       throw new MappingException(Sofia.noMappedCollection(getType().getName()));
    }
    return namingStrategy.apply(type.getSimpleName());
}

As NamingStrategy is called at runtime, we can use it to supply the tenant.

Not sure if this works as a broader solution, but thought it might be worth sharing. Of course, it could be optimised, maybe with an option specifying if the NamingStrategy should be cached, in which it works as it did previously.

agiannone avatar Mar 16 '24 07:03 agiannone