From 6610aab60e733a761d8ef6260de8793578ce1e76 Mon Sep 17 00:00:00 2001 From: Nikolai Amelichev Date: Mon, 4 Aug 2025 23:03:01 +0200 Subject: [PATCH 1/3] #157: Deprecate reflection-heavy `EntityIdSchema.SORT_ENTITY_BY_ID` in favor of `EntitySchema.defaultOrder()` Comparator --- .../test/inmemory/InMemoryTable.java | 10 ++-- .../yoj/repository/ydb/table/YdbTable.java | 2 +- .../ydb/yoj/repository/db/EntityIdSchema.java | 25 ++++++++- .../ydb/yoj/repository/db/EntitySchema.java | 9 ++++ .../ydb/yoj/repository/db/TableQueryImpl.java | 52 +++++++++++++++++-- .../repository/db/list/InMemoryQueries.java | 21 ++++++-- 6 files changed, 105 insertions(+), 14 deletions(-) diff --git a/repository-inmemory/src/main/java/tech/ydb/yoj/repository/test/inmemory/InMemoryTable.java b/repository-inmemory/src/main/java/tech/ydb/yoj/repository/test/inmemory/InMemoryTable.java index ffe9b090..d1d2b20f 100644 --- a/repository-inmemory/src/main/java/tech/ydb/yoj/repository/test/inmemory/InMemoryTable.java +++ b/repository-inmemory/src/main/java/tech/ydb/yoj/repository/test/inmemory/InMemoryTable.java @@ -110,7 +110,7 @@ public List find( @Nullable Long offset ) { // NOTE: InMemoryTable doesn't handle index. - return InMemoryQueries.find(() -> findAll().stream(), filter, orderBy, limit, offset); + return TableQueryImpl.find(() -> findAll().stream(), schema, filter, orderBy, limit, offset); } @Override @@ -192,7 +192,7 @@ public T find(Entity.Id id) { @Override public > List find(Set ids) { - return TableQueryImpl.find(this, getFirstLevelCache(), ids); + return TableQueryImpl.find(this, schema, getFirstLevelCache(), ids); } @Override @@ -218,7 +218,7 @@ public > List find(Range range) { transaction.getWatcher().markRangeRead(tableDescriptor, range); return findAll0().stream() .filter(e -> range.contains((ID) e.getId())) - .sorted(EntityIdSchema.SORT_ENTITY_BY_ID) + .sorted(schema.defaultOrder()) .collect(toList()); } @@ -239,7 +239,7 @@ public > List find(Class viewType, @Override public > List find(Class viewType, Set ids) { - return find(viewType, ids, null, EntityExpressions.defaultOrder(getType()), null); + return find(viewType, ids, null, EntityExpressions.defaultOrder(schema), null); } @Override @@ -542,7 +542,7 @@ private > Stream readTableStream(ReadTableParams .stream() .filter(e -> readTableFilter(e, params)); if (params.isOrdered()) { - stream = stream.sorted(EntityIdSchema.SORT_ENTITY_BY_ID); + stream = stream.sorted(schema.defaultOrder()); } if (params.getRowLimit() > 0) { stream = stream.limit(params.getRowLimit()); diff --git a/repository-ydb-v2/src/main/java/tech/ydb/yoj/repository/ydb/table/YdbTable.java b/repository-ydb-v2/src/main/java/tech/ydb/yoj/repository/ydb/table/YdbTable.java index 120dfbf4..9ca9ccd6 100644 --- a/repository-ydb-v2/src/main/java/tech/ydb/yoj/repository/ydb/table/YdbTable.java +++ b/repository-ydb-v2/src/main/java/tech/ydb/yoj/repository/ydb/table/YdbTable.java @@ -273,7 +273,7 @@ public T find(Entity.Id id) { @Override public > List find(Set ids) { - return TableQueryImpl.find(this, getFirstLevelCache(), ids); + return TableQueryImpl.find(this, schema, getFirstLevelCache(), ids); } @Override diff --git a/repository/src/main/java/tech/ydb/yoj/repository/db/EntityIdSchema.java b/repository/src/main/java/tech/ydb/yoj/repository/db/EntityIdSchema.java index 32a7e217..d83f379c 100644 --- a/repository/src/main/java/tech/ydb/yoj/repository/db/EntityIdSchema.java +++ b/repository/src/main/java/tech/ydb/yoj/repository/db/EntityIdSchema.java @@ -3,6 +3,7 @@ import com.google.common.base.Preconditions; import com.google.common.reflect.TypeToken; import lombok.NonNull; +import tech.ydb.yoj.DeprecationWarnings; import tech.ydb.yoj.databind.CustomValueTypes; import tech.ydb.yoj.databind.FieldValueType; import tech.ydb.yoj.databind.schema.Schema; @@ -42,11 +43,33 @@ public final class EntityIdSchema> extends Schema im private static final String ID_SUBFIELD_PATH_PREFIX = ID_FIELD_NAME + PATH_DELIMITER; private static final String ID_SUBFIELD_NAME_PREFIX = ID_FIELD_NAME + NAME_DELIMITER; + /** + * @deprecated This constant will be removed in YOJ 2.7.0. + *

If you need a {@link Comparator} to manually sort entities by ID ascending, + * use {@link EntitySchema#defaultOrder()} if you have an entity schema, + * or {@code Comparator.comparing(Entity::getId, idSchema)} if you have an entity ID schema. + *

To obtain entity schema or entity ID schema, see {@code EntitySchema.of()}, + * {@link EntitySchema#getIdSchema()} and {@code EntityIdSchema.{of, from, ofEntity}()}. + */ + @Deprecated(forRemoval = true) public static final Comparator> SORT_ENTITY_BY_ID = Comparator.comparing( - Entity::getId, (a, b) -> EntityIdSchema.ofEntity(a.getType()).compare(a, b) + Entity::getId, (a, b) -> { + DeprecationWarnings.warnOnce("EntityIdSchema[" + a.getClass().getName() + "].SORT_ENTITY_BY_ID", + "EntityIdSchema.SORT_ENTITY_BY_ID constant will be removed in YOJ 2.7.0. " + + "Please use EntitySchema.defaultOrder() if you have an entity schema, or " + + "Comparator.comparing(Entity::getId, idSchema) if you have an entity ID schema."); + return EntityIdSchema.ofEntity(a.getType()).compare(a, b); + } ); + /** + * @deprecated This method serves no useful purpose and will be removed in YOJ 2.7.0. + *

If you need a {@link Comparator} for entity IDs, use the {@code EntityIdSchema} instance as it already implements {@code Comparator}. + */ + @Deprecated(forRemoval = true) public static > Comparator> getIdComparator(Class type) { + DeprecationWarnings.warnOnce("EntityIdSchema.getIdComparator(" + type.getName() + ")", + "EntityIdSchema.getIdComparator(Class) method will be removed in YOJ 2.7.0. Please use the EntityIdSchema itself as the comparator"); return Comparator.comparing( id -> id, (a, b) -> EntityIdSchema.ofEntity(type).compare(a, b) ); diff --git a/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java b/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java index 77034fe3..1e35c82f 100644 --- a/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java +++ b/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java @@ -10,6 +10,7 @@ import tech.ydb.yoj.databind.schema.reflect.Reflector; import java.lang.reflect.Type; +import java.util.Comparator; import java.util.List; import java.util.Map; @@ -117,4 +118,12 @@ public Map flattenId(Entity.Id idValue) { public ViewSchema getViewSchema(Class viewClass) { return ViewSchema.of(getRegistry(), viewClass, getNamingStrategy()); } + + /** + * @return a comparator for sorting entities by ID ascending + * @since 2.6.24 + */ + public Comparator defaultOrder() { + return Comparator.comparing(Entity::getId, getIdSchema()); + } } diff --git a/repository/src/main/java/tech/ydb/yoj/repository/db/TableQueryImpl.java b/repository/src/main/java/tech/ydb/yoj/repository/db/TableQueryImpl.java index 4f3ee950..5cbd162a 100644 --- a/repository/src/main/java/tech/ydb/yoj/repository/db/TableQueryImpl.java +++ b/repository/src/main/java/tech/ydb/yoj/repository/db/TableQueryImpl.java @@ -1,10 +1,16 @@ package tech.ydb.yoj.repository.db; import com.google.common.collect.Sets; +import lombok.NonNull; import tech.ydb.yoj.InternalApi; +import tech.ydb.yoj.databind.expression.FilterExpression; +import tech.ydb.yoj.databind.expression.OrderExpression; import tech.ydb.yoj.repository.db.cache.FirstLevelCache; +import tech.ydb.yoj.repository.db.list.InMemoryQueries; import tech.ydb.yoj.repository.db.list.ListRequest; +import tech.ydb.yoj.util.function.StreamSupplier; +import javax.annotation.Nullable; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -12,7 +18,9 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; +import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; /** @@ -25,14 +33,16 @@ public final class TableQueryImpl { private TableQueryImpl() { } + @NonNull public static , ID extends Entity.Id> List find( - Table table, FirstLevelCache cache, Set ids + @NonNull Table table, @NonNull EntitySchema schema, @NonNull FirstLevelCache cache, + @NonNull Set ids ) { if (ids.isEmpty()) { return List.of(); } - var orderBy = EntityExpressions.defaultOrder(table.getType()); + var orderBy = EntityExpressions.defaultOrder(schema); var isPartialIdMode = ids.iterator().next().isPartial(); var foundInCache = ids.stream() @@ -71,10 +81,13 @@ public static , ID extends Entity.Id> List find( Sets.difference(Sets.difference(ids, foundInDbIds), foundInCacheIds).forEach(cache::putEmpty); } - return merged.values().stream().sorted(EntityIdSchema.SORT_ENTITY_BY_ID).collect(Collectors.toList()); + return merged.values().stream() + .sorted(schema.defaultOrder()) + .collect(toList()); } - public static > TableQueryBuilder toQueryBuilder(Table table, ListRequest request) { + @NonNull + public static > TableQueryBuilder toQueryBuilder(@NonNull Table table, @NonNull ListRequest request) { return table.query() .index(request.getIndex()) .filter(request.getFilter()) @@ -82,4 +95,35 @@ public static > TableQueryBuilder toQueryBuilder(Table .offset(request.getOffset()) .limit(request.getPageSize() + 1); } + + public static > List find(@NonNull StreamSupplier streamSupplier, + @NonNull EntitySchema schema, + @Nullable FilterExpression filter, + @Nullable OrderExpression orderBy, + @Nullable Integer limit, + @Nullable Long offset) { + if (limit == null && offset != null && offset > 0) { + throw new IllegalArgumentException("offset > 0 with limit=null is not supported"); + } + + try (Stream stream = streamSupplier.stream()) { + Stream foundStream = stream; + if (filter != null) { + foundStream = foundStream.filter(InMemoryQueries.toPredicate(filter)); + } + if (orderBy != null) { + foundStream = foundStream.sorted(InMemoryQueries.toComparator(orderBy)); + } else { + foundStream = foundStream.sorted(schema.defaultOrder()); + } + + foundStream = foundStream.skip(offset == null ? 0L : offset); + + if (limit != null) { + foundStream = foundStream.limit(limit); + } + + return foundStream.collect(toList()); + } + } } diff --git a/repository/src/main/java/tech/ydb/yoj/repository/db/list/InMemoryQueries.java b/repository/src/main/java/tech/ydb/yoj/repository/db/list/InMemoryQueries.java index c93bed9f..a810f7d4 100644 --- a/repository/src/main/java/tech/ydb/yoj/repository/db/list/InMemoryQueries.java +++ b/repository/src/main/java/tech/ydb/yoj/repository/db/list/InMemoryQueries.java @@ -13,6 +13,9 @@ import tech.ydb.yoj.databind.schema.Schema; import tech.ydb.yoj.databind.schema.Schema.JavaField; 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.TableQueryImpl; import tech.ydb.yoj.util.function.StreamSupplier; import javax.annotation.Nullable; @@ -29,18 +32,26 @@ import static java.util.function.Predicate.not; import static java.util.stream.Collectors.toList; import static tech.ydb.yoj.databind.expression.OrderExpression.SortOrder.ASCENDING; -import static tech.ydb.yoj.repository.db.EntityIdSchema.SORT_ENTITY_BY_ID; /** * Utilities for in-memory filtering and sorting objects that have a {@link Schema} (mostly {@link Entity entities}). */ public final class InMemoryQueries { + @Deprecated(forRemoval = true) + private static final Comparator> ENTITY_DEFAULT_ORDER_VIA_REFLECTION = Comparator.comparing( + Entity::getId, (a, b) -> EntityIdSchema.ofEntity(a.getType()).compare(a, b) + ); + public static > ListResult list(@NonNull StreamSupplier streamSupplier, @NonNull ListRequest request) { + if (!(request.getSchema() instanceof EntitySchema entitySchema)) { + throw new IllegalArgumentException("Expected ListRequest for an entity, but got a non-entity schema: " + request.getSchema()); + } return ListResult.forPage( request, - find( + TableQueryImpl.find( streamSupplier, + entitySchema, request.getFilter(), request.getOrderBy(), request.getPageSize() + 1, @@ -49,6 +60,10 @@ public static > ListResult list(@NonNull StreamSupplier> List find(@NonNull StreamSupplier streamSupplier, @Nullable FilterExpression filter, @Nullable OrderExpression orderBy, @@ -66,7 +81,7 @@ public static > List find(@NonNull StreamSupplier stre if (orderBy != null) { foundStream = foundStream.sorted(toComparator(orderBy)); } else { - foundStream = foundStream.sorted(SORT_ENTITY_BY_ID); + foundStream = foundStream.sorted(ENTITY_DEFAULT_ORDER_VIA_REFLECTION); } foundStream = foundStream.skip(offset == null ? 0L : offset); From a7da58bc37bc63a129615c5f36b28cfb0c99d4cf Mon Sep 17 00:00:00 2001 From: Nikolai Amelichev <1126790+nvamelichev@users.noreply.github.com> Date: Fri, 29 Aug 2025 17:58:12 +0200 Subject: [PATCH 2/3] Update EntitySchema.java --- .../src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java b/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java index 1e35c82f..bae5f809 100644 --- a/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java +++ b/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java @@ -121,7 +121,7 @@ public ViewSchema getViewSchema(Class viewClass) { /** * @return a comparator for sorting entities by ID ascending - * @since 2.6.24 + * @since 2.6.26 */ public Comparator defaultOrder() { return Comparator.comparing(Entity::getId, getIdSchema()); From 75c0837780d5af6fc29d8dbcaf674a3c54ec3f65 Mon Sep 17 00:00:00 2001 From: Nikolai Amelichev <1126790+nvamelichev@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:37:28 +0200 Subject: [PATCH 3/3] Update EntitySchema.java --- .../main/java/tech/ydb/yoj/repository/db/EntitySchema.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java b/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java index bae5f809..2b786d20 100644 --- a/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java +++ b/repository/src/main/java/tech/ydb/yoj/repository/db/EntitySchema.java @@ -120,8 +120,8 @@ public ViewSchema getViewSchema(Class viewClass) { } /** - * @return a comparator for sorting entities by ID ascending - * @since 2.6.26 + * @return a comparator for sorting entities by ID ascending (default YOJ sort order) + * @since 2.6.33 */ public Comparator defaultOrder() { return Comparator.comparing(Entity::getId, getIdSchema());