io.evitadb.api.EvitaSessionContract Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of evita_api Show documentation
Show all versions of evita_api Show documentation
Module contains external API of the evitaDB.
/*
*
* _ _ ____ ____
* _____ _(_) |_ __ _| _ \| __ )
* / _ \ \ / / | __/ _` | | | | _ \
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* Copyright (c) 2023-2024
*
* Licensed under the Business Source License, Version 1.1 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/FgForrest/evitaDB/blob/master/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.evitadb.api;
import io.evitadb.api.TransactionContract.CommitBehavior;
import io.evitadb.api.exception.CollectionNotFoundException;
import io.evitadb.api.exception.EntityAlreadyRemovedException;
import io.evitadb.api.exception.EntityClassInvalidException;
import io.evitadb.api.exception.EntityTypeAlreadyPresentInCatalogSchemaException;
import io.evitadb.api.exception.InstanceTerminatedException;
import io.evitadb.api.exception.SchemaAlteringException;
import io.evitadb.api.exception.TemporalDataNotAvailableException;
import io.evitadb.api.exception.UnexpectedResultCountException;
import io.evitadb.api.exception.UnexpectedResultException;
import io.evitadb.api.file.FileForFetch;
import io.evitadb.api.proxy.ProxyFactory;
import io.evitadb.api.query.Query;
import io.evitadb.api.query.QueryConstraints;
import io.evitadb.api.query.RequireConstraint;
import io.evitadb.api.query.filter.FilterBy;
import io.evitadb.api.query.head.Collection;
import io.evitadb.api.query.order.OrderBy;
import io.evitadb.api.query.require.EntityContentRequire;
import io.evitadb.api.query.require.EntityFetch;
import io.evitadb.api.query.require.Page;
import io.evitadb.api.query.require.Require;
import io.evitadb.api.query.require.SeparateEntityContentRequireContainer;
import io.evitadb.api.query.require.Strip;
import io.evitadb.api.query.visitor.FinderVisitor;
import io.evitadb.api.requestResponse.EvitaResponse;
import io.evitadb.api.requestResponse.data.DeletedHierarchy;
import io.evitadb.api.requestResponse.data.EntityContract;
import io.evitadb.api.requestResponse.data.EntityEditor.EntityBuilder;
import io.evitadb.api.requestResponse.data.InstanceEditor;
import io.evitadb.api.requestResponse.data.SealedEntity;
import io.evitadb.api.requestResponse.data.annotation.Entity;
import io.evitadb.api.requestResponse.data.mutation.EntityMutation;
import io.evitadb.api.requestResponse.data.structure.EntityReference;
import io.evitadb.api.requestResponse.schema.CatalogSchemaContract;
import io.evitadb.api.requestResponse.schema.CatalogSchemaEditor.CatalogSchemaBuilder;
import io.evitadb.api.requestResponse.schema.EntitySchemaContract;
import io.evitadb.api.requestResponse.schema.EntitySchemaEditor.EntitySchemaBuilder;
import io.evitadb.api.requestResponse.schema.SealedCatalogSchema;
import io.evitadb.api.requestResponse.schema.SealedEntitySchema;
import io.evitadb.api.requestResponse.schema.mutation.LocalCatalogSchemaMutation;
import io.evitadb.api.requestResponse.schema.mutation.catalog.ModifyCatalogSchemaMutation;
import io.evitadb.api.requestResponse.schema.mutation.catalog.ModifyEntitySchemaMutation;
import io.evitadb.api.task.Task;
import io.evitadb.exception.EvitaInvalidUsageException;
import io.evitadb.utils.ArrayUtils;
import io.evitadb.utils.Assert;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import java.io.Closeable;
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import static io.evitadb.api.query.QueryConstraints.entityFetch;
import static io.evitadb.api.query.QueryConstraints.require;
/**
* Session are created by the clients to envelope a "piece of work" with evitaDB. In web environment it's a good idea
* to have session per request, in batch processing it's recommended to keep session per "record page" or "transaction".
* There may be multiple {@link TransactionContract transaction} during single session instance life but there is no support
* for transactional overlap - there may be at most single transaction open in single session.
*
* EvitaSession transaction behave like Snapshot
* transaction. When no transaction is explicitly opened - each query to Evita behaves as one small transaction. Data
* updates are not allowed without explicitly opened transaction.
*
* Remember to {@link #close()} when your work with Evita is finished.
* EvitaSession contract is NOT thread safe.
*
* @author Jan Novotný ([email protected]), FG Forrest a.s. (c) 2021
*/
@NotThreadSafe
public interface EvitaSessionContract extends Comparable, Closeable {
/**
* Returns Evita instance this session is connected to.
*
* @return Evita instance this session is connected to.
*/
@Nonnull
EvitaContract getEvita();
/**
* Returns unique id of the session.
*
* @return unique ID generated when session is created.
*/
@Nonnull
UUID getId();
/**
* Returns unique catalog id that doesn't change with catalog schema changes - such as renaming.
* The id is assigned to the catalog when it is created and never changes.
*
* @return unique catalog id
*/
@Nonnull
UUID getCatalogId();
/**
* Returns catalog schema of the catalog this session is connected to.
*/
@Nonnull
SealedCatalogSchema getCatalogSchema();
/**
* Returns name of the catalog this session is connected to.
*/
@Nonnull
default String getCatalogName() {
return getCatalogSchema().getName();
}
/**
* Returns current state of the catalog.
*
* @see CatalogState
*/
@Nonnull
CatalogState getCatalogState();
/**
* Returns version of the catalog that gets incremented with each transaction commit. When catalog state is set to
* {@link CatalogState#WARMING_UP} and is not yet transactional, the version stays at zero all the time.
*
* @return version of the catalog that gets incremented with each transaction commit
*/
long getCatalogVersion();
/**
* Returns TRUE if session is active (and can be used).
*/
boolean isActive();
/**
* Switches catalog to the {@link CatalogState#ALIVE} state and terminates the Evita session so that next session is
* operating in the new catalog state.
*
* Session is {@link #close() closed} only when the state transition successfully occurs and this is signalized
* by return value.
*
* @return TRUE if catalog was successfully switched to {@link CatalogState#ALIVE} state
* @see CatalogState
*/
boolean goLiveAndClose();
/**
* Terminates Evita session and releases all used resources. This method renders the session unusable and any further
* calls to this session should end up with {@link InstanceTerminatedException}. In case there were any mutations
* in read/write session and the catalog is in transactional mode, the method is finished when the changes are
* propagated to indexes. The call is equivalent to calling {@link #closeWhen(CommitBehavior)} with
* {@link CommitBehavior#WAIT_FOR_INDEX_PROPAGATION}. This method follows synchronous principles, which are
* easier to understand and use.
*
* This method is idempotent and may be called multiple times. Only first call is really processed and others are
* ignored.
*/
@Override
default void close() {
closeWhen(CommitBehavior.defaultBehaviour());
}
/**
* Terminates Evita session and releases all used resources. This method renders the session unusable and any further
* calls to this session should end up with {@link InstanceTerminatedException}. In case there were any mutations
* in read/write session and the catalog is in transactional mode, the method is finished when the change processing
* reaches the given state. This method follows synchronous principles, which are easier to understand and use.
*
* This method is idempotent and may be called multiple times. Only first call is really processed and others are
* ignored.
*/
default long closeWhen(@Nonnull CommitBehavior commitBehaviour) {
return closeNow(commitBehaviour).join();
}
/**
* Method terminates Evita session and releases all used resources. This method renders the session unusable and
* any further calls to this session should end up with {@link InstanceTerminatedException}. Method finishes
* immediately returning a {@link CompletableFuture} that will be completed when:
*
* 1. catalog is in warm-up mode: immediately
* 2. catalog is in transactional mode and no changes were made: immediately
* 3. catalog is in transactional mode and changes were made: when the change processing reaches the given state
*
* This method is idempotent and may be called multiple times, it always returns the same future.
*/
@Nonnull
CompletableFuture closeNow(@Nonnull CommitBehavior commitBehaviour);
/**
* Method creates new a new entity schema and collection for it in the catalog this session is tied to. It returns
* an {@link EntitySchemaBuilder} that could be used for extending the initial "empty"
* {@link EntitySchemaContract entity schema}.
*
* If the collection already exists the method returns a builder for entity schema of the already existing
* entity collection - i.e. this method behaves the same as calling:
*
* ``` java
* getEntitySchema(`name`).map(SealedEntitySchema::openForWrite).orElse(null)
* ```
*
* @param entityType the name of the entity collection to be created - equals to {@link EntitySchemaContract#getName()}
*/
@Nonnull
EntitySchemaBuilder defineEntitySchema(@Nonnull String entityType);
/**
* Method allows to automatically adapt schema to passed model class. The class is expected to use annotations
* from `io.data.annotation` package. The idea behind this method is that the developers will maintain their
* model in plain java classes / interfaces and communicate with Evita using those model classes they are familiar
* with and avoid using directly generic model classes derived from {@link EntityContract}.
*
* When the developers evolve their model classes they just call this method to adapt the Evita schema accordingly
* without manually altering the schema by themselves.
*
* The analyzed schema is immediately applied in the working instance within current transaction. If no transaction
* is opened new transaction is opened enveloping all generated mutations.
*
* @param modelClass the schema template class to synchronize schema with
* @see io.evitadb.api.requestResponse.schema.ClassSchemaAnalyzer
*/
@Nonnull
SealedEntitySchema defineEntitySchemaFromModelClass(@Nonnull Class> modelClass);
/**
* Method allows to automatically adapt schema to passed model class. The class is expected to use annotations
* from `io.data.annotation` package. The idea behind this method is that the developers will maintain their
* model in plain java classes / interfaces and communicate with Evita using those model classes they are familiar
* with and avoid using directly generic model classes derived from {@link EntityContract}.
*
* When the developers evolve their model classes they just call this method to adapt the Evita schema accordingly
* without manually altering the schema by themselves.
*
* This variant doesn't immediately apply the changes to the evitaDB instance. It produces "pre-configured" builder
* instance and provide them to the `postProcessor` to consumer to alter them and when the post-processor finishes
* and only then it applies the changes to the evitaDB instance.
*
* @param modelClass the schema template class to synchronize schema with
* @param postProcessor the consumer that may alter the prepared builder before applying the changes to evitaDB
* @see io.evitadb.api.requestResponse.schema.ClassSchemaAnalyzer
*/
@Nonnull
SealedEntitySchema defineEntitySchemaFromModelClass(
@Nonnull Class> modelClass,
@Nonnull SchemaPostProcessor postProcessor
);
/**
* Returns schema definition for entity of specified type.
*/
@Nonnull
Optional getEntitySchema(@Nonnull String entityType);
/**
* Returns schema definition for entity of specified type.
*
* @throws EntityClassInvalidException when entity type cannot be extracted from the class
*/
@Nonnull
Optional getEntitySchema(@Nonnull Class> modelClass)
throws EntityClassInvalidException;
/**
* Returns schema definition for entity of specified type or throws a standardized exception.
*/
@Nonnull
SealedEntitySchema getEntitySchemaOrThrow(@Nonnull String entityType)
throws CollectionNotFoundException;
/**
* Returns schema definition for entity of specified type or throws a standardized exception.
*
* @throws EntityClassInvalidException when entity type cannot be extracted from the class
*/
@Nonnull
SealedEntitySchema getEntitySchemaOrThrow(@Nonnull Class> modelClass)
throws CollectionNotFoundException, EntityClassInvalidException;
/**
* Returns list of all entity types available in this catalog.
*/
@Nonnull
Set getAllEntityTypes();
/**
* Method executes query on {@link CatalogContract} data and returns zero or exactly one entity result. Method
* behaves exactly the same as {@link #query(Query, Class)} but verifies the count of returned results and
* translates it to simplified return type.
*
* @param query input query,
* for creation use {@link Query#query(Collection, FilterBy, OrderBy, Require)} or similar methods
* for defining constraint use {@link QueryConstraints} static methods
* @return found entity reference, empty optional object if none was found
* @throws UnexpectedResultException when {@link EvitaResponse#getRecordPage()} contains data that are not assignable to `expectedType`
* @throws InstanceTerminatedException when session has been already terminated
* @see QueryConstraints for list of available filtering and ordering constraints and requirements
*/
@Nonnull
default Optional queryOneEntityReference(@Nonnull Query query)
throws UnexpectedResultException, UnexpectedResultCountException, InstanceTerminatedException {
return queryOne(query, EntityReference.class);
}
/**
* Method executes query on {@link CatalogContract} data and returns zero or exactly one entity result. Method
* behaves exactly the same as {@link #query(Query, Class)} but verifies the count of returned results and
* translates it to simplified return type.
*
* @param query input query,
* for creation use {@link Query#query(Collection, FilterBy, OrderBy, Require)} or similar methods
* for defining constraint use {@link QueryConstraints} static methods
* @return found entity itself, empty optional object if none was found
* @throws UnexpectedResultException when {@link EvitaResponse#getRecordPage()} contains data that are not assignable to `expectedType`
* @throws InstanceTerminatedException when session has been already terminated
* @see QueryConstraints for list of available filtering and ordering constraints and requirements
*/
@Nonnull
default Optional queryOneSealedEntity(@Nonnull Query query)
throws UnexpectedResultException, UnexpectedResultCountException, InstanceTerminatedException {
if (query.getRequire() == null) {
return queryOne(
Query.query(
query.getCollection(),
query.getFilterBy(),
query.getOrderBy(),
require(entityFetch())
),
SealedEntity.class
);
} else if (FinderVisitor.findConstraints(query.getRequire(), EntityFetch.class::isInstance, SeparateEntityContentRequireContainer.class::isInstance).isEmpty()) {
return queryOne(
Query.query(
query.getCollection(),
query.getFilterBy(),
query.getOrderBy(),
(Require) query.getRequire().getCopyWithNewChildren(
ArrayUtils.mergeArrays(
new RequireConstraint[]{require(entityFetch())},
query.getRequire().getChildren()
),
query.getRequire().getAdditionalChildren()
)
),
SealedEntity.class
);
} else {
return queryOne(query, SealedEntity.class);
}
}
/**
* Method executes query on {@link CatalogContract} data and returns zero or exactly one entity result. Method
* behaves exactly the same as {@link #query(Query, Class)} but verifies the count of returned results and
* translates it to simplified return type.
*
* Because result is generic and may contain different data as its contents (based on input query), additional
* parameter `expectedType` is passed. This parameter allows to check whether passed response contains the expected
* type of data before returning it back to the client. This should prevent late ClassCastExceptions on the client
* side.
*
* @param query input query,
* for creation use {@link Query#query(Collection, FilterBy, OrderBy, Require)} or similar methods
* for defining constraint use {@link QueryConstraints} static methods
* @param expectedType type of object, that is expected to be in response data,
* use one of type: {@link EntityReference} or {@link SealedEntity}
* @return found entity reference or entity itself, empty optional object if none was found
* @throws UnexpectedResultException when {@link EvitaResponse#getRecordPage()} contains data that are not assignable to `expectedType`
* @throws InstanceTerminatedException when session has been already terminated
* @throws EntityClassInvalidException when entity type cannot be extracted from the class and is not present in the query
* @see QueryConstraints for list of available filtering and ordering constraints and requirements
*/
@Nonnull
Optional queryOne(@Nonnull Query query, @Nonnull Class expectedType)
throws UnexpectedResultException, UnexpectedResultCountException, InstanceTerminatedException;
/**
* Method executes query on {@link CatalogContract} data and returns simplified list of results. Method behaves
* exactly the same as {@link #query(Query, Class)} but verifies the count of returned results and translates
* it to simplified return type. This method will throw out all possible extra results from, because there is
* no way how to propagate them in return value. If you require extra results or paginated list use
* the {@link #query(Query, Class)} method.
*
* @param query input query,
* for creation use {@link Query#query(Collection, FilterBy, OrderBy, Require)} or similar methods
* for defining constraint use {@link QueryConstraints} static methods
* @return shortened response - only list of found entities will be returned, the list respect paging / stripping
* requirements (if none defined the method behaves as first page with 20 results is requested)
* @throws UnexpectedResultException when {@link EvitaResponse#getRecordPage()} contains data that are not assignable to `expectedType`
* @throws InstanceTerminatedException when session has been already terminated
* @see QueryConstraints for list of available filtering and ordering constraints and requirements
*/
@Nonnull
default List queryListOfEntityReferences(@Nonnull Query query)
throws UnexpectedResultException, InstanceTerminatedException {
return queryList(query, EntityReference.class);
}
/**
* Method executes query on {@link CatalogContract} data and returns simplified list of results. Method behaves
* exactly the same as {@link #query(Query, Class)} but verifies the count of returned results and translates
* it to simplified return type. This method will throw out all possible extra results from, because there is
* no way how to propagate them in return value. If you require extra results or paginated list use
* the {@link #query(Query, Class)} method.
*
* @param query input query,
* for creation use {@link Query#query(Collection, FilterBy, OrderBy, Require)} or similar methods
* for defining constraint use {@link QueryConstraints} static methods
* @return shortened response - only list of found entities will be returned, the list respect paging / stripping
* requirements (if none defined the method behaves as first page with 20 results is requested)
* @throws UnexpectedResultException when {@link EvitaResponse#getRecordPage()} contains data that are not assignable to `expectedType`
* @throws InstanceTerminatedException when session has been already terminated
* @see QueryConstraints for list of available filtering and ordering constraints and requirements
*/
@Nonnull
default List queryListOfSealedEntities(@Nonnull Query query)
throws UnexpectedResultException, InstanceTerminatedException {
if (query.getRequire() == null) {
return queryList(
Query.query(
query.getCollection(),
query.getFilterBy(),
query.getOrderBy(),
require(entityFetch())
),
SealedEntity.class
);
} else if (FinderVisitor.findConstraints(query.getRequire(), EntityFetch.class::isInstance, SeparateEntityContentRequireContainer.class::isInstance).isEmpty()) {
return queryList(
Query.query(
query.getCollection(),
query.getFilterBy(),
query.getOrderBy(),
(Require) query.getRequire().getCopyWithNewChildren(
ArrayUtils.mergeArrays(
new RequireConstraint[]{require(entityFetch())},
query.getRequire().getChildren()
),
query.getRequire().getAdditionalChildren()
)
),
SealedEntity.class
);
} else {
return queryList(query, SealedEntity.class);
}
}
/**
* Method executes query on {@link CatalogContract} data and returns simplified list of results. Method behaves
* exactly the same as {@link #query(Query, Class)} but verifies the count of returned results and translates
* it to simplified return type. This method will throw out all possible extra results from, because there is
* no way how to propagate them in return value. If you require extra results or paginated list use
* the {@link #query(Query, Class)} method.
*
* Because result is generic and may contain different data as its contents (based on input query), additional
* parameter `expectedType` is passed. This parameter allows to check whether passed response contains the expected
* type of data before returning it back to the client. This should prevent late ClassCastExceptions on the client
* side.
*
* @param query input query,
* for creation use {@link Query#query(Collection, FilterBy, OrderBy, Require)} or similar methods
* for defining constraint use {@link QueryConstraints} static methods
* @param expectedType type of object, that is expected to be in response data,
* use one of type: {@link EntityReference} or {@link SealedEntity}
* @return shortened response - only list of found entities will be returned, the list respect paging / stripping
* requirements (if none defined the method behaves as first page with 20 results is requested)
* @throws UnexpectedResultException when {@link EvitaResponse#getRecordPage()} contains data that are not assignable to `expectedType`
* @throws InstanceTerminatedException when session has been already terminated
* @throws EntityClassInvalidException when entity type cannot be extracted from the class and is not present in the query
* @see QueryConstraints for list of available filtering and ordering constraints and requirements
*/
@Nonnull
List queryList(@Nonnull Query query, @Nonnull Class expectedType)
throws UnexpectedResultException, InstanceTerminatedException;
/**
* Method executes query on {@link CatalogContract} data and returns result.
*
* @param query input query,
* for creation use {@link Query#query(Collection, FilterBy, OrderBy, Require)} or similar methods
* for defining constraint use {@link QueryConstraints} static methods
* @return full response data transfer object with all available data
* @throws UnexpectedResultException when {@link EvitaResponse#getRecordPage()} contains data that are not assignable to `expectedType`
* @throws InstanceTerminatedException when session has been already terminated
* @see QueryConstraints for list of available filtering and ordering constraints and requirements
*/
@Nonnull
default EvitaResponse queryEntityReference(@Nonnull Query query)
throws UnexpectedResultException, InstanceTerminatedException {
return query(query, EntityReference.class);
}
/**
* Method executes query on {@link CatalogContract} data and returns result.
*
* @param query input query,
* for creation use {@link Query#query(Collection, FilterBy, OrderBy, Require)} or similar methods
* for defining constraint use {@link QueryConstraints} static methods
* @return full response data transfer object with all available data
* @throws UnexpectedResultException when {@link EvitaResponse#getRecordPage()} contains data that are not assignable to `expectedType`
* @throws InstanceTerminatedException when session has been already terminated
* @see QueryConstraints for list of available filtering and ordering constraints and requirements
*/
@Nonnull
default EvitaResponse querySealedEntity(@Nonnull Query query)
throws UnexpectedResultException, InstanceTerminatedException {
if (query.getRequire() == null) {
return query(
Query.query(
query.getCollection(),
query.getFilterBy(),
query.getOrderBy(),
require(entityFetch())
),
SealedEntity.class
);
} else if (FinderVisitor.findConstraints(query.getRequire(), EntityFetch.class::isInstance, SeparateEntityContentRequireContainer.class::isInstance).isEmpty()) {
return query(
Query.query(
query.getCollection(),
query.getFilterBy(),
query.getOrderBy(),
(Require) query.getRequire().getCopyWithNewChildren(
ArrayUtils.mergeArrays(
new RequireConstraint[]{require(entityFetch())},
query.getRequire().getChildren()
),
query.getRequire().getAdditionalChildren()
)
),
SealedEntity.class
);
} else {
return query(query, SealedEntity.class);
}
}
/**
* Method executes query on {@link CatalogContract} data and returns result. Because result is generic and may contain
* different data as its contents (based on input query), additional parameter `expectedType` is passed. This parameter
* allows to check whether passed response contains the expected type of data before returning it back to the client.
* This should prevent late ClassCastExceptions on the client side.
*
* @param query input query,
* for creation use {@link Query#query(Collection, FilterBy, OrderBy, Require)} or similar methods
* for defining constraint use {@link QueryConstraints} static methods
* @param expectedType type of object, that is expected to be in response data,
* use one of type: {@link EntityReference} or {@link SealedEntity}
* @return full response data transfer object with all available data
* @throws UnexpectedResultException when {@link EvitaResponse#getRecordPage()} contains data that are not assignable to `expectedType`
* @throws InstanceTerminatedException when session has been already terminated
* @throws EntityClassInvalidException when entity type cannot be extracted from the class and is not present in the query
* @see QueryConstraints for list of available filtering and ordering constraints and requirements
*/
@Nonnull
> T query(@Nonnull Query query, @Nonnull Class expectedType)
throws UnexpectedResultException, InstanceTerminatedException;
/**
* Method returns entity by its type and primary key in requested form of completeness. This method allows quick
* access to the entity contents when primary key is known.
*/
@Nonnull
Optional getEntity(@Nonnull String entityType, int primaryKey, EntityContentRequire... require);
/**
* Method returns entity by its type and primary key in requested form of completeness. This method allows quick
* access to the entity contents when primary key is known. Result object is not constrained to an evitaDB type but
* can represent any POJO, record or interface annotated with {@link io.evitadb.api.requestResponse.data.annotation}
* annotations.
*
* @throws EntityClassInvalidException when entity type cannot be extracted from the class
*/
@Nonnull
Optional getEntity(
@Nonnull Class expectedType,
int primaryKey,
EntityContentRequire... require
) throws EntityClassInvalidException;
/**
* Method returns entity with additionally loaded data specified by requirements in second argument. This method
* is particularly useful for implementation of lazy loading when application loads only parts of the entity it
* expects to be required for handling common client request and then load additional data if processing requires
* more in-depth view of the entity.
*
* @throws EntityAlreadyRemovedException when the entity has been already removed
*/
@Nonnull
T enrichEntity(@Nonnull T partiallyLoadedEntity, EntityContentRequire... require)
throws EntityAlreadyRemovedException;
/**
* Method returns entity with additionally loaded data specified by requirements in second argument. This method
* is particularly useful for implementation of lazy loading when application loads only parts of the entity it
* expects to be required for handling common client request and then load additional data if processing requires
* more in-depth view of the entity.
*
* @throws EntityAlreadyRemovedException when the entity has been already removed
*/
@Nonnull
T enrichOrLimitEntity(@Nonnull T partiallyLoadedEntity, EntityContentRequire... require)
throws EntityAlreadyRemovedException;
/**
* Method alters the {@link CatalogSchemaContract} of the catalog this session is tied to. The method is equivalent
* to {@link #updateCatalogSchema(LocalCatalogSchemaMutation...)} but accepts the original builder. This method variant
* is present as a shortcut option for the developers.
*
* @param catalogSchemaBuilder the builder that contains the mutations in the catalog schema
* @return version of the altered schema or current version if no modification occurred.
*/
default int updateCatalogSchema(@Nonnull CatalogSchemaBuilder catalogSchemaBuilder) throws SchemaAlteringException {
Assert.isTrue(
catalogSchemaBuilder.getName().equals(getCatalogName()),
"Schema builder targets `" + catalogSchemaBuilder.getName() + "` catalog, but the session targets `" + getCatalogName() + "` catalog!"
);
return catalogSchemaBuilder.toMutation()
.map(ModifyCatalogSchemaMutation::getSchemaMutations)
.map(this::updateCatalogSchema)
.orElseGet(() -> getCatalogSchema().version());
}
/**
* Method alters the {@link CatalogSchemaContract} of the catalog this session is tied to. The method is equivalent
* to {@link #updateAndFetchCatalogSchema(LocalCatalogSchemaMutation...)} but accepts the original builder. This method
* variant is present as a shortcut option for the developers.
*
* @param catalogSchemaBuilder the builder that contains the mutations in the catalog schema
* @return possibly updated body of the {@link CatalogSchemaContract} or the original schema if no change occurred
*/
@Nonnull
default SealedCatalogSchema updateAndFetchCatalogSchema(@Nonnull CatalogSchemaBuilder catalogSchemaBuilder) throws SchemaAlteringException {
Assert.isTrue(
catalogSchemaBuilder.getName().equals(getCatalogName()),
"Schema builder targets `" + catalogSchemaBuilder.getName() + "` catalog, but the session targets `" + getCatalogName() + "` catalog!"
);
return catalogSchemaBuilder.toMutation()
.map(ModifyCatalogSchemaMutation::getSchemaMutations)
.map(this::updateAndFetchCatalogSchema)
.orElseGet(this::getCatalogSchema);
}
/**
* Method alters the {@link CatalogSchemaContract} of the catalog this session is tied to. All mutations will be
* applied or none of them (method call is atomic). The method call is idempotent - it means that when the method
* is called multiple times with same mutations the changes occur only once.
*
* @param schemaMutation array of mutations that needs to be applied on current version of {@link CatalogSchemaContract}
* @return version of the altered schema or current version if no modification occurred.
*/
int updateCatalogSchema(@Nonnull LocalCatalogSchemaMutation... schemaMutation) throws SchemaAlteringException;
/**
* Method alters the {@link CatalogSchemaContract} of the catalog this session is tied to. All mutations will be
* applied or none of them (method call is atomic). The method call is idempotent - it means that when the method
* is called multiple times with same mutations the changes occur only once.
*
* @param schemaMutation array of mutations that needs to be applied on current version of {@link CatalogSchemaContract}
* @return possibly updated body of the {@link CatalogSchemaContract} or the original schema if no change occurred
*/
@Nonnull
SealedCatalogSchema updateAndFetchCatalogSchema(@Nonnull LocalCatalogSchemaMutation... schemaMutation) throws SchemaAlteringException;
/**
* Method alters one of the {@link EntitySchemaContract entity schemas} of the catalog this session is tied to.
* The method is equivalent to {@link #updateEntitySchema(ModifyEntitySchemaMutation)} but accepts the original builder.
* This method variant is present as a shortcut option for the developers.
*
* @param entitySchemaBuilder the builder that contains the mutations in the entity schema
* @return version of the altered schema or current version if no modification occurred.
*/
default int updateEntitySchema(@Nonnull EntitySchemaBuilder entitySchemaBuilder) throws SchemaAlteringException {
return entitySchemaBuilder.toMutation()
.map(this::updateEntitySchema)
.orElse(getEntitySchemaOrThrow(entitySchemaBuilder.getName()).version());
}
/**
* Method alters one of the {@link EntitySchemaContract entity schemas} of the catalog this session is tied to.
* The method is equivalent to {@link #updateAndFetchEntitySchema(ModifyEntitySchemaMutation)} but accepts
* the original builder. This method variant is present as a shortcut option for the developers.
*
* @param entitySchemaBuilder the builder that contains the mutations in the entity schema
* @return possibly updated body of the {@link EntitySchemaContract} or the original schema if no change occurred
*/
@Nonnull
default SealedEntitySchema updateAndFetchEntitySchema(@Nonnull EntitySchemaBuilder entitySchemaBuilder) throws SchemaAlteringException {
return entitySchemaBuilder.toMutation()
.map(this::updateAndFetchEntitySchema)
.orElse(getEntitySchemaOrThrow(entitySchemaBuilder.getName()));
}
/**
* Method alters one of the {@link EntitySchemaContract entity schemas} of the catalog this session is tied to. All
* mutations will be applied or none of them (method call is atomic). The method call is idempotent - it means that
* when the method is called multiple times with same mutations the changes occur only once.
*
* @param schemaMutation the builder that contains the mutations in the entity schema
* @return version of the altered schema or current version if no modification occurred.
*/
int updateEntitySchema(@Nonnull ModifyEntitySchemaMutation schemaMutation) throws SchemaAlteringException;
/**
* Method alters one of the {@link EntitySchemaContract entity schemas} of the catalog this session is tied to. All
* mutations will be applied or none of them (method call is atomic). The method call is idempotent - it means that
* when the method is called multiple times with same mutations the changes occur only once.
*
* @param schemaMutation the builder that contains the mutations in the entity schema
* @return possibly updated body of the {@link EntitySchemaContract} or the original schema if no change occurred
*/
@Nonnull
SealedEntitySchema updateAndFetchEntitySchema(@Nonnull ModifyEntitySchemaMutation schemaMutation) throws SchemaAlteringException;
/**
* Deletes entire collection of entities along with its schema. After this operation there will be nothing left
* of the data that belong to the specified entity type.
*
* @param entityType type of the entity which collection should be deleted
* @return TRUE if collection was successfully deleted
*/
boolean deleteCollection(@Nonnull String entityType);
/**
* Deletes entire collection of entities along with its schema. After this operation there will be nothing left
* of the data that belong to the specified entity type.
*
* @param modelClass model class for the entity which collection should be deleted
* @return TRUE if collection was successfully deleted
* @throws EntityClassInvalidException when entity type cannot be extracted from the class
*/
boolean deleteCollection(@Nonnull Class> modelClass) throws EntityClassInvalidException;
/**
* Renames entire collection of entities along with its schema. After this operation there will be nothing left
* of the data that belong to the specified entity type, and entity collection under the new name becomes available.
* If you need to rename entity collection to a name of existing collection use
* the {@link #replaceCollection(String, String)} method instead.
*
* In case exception occurs the original collection (`entityType`) is guaranteed to be untouched,
* and the `newName` will not be present.
*
* @param entityType current name of the entity collection
* @param newName new name of the entity collection
* @return TRUE if collection was successfully renamed
* @throws EntityTypeAlreadyPresentInCatalogSchemaException when there is already entity collection with `newName`
* present
*/
boolean renameCollection(@Nonnull String entityType, @Nonnull String newName)
throws EntityTypeAlreadyPresentInCatalogSchemaException;
/**
* Replaces existing entity collection of particular with the contents of the another collection. When this method
* is successfully finished, the entity collection `entityTypeToBeReplaced` will be known under the name of the
* `entityTypeToBeReplacedWith` and the original contents of the `entityTypeToBeReplaced` will be purged entirely.
*
* In case exception occurs, both the original collection (`entityTypeToBeReplaced`) and replaced collection
* (`entityTypeToBeReplacedWith`) are guaranteed to be untouched.
*
* @param entityTypeToBeReplaced name of the collection that will be replaced and dropped
* @param entityTypeToBeReplacedWith name of the collection that will become the successor of the original catalog
* @return TRUE if collection was successfully replaced
*/
boolean replaceCollection(@Nonnull String entityTypeToBeReplaced, @Nonnull String entityTypeToBeReplacedWith);
/**
* Method returns count of all entities stored in the collection of passed entity type.
*
* @throws IllegalArgumentException when entity collection doesn't exist
*/
int getEntityCollectionSize(@Nonnull String entityType);
/**
* Creates entity builder for new entity without specified primary key needed to be inserted to the collection.
*
* @param entityType type of the entity that should be created
* @return builder instance to be filled up and stored via {@link #upsertEntity(Serializable)}
*/
@Nonnull
EntityBuilder createNewEntity(@Nonnull String entityType);
/**
* Creates entity builder for new entity without specified primary key needed to be inserted to the collection.
* The expected typ might any class that is annotated with {@link Entity} annotation. It may implement
* {@link InstanceEditor}, which allows you to call {@link InstanceEditor#upsertVia(EvitaSessionContract)} method
* and analyze the gathered mutations. Beware - the returned instance is always mutable and not thread safe!
*
* @param expectedType type of the entity that should be created annotated with {@link Entity}
* @return builder instance to be filled up and stored via {@link #upsertEntity(Serializable)}
*/
@Nonnull
S createNewEntity(@Nonnull Class expectedType);
/**
* Creates entity builder for new entity with externally defined primary key needed to be inserted to
* the collection.
*
* @param entityType type of the entity that should be created
* @param primaryKey externally assigned primary key for the entity
* @return builder instance to be filled up and stored via {@link #upsertEntity(Serializable)}
*/
@Nonnull
EntityBuilder createNewEntity(@Nonnull String entityType, int primaryKey);
/**
* Creates entity builder for new entity without specified primary key needed to be inserted to the collection.
* The expected typ might any class that is annotated with {@link Entity} annotation. It may implement
* {@link InstanceEditor}, which allows you to call {@link InstanceEditor#upsertVia(EvitaSessionContract)} method
* and analyze the gathered mutations. Beware - the returned instance is always mutable and not thread safe!
*
* @param expectedType type of the entity that should be created annotated with {@link Entity}
* @param primaryKey externally assigned primary key for the entity
* @return builder instance to be filled up and stored via {@link #upsertEntity(Serializable)}
*/
@Nonnull
S createNewEntity(@Nonnull Class expectedType, int primaryKey);
/**
* Shorthand method for {@link #upsertEntity(EntityMutation)} that accepts custom entity instance that can produce
* mutation. The entity instance must represent a proxied instance that can be cast to {@link InstanceEditor}
* under the cover (it may not explicitly implement this interface, but the proxying mechanism will do that).
*
* It's not possible to upsert non-proxied instances such as Java records or final or sealed classes.
*
* @param customEntity that contains changed entity state
*/
@Nonnull
EntityReference upsertEntity(@Nonnull S customEntity);
/**
* Shorthand method for {@link #upsertEntity(EntityMutation)} that accepts custom entity instance that can produce
* mutation. The entity instance must represent a proxied instance that can be cast to {@link InstanceEditor}
* under the cover (it may not explicitly implement this interface, but the proxying mechanism will do that).
*
* It's not possible to upsert non-proxied instances such as Java records or final or sealed classes.
*
* @param customEntity that contains changed entity state
*/
@Nonnull
List upsertEntityDeeply(@Nonnull S customEntity);
/**
* Method inserts to or updates entity in collection according to passed set of mutations.
*
* @param entityMutation list of mutation snippets that alter or form the entity
*/
@Nonnull
EntityReference upsertEntity(@Nonnull EntityMutation entityMutation);
/**
* Shorthand method for {@link #upsertEntity(EntityMutation)} that accepts {@link EntityBuilder} that can produce
* mutation.
*
* @param entityBuilder that contains changed entity state
* @return modified entity fetched according to `require` definition
*/
@Nonnull
SealedEntity upsertAndFetchEntity(@Nonnull EntityBuilder entityBuilder, EntityContentRequire... require);
/**
* Method inserts to or updates entity in collection according to passed set of mutations.
*
* @param entityMutation list of mutation snippets that alter or form the entity
* @return modified entity fetched according to `require` definition
*/
@Nonnull
SealedEntity upsertAndFetchEntity(@Nonnull EntityMutation entityMutation, EntityContentRequire... require);
/**
* Method removes existing entity in collection by its primary key. All entities of other entity types that reference
* removed entity in their {@link SealedEntity#getReference(String, int)} still keep the data untouched.
*
* @return true if entity existed and was removed
*/
boolean deleteEntity(@Nonnull String entityType, int primaryKey);
/**
* Method removes existing entity in collection by its primary key. All entities of other entity types that reference
* removed entity in their {@link SealedEntity#getReference(String, int)} still keep the data untouched.
*
* @return true if entity existed and was removed
* @throws EntityClassInvalidException when entity type cannot be extracted from the class
*/
boolean deleteEntity(@Nonnull Class> modelClass, int primaryKey) throws EntityClassInvalidException;
/**
* Method removes existing entity in collection by its primary key. All entities of other entity types that reference
* removed entity in their {@link SealedEntity#getReference(String, int)} still keep the data untouched.
*
* @return removed entity fetched according to `require` definition
*/
@Nonnull
Optional deleteEntity(@Nonnull String entityType, int primaryKey, EntityContentRequire... require);
/**
* Method removes existing entity in collection by its primary key. All entities of other entity types that reference
* removed entity in their {@link SealedEntity#getReference(String, int)} still keep the data untouched.
*
* @return removed entity fetched according to `require` definition
* @throws EntityClassInvalidException when entity type cannot be extracted from the class
*/
@Nonnull
Optional deleteEntity(@Nonnull Class modelClass, int primaryKey, EntityContentRequire... require)
throws EntityClassInvalidException;
/**
* Method removes existing hierarchical entity in collection by its primary key. Method also removes all entities
* of the same type that are transitively referencing the removed entity as its parent. All entities of other entity
* types that reference removed entities in their {@link SealedEntity#getReference(String, int)} still keep
* the data untouched.
*
* @return number of removed entities
* @throws EvitaInvalidUsageException when entity type has not hierarchy support enabled in schema
*/
int deleteEntityAndItsHierarchy(@Nonnull String entityType, int primaryKey)
throws EvitaInvalidUsageException;
/**
* Method removes existing hierarchical entity in collection by its primary key. Method also removes all entities
* of the same type that are transitively referencing the removed entity as its parent. All entities of other entity
* types that reference removed entities in their {@link SealedEntity#getReference(String, int)} still keep
* the data untouched.
*
* @return number of removed entities and the body of the deleted root entity
* @throws EvitaInvalidUsageException when entity type has not hierarchy support enabled in schema
*/
@Nonnull
DeletedHierarchy deleteEntityAndItsHierarchy(@Nonnull String entityType, int primaryKey, EntityContentRequire... require)
throws EvitaInvalidUsageException;
/**
* Method removes existing hierarchical entity in collection by its primary key. Method also removes all entities
* of the same type that are transitively referencing the removed entity as its parent. All entities of other entity
* types that reference removed entities in their {@link SealedEntity#getReference(String, int)} still keep
* the data untouched.
*
* @return number of removed entities and the body of the deleted root entity
* @throws EvitaInvalidUsageException when entity type has not hierarchy support enabled in schema
* @throws EntityClassInvalidException when entity type cannot be extracted from the class
*/
@Nonnull
DeletedHierarchy deleteEntityAndItsHierarchy(@Nonnull Class modelClass, int primaryKey, EntityContentRequire... require)
throws EvitaInvalidUsageException, EntityClassInvalidException;
/**
* Method removes all entities that match passed query. All entities of other entity types that reference removed
* entities in their {@link SealedEntity#getReference(String, int)} still keep the data untouched.
*
* Beware: you need to provide {@link Page} or {@link Strip} in the query to control the maximum number of removed
* entities. Otherwise, the default value of maximum of `20` entities to remove will be used.
*
* @return number of deleted entities
*/
int deleteEntities(@Nonnull Query query);
/**
* Method removes all entities that match passed query. All entities of other entity types that reference removed
* entities in their {@link SealedEntity#getReference(String, int)} still keep the data untouched. This variant of
* the delete by query method allows returning partial of full bodies of the removed entities.
*
* Beware: you need to provide {@link Page} or {@link Strip} in the query to control the maximum number of removed
* entities. Otherwise, the default value of maximum of `20` entities to remove will be used.
*
* @return bodies of deleted entities according to {@link Query#getRequire() requirements}
*/
@Nonnull
SealedEntity[] deleteSealedEntitiesAndReturnBodies(@Nonnull Query query);
/**
* Creates a backup of the specified catalog and returns an InputStream to read the binary data of the zip file.
*
* @param pastMoment leave null for creating backup for actual dataset, or specify past moment to create backup for
* the dataset as it was at that moment
* @param includingWAL if true, the backup will include the Write-Ahead Log (WAL) file and when the catalog is
* restored, it'll replay the WAL contents locally to bring the catalog to the current state
* @return jobId of the backup process
* @throws TemporalDataNotAvailableException when the past data is not available
*/
@Nonnull
Task, FileForFetch> backupCatalog(@Nullable OffsetDateTime pastMoment, boolean includingWAL) throws TemporalDataNotAvailableException;
/**
* Default implementation uses ID for comparing two sessions (and to distinguish one session from another).
*
* @return 0 if both sessions are the same
*/
default int compareTo(@Nonnull EvitaSessionContract otherSession) {
return getId().compareTo(otherSession.getId());
}
/**
* Returns {@link TransactionContract#getTransactionId()} of the currently opened transaction in this session. Returns empty
* value if no transaction is present.
*/
@Nonnull
Optional getOpenedTransactionId();
/**
* Returns TRUE if transaction is currently open in this session.
*/
default boolean isTransactionOpen() {
return getOpenedTransactionId().isPresent();
}
/**
* Returns true if currently opened transaction has rollback flag set on.
*/
boolean isRollbackOnly();
/**
* Method marks current transaction to be rolled back on {@link #close()}.
* Changes made in transaction will never make it to the database.
*/
void setRollbackOnly();
/**
* Method returns true if the session is in read-only mode. That means no transaction is opened and no data will
* be modified within this session. Read-only sessions allow more aggressive optimizations, such as using cached
* results.
*
* @see io.evitadb.api.SessionTraits.SessionFlags#READ_WRITE
*/
boolean isReadOnly();
/**
* Returns true if session is switched to binary format output.
*
* @see io.evitadb.api.SessionTraits.SessionFlags#BINARY
* @see io.evitadb.api.requestResponse.EvitaBinaryEntityResponse
*/
boolean isBinaryFormat();
/**
* Returns true if session has dry run flag that means that no updates will be committed whatsoever.
*
* @see io.evitadb.api.SessionTraits.SessionFlags#DRY_RUN
*/
boolean isDryRun();
/**
* Returns period in seconds this session has been inactive (no call occurred on it).
*/
long getInactivityDurationInSeconds();
/**
* Returns implementation of the proxy factory that is used to wrap the returned {@link SealedEntity} into a custom
* Java types.
*/
@Nonnull
ProxyFactory getProxyFactory();
}