All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.ydb.yoj.repository.db.projection.ProjectionMappings Maven / Gradle / Ivy

Go to download

Core YOJ (YDB ORM for Java) abstractions and APIs for domain entities, repositories, transactions etc.

There is a newer version: 2.6.1
Show newest version
package tech.ydb.yoj.repository.db.projection;

import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import tech.ydb.yoj.databind.schema.Schema;
import tech.ydb.yoj.repository.db.Entity;
import tech.ydb.yoj.repository.db.EntityIdSchema;
import tech.ydb.yoj.repository.db.EntitySchema;
import tech.ydb.yoj.repository.db.list.ListRequest;
import tech.ydb.yoj.repository.db.list.ListResult;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.UnaryOperator;

import static java.util.stream.Collectors.toList;
import static lombok.AccessLevel.PRIVATE;

public final class ProjectionMappings {
    private ProjectionMappings() {
    }

    /**
     * Creates a lenient one-to-one mapping from entity fields to projection fields, which contains all
     * {@link #strictFieldMapping(Class, Class) strict mappings}, plus mappings of the form
     * {@code  <-> } (if both fields exist).
     *
     * @param projectionType projection class
     * @param entityType     entity class
     * @param 

projection type * @param entity type * @return Map: Entity field path -> Projection field path * @see #strictFieldMapping(Class, Class) */ @NonNull public static

, T extends Entity> Map lenientFieldMapping( @NonNull Class

projectionType, @NonNull Class entityType) { EntitySchema entitySchema = EntitySchema.of(entityType); Class entityIdType = entitySchema.getIdSchema().getType(); BiMap mapping = HashBiMap.create(strictFieldMapping(projectionType, entityType)); EntitySchema.of(projectionType).flattenFields() .stream() .filter(pf -> !isEntityId(pf, entityIdType) && !mapping.inverse().containsKey(pf.getPath())) .forEach(pf -> findMatchingNonIdField(pf, entitySchema) .ifPresent(ef -> mapping.put(ef.getPath(), pf.getPath())) ); return mapping; } /** * Creates a one-to-one mapping from entity fields to entity projection ID fields, assuming that the projection ID * contains fields with the same name as in the main entity and at most one field for the main entity ID * (with any name). * *

* E.g., the following entity-projection pair qualifies:

     * @Value class MyEntity implements Entity<MyEntity> {
     *     Id id;
     *     String field;
     *
     *     @Value static class Id implements Entity.Id<MyEntity> { String value; }
     * }
     *
     * @Value class MyIndex implements Entity<MyIndex> {
     *     Id id;
     *
     *     @Value
     *     static class Id implements Entity.Id<MyIndex> {
     *         // MUST have the same Java field name as in the entity class
     *         // (DB name specified in @Column annotation does not matter.)
     *         String field;
     *
     *         // OPTIONAL. If present, this field MAY have any name
     *         MyEntity.Id entityId;
     *     }
     * }
     * 
* * @param projectionType projection class * @param entityType entity class * @param

projection type * @param entity type * @return Bidirectional mapping: Entity field path -> Projection field path */ @NonNull public static

, T extends Entity> Map strictFieldMapping( @NonNull Class

projectionType, @NonNull Class entityType) { EntitySchema entitySchema = EntitySchema.of(entityType); Class entityIdType = entitySchema.getIdSchema().getType(); List projectionIdFields = EntityIdSchema.ofEntity(projectionType).getFields(); Map mapping = new HashMap<>(); projectionIdFields .stream() .filter(f -> !isEntityId(f, entityIdType)) .flatMap(Schema.JavaField::flatten) .forEach(f -> mapping.put(getMatchingNonIdField(f, entitySchema).getPath(), f.getPath())); List idTypeFields = projectionIdFields.stream() .filter(f -> isEntityId(f, entityIdType)) .collect(toList()); if (idTypeFields.size() > 1) { throw new IllegalStateException("Projection ID cannot have more than 1 field with type: " + entityIdType); } if (idTypeFields.size() == 0) { return mapping; } idTypeFields.get(0).flatten().forEach(idPart -> mapping.put(getMatchingIdField(idPart, entitySchema).getPath(), idPart.getPath())); return mapping; } private static boolean isEntityId(@NonNull Schema.JavaField field, @NonNull Class entityIdType) { return field.getType().equals(entityIdType); } @NonNull private static Optional findMatchingNonIdField(@NonNull Schema.JavaField projectionField, @NonNull EntitySchema realEntity) { // Entity. <-> Projection.id. return realEntity.findField(projectionField.getRawPath()); } @NonNull private static Schema.JavaField getMatchingNonIdField(@NonNull Schema.JavaField projectionField, @NonNull EntitySchema realEntity) { // Entity. <-> Projection.id. return realEntity.getField(projectionField.getRawPath()); } @NonNull private static Schema.JavaField getMatchingIdField(@NonNull Schema.JavaField projectionIdField, @NonNull EntitySchema realEntity) { // Entity.id[.] <-> Projection.id.[.] return realEntity.getIdSchema().getField(projectionIdField.getRawSubPath(1)); } @NonNull public static

> ListViaProjection

listViaProjection(@NonNull Class

projectionType) { return new ListViaProjection<>(projectionType); } @RequiredArgsConstructor(access = PRIVATE) public static final class ListViaProjection

> { private final Class

projectionType; @NonNull public > Listing entities(@NonNull ListRequest request) { return entities(request, lenientFieldMapping(projectionType, request.getSchema().getType())); } @NonNull public > Listing entities(@NonNull ListRequest request, @NonNull Map fieldMapping) { return entities(request, f -> getFieldMapping(fieldMapping, f)); } @NonNull private String getFieldMapping(@NonNull Map fieldMapping, String f) { String mapping = fieldMapping.get(f); Preconditions.checkState(mapping != null, "No mapping for entity field \"%s\" in projection %s", f, projectionType); return mapping; } @NonNull public > Listing entities(@NonNull ListRequest request, @NonNull UnaryOperator fieldMapping) { return new Listing<>(request, request.forEntity(projectionType, fieldMapping)); } @RequiredArgsConstructor(access = PRIVATE) public static final class Listing, P extends Entity

> { private final ListRequest request; private final ListRequest

projRequest; @NonNull public TransformedListing transforming(@NonNull Function unproject) { return new TransformedListing<>(this, unproject); } @NonNull public ListResult

run(@NonNull Function, ListResult

> listFunc) { return listFunc.apply(projRequest); } } @RequiredArgsConstructor(access = PRIVATE) public static final class TransformedListing, P extends Entity

> { private final Listing listing; private final Function unproject; @NonNull public ListResult run(@NonNull Function, ListResult

> listFunc) { ListResult

projResult = listing.run(listFunc); return ListResult.builder(listing.request) .entries(projResult.stream().map(unproject).collect(toList())) .lastPage(projResult.isLastPage()) .build(); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy