Skip to content

Commit

Permalink
fix: fixed random order when entity primary key translation occurs
Browse files Browse the repository at this point in the history
  • Loading branch information
novoj committed Dec 8, 2023
1 parent 7e16184 commit 4796159
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 7 deletions.
12 changes: 12 additions & 0 deletions evita_engine/src/main/java/io/evitadb/core/query/QueryContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,18 @@ public int translateEntity(@Nonnull EntityContract entity) {
}
}

/**
* Method returns requested entity primary key by specifying its primary key (either virtual or real).
*/
public int translateToEntityPrimaryKey(int primaryKey) {
if (this.entityReferencePkSequence > 0) {
final EntityReferenceContract<EntityReference> referencedEntity = this.entityReferencePkIndex.get(primaryKey);
return referencedEntity == null ? primaryKey : referencedEntity.getPrimaryKey();
} else {
return primaryKey;
}
}

/**
* Method returns requested {@link EntityReference} by specifying its primary key (either virtual or real).
*/
Expand Down
26 changes: 24 additions & 2 deletions evita_engine/src/main/java/io/evitadb/core/query/QueryPlanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@
import io.evitadb.core.query.indexSelection.TargetIndexes;
import io.evitadb.core.query.sort.CacheableSorter;
import io.evitadb.core.query.sort.ConditionalSorter;
import io.evitadb.core.query.sort.NoSorter;
import io.evitadb.core.query.sort.OrderByVisitor;
import io.evitadb.core.query.sort.Sorter;
import io.evitadb.core.query.sort.primaryKey.TranslatedPrimaryKeySorter;
import io.evitadb.index.CatalogIndex;
import io.evitadb.index.EntityIndex;
import io.evitadb.index.EntityIndexType;
Expand Down Expand Up @@ -433,7 +435,7 @@ private static List<QueryPlanBuilder> createSorter(
// in case of debug cached variant tree or the entity is not known, we cannot use cache here
// and we need to retain original non-cached sorter
final Sorter sorter = orderByVisitor.getSorter();
builder.appendSorter(sorter);
builder.appendSorter(replaceNoSorterIfNecessary(queryContext, sorter));
} finally {
if (multipleAlternatives) {
queryContext.popStep();
Expand All @@ -456,7 +458,7 @@ private static List<QueryPlanBuilder> createSorter(
queryContext, builder.getFilterFormula(),
builder.getTargetIndexes(),
builder.getPrefetchFormulaVisitor(),
replacedSorter
replaceNoSorterIfNecessary(queryContext, replacedSorter)
)
);
} else {
Expand All @@ -472,6 +474,26 @@ private static List<QueryPlanBuilder> createSorter(
}
}

/**
* This method replaces no sorter - which should always represent primary keys in ascending order - with the special
* implementation in case the entity is not known in the query. In such case the primary keys are translated
* different ids and those ids are translated back at the end of the query. Unfortunately the order of the translated
* keys might be different than the original order of the primary keys, so we need to sort them here according to
* their original primary keys order in ascending fashion.
*
* @param queryContext query context
* @param sorter identified sorter
* @return sorter in input or new implementation that ensures proper sorting by primary keys in ascending order
*/
@Nonnull
private static Sorter replaceNoSorterIfNecessary(@Nonnull QueryContext queryContext, @Nonnull Sorter sorter) {
if (sorter instanceof NoSorter && !queryContext.isEntityTypeKnown()) {
return TranslatedPrimaryKeySorter.INSTANCE;
} else {
return sorter;
}
}

/**
* Method creates list of {@link ExtraResultProducer} implementations that fabricate requested extra data structures
* that are somehow connected with the processed query taking existing formula and their memoized results into
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ public int sortAndSlice(@Nonnull QueryContext queryContext, @Nonnull Formula inp

// copy the sorted data to result
final int length = endIndex - startIndex;
System.arraycopy(filteredRecordIds, startIndex, result, peak, Math.min(result.length, length));
return length;
final int newPeak = Math.min(filteredRecordIds.length, length);
System.arraycopy(filteredRecordIds, startIndex, result, peak, newPeak);
return newPeak;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
*
* _ _ ____ ____
* _____ _(_) |_ __ _| _ \| __ )
* / _ \ \ / / | __/ _` | | | | _ \
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* Copyright (c) 2023
*
* 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/main/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.core.query.sort.primaryKey;

import io.evitadb.core.query.QueryContext;
import io.evitadb.core.query.algebra.Formula;
import io.evitadb.core.query.sort.Sorter;
import io.evitadb.index.bitmap.Bitmap;
import io.evitadb.utils.ArrayUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
* This sorter sorts translated primary keys according to original primary keys in ascending order. It is used with
* the special implementation in case the entity is not known in the query. In such case the primary keys are translated
* different ids and those ids are translated back at the end of the query. Unfortunately the order of the translated
* keys might be different than the original order of the primary keys, so we need to sort them here according to
* their original primary keys order in ascending fashion.
*
* @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2023
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TranslatedPrimaryKeySorter implements Sorter {
public static final Sorter INSTANCE = new TranslatedPrimaryKeySorter();

@Nonnull
@Override
public Sorter cloneInstance() {
return INSTANCE;
}

@Nonnull
@Override
public Sorter andThen(Sorter sorterForUnknownRecords) {
return INSTANCE;
}

@Nullable
@Override
public Sorter getNextSorter() {
return null;
}

@Override
public int sortAndSlice(@Nonnull QueryContext queryContext, @Nonnull Formula input, int startIndex, int endIndex, @Nonnull int[] result, int peak) {
final Bitmap translatedPrimaryKeysBitmap = input.compute();
final int[] translatedPrimaryKeys = translatedPrimaryKeysBitmap.getArray();
final int[] originalPrimaryKeys = translatedPrimaryKeysBitmap.stream().map(queryContext::translateToEntityPrimaryKey).toArray();
// initialize order array
final int[] order = new int[originalPrimaryKeys.length];
for (int i = 0; i < order.length; i++) {
order[i] = i;
}

ArrayUtils.sortSecondAlongFirstArray(originalPrimaryKeys, order);
final int length = endIndex - startIndex;
for (int i = peak; i < translatedPrimaryKeys.length && i < length; i++) {
result[i] = translatedPrimaryKeys[order[i]];
}
return peak + Math.min(translatedPrimaryKeys.length, length);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Handles requests for multiple unknown entities identified by their URLs or codes.
Expand Down Expand Up @@ -72,8 +71,6 @@ protected EndpointResponse<List<EntityClassifier>> doHandleRequest(@Nonnull Rest
log.debug("Generated evitaDB query for unknown entity list fetch is `{}`.", query);

final List<EntityClassifier> entities = exchange.session().queryList(query, EntityClassifier.class);
log.info("Fetched list of unknown entities: {}", entities.stream().map(it -> it.getPrimaryKey().toString()).collect(Collectors.joining(", ")));

return new SuccessEndpointResponse<>(entities);
}

Expand Down

0 comments on commit 4796159

Please sign in to comment.