io.evitadb.api.requestResponse.data.structure.InitialEntityBuilder 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.
The newest version!
/*
*
* _ _ ____ ____
* _____ _(_) |_ __ _| _ \| __ )
* / _ \ \ / / | __/ _` | | | | _ \
* | __/\ 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.requestResponse.data.structure;
import io.evitadb.api.exception.ReferenceNotKnownException;
import io.evitadb.api.requestResponse.data.AssociatedDataContract;
import io.evitadb.api.requestResponse.data.AttributesContract;
import io.evitadb.api.requestResponse.data.EntityClassifierWithParent;
import io.evitadb.api.requestResponse.data.EntityEditor.EntityBuilder;
import io.evitadb.api.requestResponse.data.PriceContract;
import io.evitadb.api.requestResponse.data.PriceInnerRecordHandling;
import io.evitadb.api.requestResponse.data.PricesContract;
import io.evitadb.api.requestResponse.data.ReferenceContract;
import io.evitadb.api.requestResponse.data.ReferenceEditor.ReferenceBuilder;
import io.evitadb.api.requestResponse.data.Versioned;
import io.evitadb.api.requestResponse.data.mutation.EntityMutation;
import io.evitadb.api.requestResponse.data.mutation.EntityMutation.EntityExistence;
import io.evitadb.api.requestResponse.data.mutation.EntityUpsertMutation;
import io.evitadb.api.requestResponse.data.mutation.associatedData.AssociatedDataMutation;
import io.evitadb.api.requestResponse.data.mutation.associatedData.UpsertAssociatedDataMutation;
import io.evitadb.api.requestResponse.data.mutation.attribute.AttributeMutation;
import io.evitadb.api.requestResponse.data.mutation.attribute.UpsertAttributeMutation;
import io.evitadb.api.requestResponse.data.mutation.parent.SetParentMutation;
import io.evitadb.api.requestResponse.data.mutation.price.SetPriceInnerRecordHandlingMutation;
import io.evitadb.api.requestResponse.data.mutation.price.UpsertPriceMutation;
import io.evitadb.api.requestResponse.data.mutation.reference.InsertReferenceMutation;
import io.evitadb.api.requestResponse.data.mutation.reference.ReferenceAttributeMutation;
import io.evitadb.api.requestResponse.data.mutation.reference.ReferenceKey;
import io.evitadb.api.requestResponse.data.mutation.reference.SetReferenceGroupMutation;
import io.evitadb.api.requestResponse.schema.Cardinality;
import io.evitadb.api.requestResponse.schema.EntitySchemaContract;
import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract;
import io.evitadb.api.requestResponse.schema.dto.EntitySchema;
import io.evitadb.dataType.DateTimeRange;
import lombok.experimental.Delegate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Currency;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Optional.of;
import static java.util.Optional.ofNullable;
/**
* Builder that is used to create the entity.
* Due to performance reasons (see {@link DirectWriteOrOperationLog} microbenchmark) there is special implementation
* for the situation when entity is newly created. In this case we know everything is new and we don't need to closely
* monitor the changes so this can speed things up.
*
* @author Jan Novotný ([email protected]), FG Forrest a.s. (c) 2021
*/
public class InitialEntityBuilder implements EntityBuilder {
@Serial private static final long serialVersionUID = -3674623071115207036L;
private final String type;
private final EntitySchemaContract schema;
private final Integer primaryKey;
@Delegate(types = AttributesContract.class)
private final InitialEntityAttributesBuilder attributesBuilder;
@Delegate(types = AssociatedDataContract.class)
private final AssociatedDataBuilder associatedDataBuilder;
@Delegate(types = PricesContract.class, excludes = Versioned.class)
private final PricesBuilder pricesBuilder;
private final Map references;
private Integer parent;
public InitialEntityBuilder(@Nonnull String type) {
this.type = type;
this.schema = EntitySchema._internalBuild(type);
this.primaryKey = null;
this.attributesBuilder = new InitialEntityAttributesBuilder(schema);
this.associatedDataBuilder = new InitialAssociatedDataBuilder(schema);
this.pricesBuilder = new InitialPricesBuilder(schema);
this.references = new LinkedHashMap<>();
}
public InitialEntityBuilder(@Nonnull EntitySchemaContract schema) {
this.type = schema.getName();
this.schema = schema;
this.primaryKey = null;
this.attributesBuilder = new InitialEntityAttributesBuilder(schema);
this.associatedDataBuilder = new InitialAssociatedDataBuilder(schema);
this.pricesBuilder = new InitialPricesBuilder(schema);
this.references = new LinkedHashMap<>();
}
public InitialEntityBuilder(@Nonnull String type, @Nullable Integer primaryKey) {
this.type = type;
this.primaryKey = primaryKey;
this.schema = EntitySchema._internalBuild(type);
this.attributesBuilder = new InitialEntityAttributesBuilder(schema);
this.associatedDataBuilder = new InitialAssociatedDataBuilder(schema);
this.pricesBuilder = new InitialPricesBuilder(schema);
this.references = new LinkedHashMap<>();
}
public InitialEntityBuilder(@Nonnull EntitySchemaContract schema, @Nullable Integer primaryKey) {
this.type = schema.getName();
this.schema = schema;
this.primaryKey = primaryKey;
this.attributesBuilder = new InitialEntityAttributesBuilder(schema);
this.associatedDataBuilder = new InitialAssociatedDataBuilder(schema);
this.pricesBuilder = new InitialPricesBuilder(schema);
this.references = new LinkedHashMap<>();
}
public InitialEntityBuilder(
@Nonnull EntitySchemaContract entitySchema,
@Nullable Integer primaryKey,
@Nonnull Collection attributeValues,
@Nonnull Collection associatedDataValues,
@Nonnull Collection referenceContracts,
@Nullable PriceInnerRecordHandling priceInnerRecordHandling,
@Nonnull Collection prices
) {
this.type = entitySchema.getName();
this.schema = entitySchema;
this.primaryKey = primaryKey;
this.attributesBuilder = new InitialEntityAttributesBuilder(schema);
for (AttributeValue attributeValue : attributeValues) {
final AttributeKey attributeKey = attributeValue.key();
if (attributeKey.localized()) {
this.attributesBuilder.setAttribute(
attributeKey.attributeName(),
attributeKey.locale(),
attributeValue.value()
);
} else {
this.attributesBuilder.setAttribute(
attributeKey.attributeName(),
attributeValue.value()
);
}
}
this.associatedDataBuilder = new InitialAssociatedDataBuilder(schema);
for (AssociatedDataValue associatedDataValue : associatedDataValues) {
final AssociatedDataKey associatedDataKey = associatedDataValue.key();
if (associatedDataKey.localized()) {
this.associatedDataBuilder.setAssociatedData(
associatedDataKey.associatedDataName(),
associatedDataKey.locale(),
associatedDataValue.value()
);
} else {
this.associatedDataBuilder.setAssociatedData(
associatedDataKey.associatedDataName(),
associatedDataValue.value()
);
}
}
this.pricesBuilder = new InitialPricesBuilder(schema);
ofNullable(priceInnerRecordHandling)
.ifPresent(this.pricesBuilder::setPriceInnerRecordHandling);
for (PriceContract price : prices) {
this.pricesBuilder.setPrice(
price.priceId(),
price.priceList(),
price.currency(),
price.innerRecordId(),
price.priceWithoutTax(),
price.taxRate(),
price.priceWithTax(),
price.validity(),
price.indexed()
);
}
this.references = referenceContracts.stream()
.collect(
Collectors.toMap(
ReferenceContract::getReferenceKey,
Function.identity(),
(o, o2) -> {
throw new IllegalStateException("Duplicate key " + o);
},
LinkedHashMap::new
)
);
}
@Override
public boolean dropped() {
return false;
}
@Override
public int version() {
return 1;
}
@Override
@Nonnull
public String getType() {
return type;
}
@Override
@Nonnull
public EntitySchemaContract getSchema() {
return schema;
}
@Override
@Nullable
public Integer getPrimaryKey() {
return primaryKey;
}
@Override
public boolean parentAvailable() {
return true;
}
@Nonnull
@Override
public Optional getParentEntity() {
return ofNullable(parent)
.map(it -> new EntityReferenceWithParent(type, it, null));
}
@Override
public boolean referencesAvailable() {
return true;
}
@Override
public boolean referencesAvailable(@Nonnull String referenceName) {
return true;
}
@Nonnull
@Override
public Collection getReferences() {
return references.values();
}
@Nonnull
@Override
public Collection getReferences(@Nonnull String referenceName) {
return references
.values()
.stream()
.filter(it -> Objects.equals(referenceName, it.getReferenceName()))
.collect(Collectors.toList());
}
@Nonnull
@Override
public Optional getReference(@Nonnull String referenceName, int referencedEntityId) {
return ofNullable(references.get(new ReferenceKey(referenceName, referencedEntityId)));
}
@Nonnull
public Set getAllLocales() {
return Stream.concat(
attributesBuilder.getAttributeLocales().stream(),
associatedDataBuilder.getAssociatedDataLocales().stream()
)
.collect(Collectors.toSet());
}
@Nonnull
public Set getLocales() {
return getAllLocales();
}
@Nonnull
@Override
public EntityBuilder removeAttribute(@Nonnull String attributeName) {
attributesBuilder.removeAttribute(attributeName);
return this;
}
@Nonnull
@Override
public EntityBuilder setAttribute(@Nonnull String attributeName, @Nullable T attributeValue) {
attributesBuilder.setAttribute(attributeName, attributeValue);
return this;
}
@Nonnull
@Override
public EntityBuilder setAttribute(@Nonnull String attributeName, @Nullable T[] attributeValue) {
attributesBuilder.setAttribute(attributeName, attributeValue);
return this;
}
@Nonnull
@Override
public EntityBuilder removeAttribute(@Nonnull String attributeName, @Nonnull Locale locale) {
attributesBuilder.removeAttribute(attributeName, locale);
return this;
}
@Nonnull
@Override
public EntityBuilder setAttribute(@Nonnull String attributeName, @Nonnull Locale locale, @Nullable T attributeValue) {
attributesBuilder.setAttribute(attributeName, locale, attributeValue);
return this;
}
@Nonnull
@Override
public EntityBuilder setAttribute(@Nonnull String attributeName, @Nonnull Locale locale, @Nullable T[] attributeValue) {
attributesBuilder.setAttribute(attributeName, locale, attributeValue);
return this;
}
@Nonnull
@Override
public EntityBuilder mutateAttribute(@Nonnull AttributeMutation mutation) {
attributesBuilder.mutateAttribute(mutation);
return this;
}
@Nonnull
@Override
public EntityBuilder removeAssociatedData(@Nonnull String associatedDataName) {
associatedDataBuilder.removeAssociatedData(associatedDataName);
return this;
}
@Nonnull
@Override
public EntityBuilder setAssociatedData(@Nonnull String associatedDataName, @Nullable T associatedDataValue) {
associatedDataBuilder.setAssociatedData(associatedDataName, associatedDataValue);
return this;
}
@Nonnull
@Override
public EntityBuilder setAssociatedData(@Nonnull String associatedDataName, @Nonnull T[] associatedDataValue) {
associatedDataBuilder.setAssociatedData(associatedDataName, associatedDataValue);
return this;
}
@Nonnull
@Override
public EntityBuilder removeAssociatedData(@Nonnull String associatedDataName, @Nonnull Locale locale) {
associatedDataBuilder.removeAssociatedData(associatedDataName, locale);
return this;
}
@Nonnull
@Override
public EntityBuilder setAssociatedData(@Nonnull String associatedDataName, @Nonnull Locale locale, @Nullable T associatedDataValue) {
associatedDataBuilder.setAssociatedData(associatedDataName, locale, associatedDataValue);
return this;
}
@Nonnull
@Override
public EntityBuilder setAssociatedData(@Nonnull String associatedDataName, @Nonnull Locale locale, @Nullable T[] associatedDataValue) {
associatedDataBuilder.setAssociatedData(associatedDataName, locale, associatedDataValue);
return this;
}
@Nonnull
@Override
public EntityBuilder mutateAssociatedData(@Nonnull AssociatedDataMutation mutation) {
associatedDataBuilder.mutateAssociatedData(mutation);
return this;
}
@Override
public EntityBuilder setParent(int parentPrimaryKey) {
this.parent = parentPrimaryKey;
return this;
}
@Override
public EntityBuilder removeParent() {
this.parent = null;
return this;
}
@Override
public EntityBuilder setReference(@Nonnull String referenceName, int referencedPrimaryKey) {
final ReferenceSchemaContract referenceSchema = getReferenceSchemaOrThrowException(referenceName);
return setReference(referenceName, referenceSchema.getReferencedEntityType(), referenceSchema.getCardinality(), referencedPrimaryKey, null);
}
@Override
public EntityBuilder setReference(@Nonnull String referenceName, int referencedPrimaryKey, @Nullable Consumer whichIs) {
final ReferenceSchemaContract referenceSchema = getReferenceSchemaOrThrowException(referenceName);
return setReference(referenceName, referenceSchema.getReferencedEntityType(), referenceSchema.getCardinality(), referencedPrimaryKey, whichIs);
}
@Override
public EntityBuilder setReference(@Nonnull String referenceName, @Nonnull String referencedEntityType, @Nonnull Cardinality cardinality, int referencedPrimaryKey) {
return setReference(referenceName, referencedEntityType, cardinality, referencedPrimaryKey, null);
}
@Override
public EntityBuilder setReference(@Nonnull String referenceName, @Nonnull String referencedEntityType, @Nonnull Cardinality cardinality, int referencedPrimaryKey, @Nullable Consumer whichIs) {
final InitialReferenceBuilder builder = new InitialReferenceBuilder(this.schema, referenceName, referencedPrimaryKey, cardinality, referencedEntityType);
ofNullable(whichIs).ifPresent(it -> it.accept(builder));
final Reference reference = builder.build();
references.put(new ReferenceKey(referenceName, referencedPrimaryKey), reference);
return this;
}
@Override
public void addOrReplaceReferenceMutations(@Nonnull ReferenceBuilder referenceBuilder) {
references.put(referenceBuilder.getReferenceKey(), referenceBuilder.build());
}
@Override
public EntityBuilder removeReference(@Nonnull String referenceName, int referencedPrimaryKey) {
this.references.remove(new ReferenceKey(referenceName, referencedPrimaryKey));
return this;
}
@Override
public EntityBuilder setPrice(int priceId, @Nonnull String priceList, @Nonnull Currency currency, @Nonnull BigDecimal priceWithoutTax, @Nonnull BigDecimal taxRate, @Nonnull BigDecimal priceWithTax, boolean indexed) {
pricesBuilder.setPrice(priceId, priceList, currency, priceWithoutTax, taxRate, priceWithTax, indexed);
return this;
}
@Override
public EntityBuilder setPrice(int priceId, @Nonnull String priceList, @Nonnull Currency currency, @Nullable Integer innerRecordId, @Nonnull BigDecimal priceWithoutTax, @Nonnull BigDecimal taxRate, @Nonnull BigDecimal priceWithTax, boolean indexed) {
pricesBuilder.setPrice(priceId, priceList, currency, innerRecordId, priceWithoutTax, taxRate, priceWithTax, indexed);
return this;
}
@Override
public EntityBuilder setPrice(int priceId, @Nonnull String priceList, @Nonnull Currency currency, @Nonnull BigDecimal priceWithoutTax, @Nonnull BigDecimal taxRate, @Nonnull BigDecimal priceWithTax, DateTimeRange validity, boolean indexed) {
pricesBuilder.setPrice(priceId, priceList, currency, priceWithoutTax, taxRate, priceWithTax, validity, indexed);
return this;
}
@Override
public EntityBuilder setPrice(int priceId, @Nonnull String priceList, @Nonnull Currency currency, @Nullable Integer innerRecordId, @Nonnull BigDecimal priceWithoutTax, @Nonnull BigDecimal taxRate, @Nonnull BigDecimal priceWithTax, @Nullable DateTimeRange validity, boolean indexed) {
pricesBuilder.setPrice(priceId, priceList, currency, innerRecordId, priceWithoutTax, taxRate, priceWithTax, validity, indexed);
return this;
}
@Override
public EntityBuilder removePrice(int priceId, @Nonnull String priceList, @Nonnull Currency currency) {
pricesBuilder.removePrice(priceId, priceList, currency);
return this;
}
@Override
public EntityBuilder setPriceInnerRecordHandling(@Nonnull PriceInnerRecordHandling priceInnerRecordHandling) {
pricesBuilder.setPriceInnerRecordHandling(priceInnerRecordHandling);
return this;
}
@Override
public EntityBuilder removePriceInnerRecordHandling() {
pricesBuilder.removePriceInnerRecordHandling();
return this;
}
@Override
public EntityBuilder removeAllNonTouchedPrices() {
pricesBuilder.removeAllNonTouchedPrices();
return this;
}
@Nonnull
@Override
public Optional toMutation() {
return of(
new EntityUpsertMutation(
getType(),
getPrimaryKey(),
EntityExistence.MUST_NOT_EXIST,
Stream.of(
ofNullable(parent)
.map(SetParentMutation::new)
.stream(),
references.values()
.stream()
.flatMap(it -> Stream.concat(
Stream.of(
new InsertReferenceMutation(it.getReferenceKey(), it.getReferenceCardinality(), it.getReferencedEntityType()),
it.getGroup().map(group -> new SetReferenceGroupMutation(it.getReferenceKey(), group.getType(), group.getPrimaryKey())).orElse(null)
)
.filter(Objects::nonNull),
it.getAttributeValues()
.stream()
.filter(x -> Objects.nonNull(x.value()))
.map(x ->
new ReferenceAttributeMutation(
it.getReferenceKey(),
new UpsertAttributeMutation(x.key(), x.value())
)
)
)
),
attributesBuilder
.getAttributeValues()
.stream()
.filter(it -> it.value() != null)
.map(it -> new UpsertAttributeMutation(it.key(), it.value())),
associatedDataBuilder
.getAssociatedDataValues()
.stream()
.map(it -> new UpsertAssociatedDataMutation(it.key(), it.value())),
Stream.of(
new SetPriceInnerRecordHandlingMutation(pricesBuilder.getPriceInnerRecordHandling())
),
pricesBuilder
.getPrices()
.stream()
.map(it -> new UpsertPriceMutation(it.priceKey(), it))
)
.flatMap(it -> it)
.filter(Objects::nonNull)
.collect(Collectors.toList())
)
);
}
@Nonnull
@Override
public Entity toInstance() {
return Entity._internalBuild(
primaryKey,
version(),
schema,
parent,
references.values(),
attributesBuilder.build(),
associatedDataBuilder.build(),
pricesBuilder.build(),
getAllLocales(),
Stream.concat(
schema.getReferences().keySet().stream(),
references.keySet()
.stream()
.map(ReferenceKey::referenceName)
).collect(Collectors.toSet()),
schema.isWithHierarchy() || parent != null,
false
);
}
@Nonnull
private ReferenceSchemaContract getReferenceSchemaOrThrowException(@Nonnull String referenceName) {
return getSchema()
.getReference(referenceName)
.orElseThrow(() -> new ReferenceNotKnownException(referenceName));
}
}