use of com.apple.foundationdb.record.query.expressions.OrComponent in project fdb-record-layer by FoundationDB.
the class RecordQueryPlanner method normalizeAndOrForInAsOr.
private QueryComponent normalizeAndOrForInAsOr(@Nonnull QueryComponent component) {
if (!(component instanceof AndComponent)) {
return component;
}
final AndComponent and = (AndComponent) component;
OrComponent singleOrChild = null;
final List<QueryComponent> otherChildren = new ArrayList<>();
for (QueryComponent child : and.getChildren()) {
if (child instanceof OrComponent) {
if (singleOrChild == null) {
singleOrChild = (OrComponent) child;
} else {
return and;
}
} else if (Query.isSingleFieldComparison(child)) {
otherChildren.add(child);
} else {
return and;
}
}
if (singleOrChild == null) {
return and;
}
// We have exactly one OR child and the others are single field comparisons
return OrComponent.from(distributeAnd(otherChildren, singleOrChild.getChildren()));
}
use of com.apple.foundationdb.record.query.expressions.OrComponent in project fdb-record-layer by FoundationDB.
the class TextIndexTest method queryComplexDocumentsWithAdditionalFilters.
@Test
public void queryComplexDocumentsWithAdditionalFilters() throws Exception {
final List<String> textSamples = Arrays.asList(TextSamples.ANGSTROM, TextSamples.ROMEO_AND_JULIET_PROLOGUE, TextSamples.AETHELRED, TextSamples.FRENCH, TextSamples.GERMAN, TextSamples.ROMEO_AND_JULIET_PROLOGUE, TextSamples.YIDDISH, "Napoleon and the Duke of Wellington met in Waterloo in 1815.");
final List<ComplexDocument> documents = IntStream.range(0, textSamples.size()).mapToObj(i -> ComplexDocument.newBuilder().setDocId(i).setGroup(i % 2).setText(textSamples.get(i)).addTag("3:" + (i % 3)).setScore(i).build()).collect(Collectors.toList());
final Index rankIndex = new Index("Complex$rank(score)", field("score").groupBy(field("group")), IndexTypes.RANK);
try (FDBRecordContext context = openContext()) {
openRecordStore(context, metaDataBuilder -> {
final RecordTypeBuilder complexDocRecordType = metaDataBuilder.getRecordType(COMPLEX_DOC);
metaDataBuilder.addIndex(complexDocRecordType, COMPLEX_TEXT_BY_GROUP);
metaDataBuilder.addIndex(complexDocRecordType, rankIndex);
});
documents.forEach(recordStore::saveRecord);
assertEquals(Collections.singletonList(Tuple.from(1L, 5L)), queryComplexDocumentsWithIndex(Query.field("text").text().containsAll("fearful passage love", 7), Query.field("tag").oneOfThem().equalsValue("3:2"), 1, 758136568));
assertEquals(Arrays.asList(Tuple.from(1L, 1L), Tuple.from(1L, 5L)), queryComplexDocumentsWithIndex(Query.field("text").text().containsAll("fearful passage love", 7), Query.field("text").text().containsPrefix("continu"), 1, -1043653062));
assertEquals(Collections.singletonList(Tuple.from(1L, 7L)), queryComplexDocumentsWithIndex(Query.field("text").text().contains("napoleon"), Query.and(Query.field("text").text().containsPrefix("th"), Query.field("text").text().contains("waterloo")), 1, -754900112));
assertEquals(Collections.singletonList(Tuple.from(1L, 1L)), queryComplexDocumentsWithIndex(Query.field("text").text().containsAll("fearful passage love", 7), Query.not(Query.field("tag").oneOfThem().equalsValue("3:2")), 1, 758136569));
assertEquals(Collections.singletonList(Tuple.from(1L, 1L)), queryComplexDocumentsWithIndex(Query.field("text").text().containsAll("fearful passage love", 7), Query.not(Query.field("tag").oneOfThem().equalsValue("3:2")), 1, 758136569));
assertEquals(Arrays.asList(Tuple.from(0L, 0L), Tuple.from(0L, 6L)), queryComplexDocumentsWithOr((OrComponent) Query.or(Query.field("text").text().containsAll("unit named after"), Query.field("text").text().containsPhrase("אן ארמיי און פלאט")), 0, -1558384887));
assertEquals(Collections.singletonList(Tuple.from(1L, 5L)), queryComplexDocumentsWithIndex(Query.field("text").text().containsAll("fearful passage love", 7), Query.or(Query.field("tag").oneOfThem().equalsValue("3:2"), Query.field("tag").oneOfThem().equalsValue("3:0")), true, 1, -27568755));
assertEquals(Collections.singletonList(Tuple.from(1L, 1L)), queryComplexDocumentsWithIndex(Query.field("text").text().containsAll("fearful passage love", 7), Query.rank(field("score").groupBy(field("group"))).lessThan(2L), true, 1, -2132208833));
assertEquals(Collections.singletonList(Tuple.from(1L, 5L)), queryComplexDocumentsWithIndex(Query.field("text").text().containsAllPrefixes("fear pass love", true), Query.field("tag").oneOfThem().equalsValue("3:2"), true, 1, -419325379));
assertEquals(Collections.singletonList(Tuple.from(1L, 5L)), queryComplexDocumentsWithIndex(Query.field("text").text().containsAllPrefixes("fear pass love", false), Query.field("tag").oneOfThem().equalsValue("3:2"), false, 1, -1902024530));
assertEquals(Collections.singletonList(Tuple.from(1L, 1L)), queryComplexDocumentsWithIndex(Query.field("text").text().containsAllPrefixes("fear pass love"), Query.rank(field("score").groupBy(field("group"))).lessThan(2L), true, 1, 669157421));
commit(context);
}
}
use of com.apple.foundationdb.record.query.expressions.OrComponent in project fdb-record-layer by FoundationDB.
the class FDBOrQueryToUnionTest method testOrQueryChildReordering2.
@DualPlannerTest
@ParameterizedTest
@BooleanSource
void testOrQueryChildReordering2(boolean shouldDeferFetch) throws Exception {
RecordMetaDataHook hook = complexQuerySetupHook();
complexQuerySetup(hook);
RecordQuery query1 = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.or(Query.field("str_value_indexed").equalsValue("odd"), Query.field("num_value_3_indexed").equalsValue(0), Query.field("num_value_3_indexed").equalsValue(3))).setSort(null, true).setRemoveDuplicates(true).build();
setDeferFetchAfterUnionAndIntersection(shouldDeferFetch);
// Index(MySimpleRecord$str_value_indexed [[odd],[odd]] REVERSE) ∪ Index(MySimpleRecord$num_value_3_indexed [[0],[0]] REVERSE) ∪ Index(MySimpleRecord$num_value_3_indexed [[3],[3]] REVERSE)
// Fetch(Covering(Index(MySimpleRecord$str_value_indexed [[odd],[odd]] REVERSE) -> [rec_no: KEY[1], str_value_indexed: KEY[0]]) ∪ Covering(Index(MySimpleRecord$num_value_3_indexed [[0],[0]] REVERSE) -> [num_value_3_indexed: KEY[0], rec_no: KEY[1]]) ∪ Covering(Index(MySimpleRecord$num_value_3_indexed [[3],[3]] REVERSE) -> [num_value_3_indexed: KEY[0], rec_no: KEY[1]]))
RecordQueryPlan plan1 = planner.plan(query1);
RecordQuery query2 = query1.toBuilder().setFilter(Query.or(Lists.reverse(Objects.requireNonNull((OrComponent) query1.getFilter()).getChildren()))).build();
// Index(MySimpleRecord$num_value_3_indexed [[3],[3]] REVERSE) ∪ Index(MySimpleRecord$num_value_3_indexed [[0],[0]] REVERSE) ∪ Index(MySimpleRecord$str_value_indexed [[odd],[odd]] REVERSE)
// Fetch(Covering(Index(MySimpleRecord$num_value_3_indexed [[3],[3]] REVERSE) -> [num_value_3_indexed: KEY[0], rec_no: KEY[1]]) ∪ Covering(Index(MySimpleRecord$num_value_3_indexed [[0],[0]] REVERSE) -> [num_value_3_indexed: KEY[0], rec_no: KEY[1]]) ∪ Covering(Index(MySimpleRecord$str_value_indexed [[odd],[odd]] REVERSE) -> [rec_no: KEY[1], str_value_indexed: KEY[0]]))
RecordQueryPlan plan2 = planner.plan(query2);
assertNotEquals(plan1.hashCode(), plan2.hashCode());
assertNotEquals(plan1, plan2);
assertEquals(plan1.semanticHashCode(), plan2.semanticHashCode());
assertTrue(plan1.semanticEquals(plan2));
if (shouldDeferFetch || planner instanceof CascadesPlanner) {
assertEquals(770691035, plan1.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1890796442, plan1.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(55660884, plan1.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
assertEquals(1289607451, plan2.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-29394342, plan2.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(1772831508, plan2.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertEquals(723665474, plan1.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-330673401, plan1.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(2129158337, plan1.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
assertEquals(184229634, plan2.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(2044103111, plan2.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-448638335, plan2.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
Set<Long> seen = new HashSet<>();
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
int i = 0;
try (RecordCursorIterator<FDBQueriedRecord<Message>> cursor1 = recordStore.executeQuery(plan1).asIterator();
RecordCursorIterator<FDBQueriedRecord<Message>> cursor2 = recordStore.executeQuery(plan2).asIterator()) {
while (cursor1.hasNext()) {
assertThat(cursor2.hasNext(), is(true));
FDBQueriedRecord<Message> rec1 = cursor1.next();
FDBQueriedRecord<Message> rec2 = cursor2.next();
assertEquals(Objects.requireNonNull(rec1).getRecord(), Objects.requireNonNull(rec2).getRecord());
TestRecords1Proto.MySimpleRecord.Builder myrec = TestRecords1Proto.MySimpleRecord.newBuilder();
myrec.mergeFrom(rec1.getRecord());
assertTrue(myrec.getStrValueIndexed().equals("odd") || myrec.getNumValue3Indexed() == 0 || myrec.getNumValue3Indexed() == 3, "condition on record not met");
assertFalse(seen.contains(myrec.getRecNo()), "Already saw a record!");
seen.add(myrec.getRecNo());
i++;
}
assertThat(cursor2.hasNext(), is(false));
}
assertEquals(70, i);
assertDiscardedAtMost(40, context);
if (shouldDeferFetch) {
assertLoadRecord(140, context);
}
}
}
use of com.apple.foundationdb.record.query.expressions.OrComponent in project fdb-record-layer by FoundationDB.
the class FDBOrQueryToUnionTest method testOrQueryNoIndex.
/**
* Verify that queries with an OR of predicates with a common scan and different filters does not bother with a Union.
*/
@DualPlannerTest
void testOrQueryNoIndex() throws Exception {
RecordMetaDataHook hook = metadata -> metadata.removeIndex("MySimpleRecord$num_value_3_indexed");
complexQuerySetup(hook);
QueryComponent orComponent = Query.or(Query.field("num_value_3_indexed").equalsValue(1), Query.field("num_value_3_indexed").equalsValue(2), Query.field("num_value_3_indexed").equalsValue(4));
RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.and(Query.field("str_value_indexed").equalsValue("even"), orComponent)).build();
// Index(MySimpleRecord$str_value_indexed [[even],[even]]) | Or([num_value_3_indexed EQUALS 1, num_value_3_indexed EQUALS 2, num_value_3_indexed EQUALS 4])
RecordQueryPlan plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
final BindingMatcher<? extends RecordQueryPlan> planMatcher = filterPlan(indexPlan().where(indexName("MySimpleRecord$str_value_indexed")).and(scanComparisons(range("[[even],[even]]")))).where(queryComponents(exactly(equalsObject(orComponent))));
assertMatchesExactly(plan, planMatcher);
assertEquals(-1553701984, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1108620348, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(1573180943, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
final BindingMatcher<? extends RecordQueryPlan> planMatcher = predicatesFilterPlan(indexPlan().where(indexName("MySimpleRecord$str_value_indexed")).and(scanComparisons(range("[[even],[even]]")))).where(predicates(only(orPredicate(exactly(valuePredicate(fieldValue("num_value_3_indexed"), new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 1)), valuePredicate(fieldValue("num_value_3_indexed"), new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 2)), valuePredicate(fieldValue("num_value_3_indexed"), new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 4)))))));
assertMatchesExactly(plan, planMatcher);
assertEquals(-1029166388, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1957223857, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-1873182844, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
int i = 0;
try (RecordCursorIterator<FDBQueriedRecord<Message>> cursor = recordStore.executeQuery(plan).asIterator()) {
while (cursor.hasNext()) {
FDBQueriedRecord<Message> rec = cursor.next();
TestRecords1Proto.MySimpleRecord.Builder myrec = TestRecords1Proto.MySimpleRecord.newBuilder();
myrec.mergeFrom(Objects.requireNonNull(rec).getRecord());
assertEquals("even", myrec.getStrValueIndexed());
assertTrue(myrec.getNumValue3Indexed() == 1 || myrec.getNumValue3Indexed() == 2 || myrec.getNumValue3Indexed() == 4);
i++;
}
}
assertEquals(10 + 10 + 10, i);
assertDiscardedExactly(10 + 10, context);
}
}
use of com.apple.foundationdb.record.query.expressions.OrComponent in project fdb-record-layer by FoundationDB.
the class RecordQueryPlanner method normalizeAndOr.
@Nonnull
private // Distribute it across a disjunction so that we can union complex index lookups.
QueryComponent normalizeAndOr(AndComponent and) {
if (and.getChildren().size() == 2) {
QueryComponent child1 = and.getChildren().get(0);
QueryComponent child2 = and.getChildren().get(1);
if (child1 instanceof OrComponent && Query.isSingleFieldComparison(child2)) {
return OrComponent.from(distributeAnd(Collections.singletonList(child2), ((OrComponent) child1).getChildren()));
}
if (child2 instanceof OrComponent && Query.isSingleFieldComparison(child1)) {
return OrComponent.from(distributeAnd(Collections.singletonList(child1), ((OrComponent) child2).getChildren()));
}
}
return and;
}
Aggregations