Add equivalent test coverage for all RHS join impls (#9831)

* Add equivalent test coverage for all RHS join impls

* address comments
This commit is contained in:
Maytas Monsereenusorn 2020-05-06 13:10:41 -10:00 committed by GitHub
parent 6674d721bc
commit accd710115
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 1160 additions and 0 deletions

View File

@ -364,6 +364,74 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_factToCountryLeftUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.LEFT));
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
null,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.verifyCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
null,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{"Talk:Oswald Tilghman", null, null, null},
new Object[]{"Rallicula", null, null, null},
new Object[]{"Peremptory norm", "AU", "AU", "Australia"},
new Object[]{"Apamea abruzzorum", null, null, null},
new Object[]{"Atractus flammigerus", null, null, null},
new Object[]{"Agama mossambica", null, null, null},
new Object[]{"Mathis Bolly", "MX", "MX", "Mexico"},
new Object[]{"유희왕 GX", "KR", "KR", "Republic of Korea"},
new Object[]{"青野武", "JP", "JP", "Japan"},
new Object[]{"Golpe de Estado en Chile de 1973", "CL", "CL", "Chile"},
new Object[]{"President of India", "US", "US", "United States"},
new Object[]{"Diskussion:Sebastian Schulz", "DE", "DE", "Germany"},
new Object[]{"Saison 9 de Secret Story", "FR", "FR", "France"},
new Object[]{"Glasgow", "GB", "GB", "United Kingdom"},
new Object[]{"Didier Leclair", "CA", "CA", "Canada"},
new Object[]{"Les Argonautes", "CA", "CA", "Canada"},
new Object[]{"Otjiwarongo Airport", "US", "US", "United States"},
new Object[]{"Sarah Michelle Gellar", "CA", "CA", "Canada"},
new Object[]{"DirecTV", "US", "US", "United States"},
new Object[]{"Carlo Curti", "US", "US", "United States"},
new Object[]{"Giusy Ferreri discography", "IT", "IT", "Italy"},
new Object[]{"Roma-Bangkok", "IT", "IT", "Italy"},
new Object[]{"Wendigo", "SV", "SV", "El Salvador"},
new Object[]{"Алиса в Зазеркалье", "NO", "NO", "Norway"},
new Object[]{"Gabinete Ministerial de Rafael Correa", "EC", "EC", "Ecuador"},
new Object[]{"Old Anatolian Turkish", "US", "US", "United States"},
new Object[]{"Cream Soda", "SU", "SU", "States United"},
new Object[]{"Orange Soda", "MatchNothing", null, null},
new Object[]{"History of Fourems", "MMMM", "MMMM", "Fourems"}
)
);
}
@Test
public void test_makeCursors_factToCountryInner()
{
@ -666,6 +734,46 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_factToCountryLeftWithFilterOnFactsUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.LEFT));
Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter();
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
filter,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.verifyCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
filter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{"Diskussion:Sebastian Schulz", "DE", "DE", "Germany"}
)
);
}
@Test
public void test_makeCursors_factToCountryRightWithFilterOnLeftIsNull()
{
@ -709,6 +817,48 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_factToCountryRightWithFilterOnLeftIsNullUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.RIGHT));
Filter filter = new SelectorDimFilter("channel", null, null).toFilter();
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
filter,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.verifyCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
filter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
"countryNumber",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{null, null, NullHandling.sqlCompatible() ? null : 0L, "AX", "Atlantis"},
new Object[]{null, null, NullHandling.sqlCompatible() ? null : 0L, "USCA", "Usca"}
)
);
}
@Test
public void test_makeCursors_factToCountryFullWithFilterOnLeftIsNull()
{
@ -752,6 +902,48 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_factToCountryFullWithFilterOnLeftIsNullUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.FULL));
Filter filter = new SelectorDimFilter("channel", null, null).toFilter();
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
filter,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.verifyCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
filter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
"countryNumber",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{null, null, NullHandling.sqlCompatible() ? null : 0L, "AX", "Atlantis"},
new Object[]{null, null, NullHandling.sqlCompatible() ? null : 0L, "USCA", "Usca"}
)
);
}
@Test
public void test_makeCursors_factToCountryRightWithFilterOnJoinable()
{
@ -799,6 +991,52 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_factToCountryRightWithFilterOnJoinableUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.RIGHT));
Filter filter = new SelectorDimFilter(
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v",
"Germany",
null
).toFilter();
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
filter,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.verifyCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
filter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
"countryNumber",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{"Diskussion:Sebastian Schulz", "DE", 3L, "DE", "Germany"}
)
);
}
@Test
public void test_makeCursors_factToCountryLeftWithFilterOnJoinable()
{
@ -975,6 +1213,87 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_factToCountryInnerWithFilterInsteadOfRealJoinConditionUsingLookup()
{
// Join condition => always true.
// Filter => Fact to countries on countryIsoCode.
List<JoinableClause> joinableClauses = ImmutableList.of(
new JoinableClause(
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
LookupJoinable.wrap(countryIsoCodeToNameLookup),
JoinType.INNER,
JoinConditionAnalysis.forExpression(
"1",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
ExprMacroTable.nil()
)
)
);
Filter filter = new ExpressionDimFilter(
StringUtils.format("\"%sk\" == countryIsoCode", FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX),
ExprMacroTable.nil()
).toFilter();
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
filter,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.verifyCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
filter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{"Peremptory norm", "AU", "AU", "Australia"},
new Object[]{"Mathis Bolly", "MX", "MX", "Mexico"},
new Object[]{"유희왕 GX", "KR", "KR", "Republic of Korea"},
new Object[]{"青野武", "JP", "JP", "Japan"},
new Object[]{"Golpe de Estado en Chile de 1973", "CL", "CL", "Chile"},
new Object[]{"President of India", "US", "US", "United States"},
new Object[]{"Diskussion:Sebastian Schulz", "DE", "DE", "Germany"},
new Object[]{"Saison 9 de Secret Story", "FR", "FR", "France"},
new Object[]{"Glasgow", "GB", "GB", "United Kingdom"},
new Object[]{"Didier Leclair", "CA", "CA", "Canada"},
new Object[]{"Les Argonautes", "CA", "CA", "Canada"},
new Object[]{"Otjiwarongo Airport", "US", "US", "United States"},
new Object[]{"Sarah Michelle Gellar", "CA", "CA", "Canada"},
new Object[]{"DirecTV", "US", "US", "United States"},
new Object[]{"Carlo Curti", "US", "US", "United States"},
new Object[]{"Giusy Ferreri discography", "IT", "IT", "Italy"},
new Object[]{"Roma-Bangkok", "IT", "IT", "Italy"},
new Object[]{"Wendigo", "SV", "SV", "El Salvador"},
new Object[]{"Алиса в Зазеркалье", "NO", "NO", "Norway"},
new Object[]{"Gabinete Ministerial de Rafael Correa", "EC", "EC", "Ecuador"},
new Object[]{"Old Anatolian Turkish", "US", "US", "United States"},
new Object[]{"Cream Soda", "SU", "SU", "States United"},
new Object[]{"History of Fourems", "MMMM", "MMMM", "Fourems"}
)
);
}
@Test
public void test_makeCursors_factToRegionToCountryLeft()
{
@ -1347,6 +1666,72 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_factToCountryUsingVirtualColumnUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(
new JoinableClause(
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
LookupJoinable.wrap(countryIsoCodeToNameLookup),
JoinType.INNER,
JoinConditionAnalysis.forExpression(
StringUtils.format("\"%sk\" == virtual", FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX),
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
ExprMacroTable.nil()
)
)
);
VirtualColumns virtualColumns = VirtualColumns.create(
Collections.singletonList(
new ExpressionVirtualColumn(
"virtual",
"concat(substring(countryIsoCode, 0, 1),'L')",
ValueType.STRING,
ExprMacroTable.nil()
)
)
);
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
virtualColumns,
null,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.verifyCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
null,
Intervals.ETERNITY,
virtualColumns,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
"virtual",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{"Golpe de Estado en Chile de 1973", "CL", "CL", "CL", "Chile"},
new Object[]{"Didier Leclair", "CA", "CL", "CL", "Chile"},
new Object[]{"Les Argonautes", "CA", "CL", "CL", "Chile"},
new Object[]{"Sarah Michelle Gellar", "CA", "CL", "CL", "Chile"}
)
);
}
@Test
public void test_makeCursors_factToCountryUsingExpression()
{
@ -1404,6 +1789,63 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_factToCountryUsingExpressionUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(
new JoinableClause(
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
LookupJoinable.wrap(countryIsoCodeToNameLookup),
JoinType.INNER,
JoinConditionAnalysis.forExpression(
StringUtils.format(
"\"%sk\" == concat(substring(countryIsoCode, 0, 1),'L')",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX
),
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
ExprMacroTable.nil()
)
)
);
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
null,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.verifyCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
null,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{"Golpe de Estado en Chile de 1973", "CL", "CL", "Chile"},
new Object[]{"Didier Leclair", "CA", "CL", "Chile"},
new Object[]{"Les Argonautes", "CA", "CL", "Chile"},
new Object[]{"Sarah Michelle Gellar", "CA", "CL", "Chile"}
)
);
}
@Test
public void test_makeCursors_factToRegionTheWrongWay()
{
@ -1513,6 +1955,52 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_errorOnNonEquiJoinUsingLookup()
{
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Cannot join lookup with non-equi condition: x == y");
List<JoinableClause> joinableClauses = ImmutableList.of(
new JoinableClause(
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
LookupJoinable.wrap(countryIsoCodeToNameLookup),
JoinType.LEFT,
JoinConditionAnalysis.forExpression(
"x == y",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
ExprMacroTable.nil()
)
)
);
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
null,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.readCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
null,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of()
);
}
@Test
public void test_makeCursors_errorOnNonKeyBasedJoin()
{
@ -1559,6 +2047,51 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
);
}
@Test
public void test_makeCursors_errorOnNonKeyBasedJoinUsingLookup()
{
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Cannot join lookup with condition referring to non-key column: x == \"c1.countryName");
List<JoinableClause> joinableClauses = ImmutableList.of(
new JoinableClause(
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
LookupJoinable.wrap(countryIsoCodeToNameLookup),
JoinType.LEFT,
JoinConditionAnalysis.forExpression(
StringUtils.format("x == \"%scountryName\"", FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX),
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
ExprMacroTable.nil()
)
)
);
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
null,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.readCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
null,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of()
);
}
@Test
public void test_makeCursors_factToCountryLeft_filterExcludesAllLeftRows()
{
@ -1598,4 +2131,43 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
ImmutableList.of()
);
}
@Test
public void test_makeCursors_factToCountryLeft_filterExcludesAllLeftRowsUsingLookup()
{
Filter originalFilter = new SelectorFilter("page", "this matches nothing");
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.LEFT));
JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis(
joinableClauses,
VirtualColumns.EMPTY,
originalFilter,
true,
true,
true,
QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE
);
JoinTestHelper.verifyCursors(
new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
preAnalysis
).makeCursors(
originalFilter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of()
);
}
}

View File

@ -41,6 +41,7 @@ import org.apache.druid.segment.filter.SelectorFilter;
import org.apache.druid.segment.join.filter.JoinFilterAnalyzer;
import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis;
import org.apache.druid.segment.join.filter.JoinFilterSplit;
import org.apache.druid.segment.join.lookup.LookupJoinable;
import org.apache.druid.segment.join.table.IndexedTableJoinable;
import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
import org.junit.Assert;
@ -1001,6 +1002,95 @@ public class JoinFilterAnalyzerTest extends BaseHashJoinSegmentStorageAdapterTes
);
}
@Test
public void test_filterPushDown_factConcatExpressionToCountryLeftFilterOnChannelAndCountryNameUsingLookup()
{
JoinableClause factExprToCountry = new JoinableClause(
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
LookupJoinable.wrap(countryIsoCodeToNameLookup),
JoinType.LEFT,
JoinConditionAnalysis.forExpression(
StringUtils.format(
"\"%sk\" == concat(countryIsoCode, regionIsoCode)",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX
),
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX,
ExprMacroTable.nil()
)
);
List<JoinableClause> joinableClauses = ImmutableList.of(
factExprToCountry
);
Filter filter = new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", "#en.wikipedia"),
new SelectorFilter("c1.v", "Usca")
)
);
JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis(
joinableClauses,
filter
);
HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
joinFilterPreAnalysis
);
ExpressionVirtualColumn expectedVirtualColumn = new ExpressionVirtualColumn(
"JOIN-FILTER-PUSHDOWN-VIRTUAL-COLUMN-0",
"concat(countryIsoCode, regionIsoCode)",
ValueType.STRING,
ExprMacroTable.nil()
);
JoinFilterSplit expectedFilterSplit = new JoinFilterSplit(
new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", "#en.wikipedia"),
new InDimFilter("JOIN-FILTER-PUSHDOWN-VIRTUAL-COLUMN-0", ImmutableSet.of("USCA"), null, null).toFilter()
)
),
new SelectorFilter("c1.v", "Usca"),
ImmutableList.of(
expectedVirtualColumn
)
);
JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis);
Assert.assertEquals(
expectedFilterSplit.getBaseTableFilter(),
actualFilterSplit.getBaseTableFilter()
);
Assert.assertEquals(
expectedFilterSplit.getJoinTableFilter(),
actualFilterSplit.getJoinTableFilter()
);
ExpressionVirtualColumn actualVirtualColumn = (ExpressionVirtualColumn) actualFilterSplit.getPushDownVirtualColumns()
.get(0);
compareExpressionVirtualColumns(expectedVirtualColumn, actualVirtualColumn);
JoinTestHelper.verifyCursors(
adapter.makeCursors(
filter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{"President of India", "Usca"},
new Object[]{"Otjiwarongo Airport", "Usca"},
new Object[]{"Carlo Curti", "Usca"}
)
);
}
@Test
public void test_filterPushDown_factToCountryRightWithFilterOnChannelAndJoinable()
{
@ -1057,6 +1147,61 @@ public class JoinFilterAnalyzerTest extends BaseHashJoinSegmentStorageAdapterTes
);
}
@Test
public void test_filterPushDown_factToCountryRightWithFilterOnChannelAndJoinableUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.RIGHT));
Filter originalFilter = new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", "#de.wikipedia"),
new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "Germany")
)
);
JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis(
joinableClauses,
originalFilter
);
HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
joinFilterPreAnalysis
);
JoinFilterSplit expectedFilterSplit = new JoinFilterSplit(
new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", "#de.wikipedia"),
new InDimFilter("countryIsoCode", ImmutableSet.of("DE"), null, null).toFilter()
)
),
new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "Germany"),
ImmutableList.of()
);
JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis);
Assert.assertEquals(expectedFilterSplit, actualFilterSplit);
JoinTestHelper.verifyCursors(
adapter.makeCursors(
originalFilter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
"countryNumber",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{"Diskussion:Sebastian Schulz", "DE", 3L, "DE", "Germany"}
)
);
}
@Test
public void test_filterPushDown_factToCountryRightWithFilterOnNullColumns()
{
@ -1111,6 +1256,59 @@ public class JoinFilterAnalyzerTest extends BaseHashJoinSegmentStorageAdapterTes
);
}
@Test
public void test_filterPushDown_factToCountryRightWithFilterOnNullColumnsUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.RIGHT));
Filter originalFilter = new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", null),
new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null)
)
);
JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis(
joinableClauses,
originalFilter
);
HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
joinFilterPreAnalysis
);
JoinFilterSplit expectedFilterSplit = new JoinFilterSplit(
null,
new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", null),
new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null)
)
),
ImmutableList.of()
);
JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis);
Assert.assertEquals(expectedFilterSplit, actualFilterSplit);
JoinTestHelper.verifyCursors(
adapter.makeCursors(
originalFilter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
"countryNumber",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of()
);
}
@Test
public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnChannelAndCountryName()
{
@ -1173,6 +1371,67 @@ public class JoinFilterAnalyzerTest extends BaseHashJoinSegmentStorageAdapterTes
);
}
@Test
public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnChannelAndCountryNameUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingNumberLookup(JoinType.INNER));
Filter originalFilter = new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", "#en.wikipedia"),
new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", "Australia")
)
);
JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis(
joinableClauses,
originalFilter
);
HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
joinFilterPreAnalysis
);
JoinFilterSplit expectedFilterSplit = new JoinFilterSplit(
new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", "#en.wikipedia"),
new InDimFilter("countryNumber", ImmutableSet.of("0"), null, null).toFilter()
)
),
new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", "Australia"),
ImmutableList.of()
);
JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis);
Assert.assertEquals(expectedFilterSplit, actualFilterSplit);
// In non-SQL-compatible mode, we get an extra row, since the 'null' countryNumber for "Talk:Oswald Tilghman"
// is interpreted as 0 (a.k.a. Australia).
JoinTestHelper.verifyCursors(
adapter.makeCursors(
originalFilter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "k",
FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v"
),
NullHandling.sqlCompatible() ?
ImmutableList.of(
new Object[]{"Peremptory norm", "AU", "0", "Australia"}
) :
ImmutableList.of(
new Object[]{"Talk:Oswald Tilghman", null, "0", "Australia"},
new Object[]{"Peremptory norm", "AU", "0", "Australia"}
)
);
}
@Test
public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnNulls()
{
@ -1226,6 +1485,58 @@ public class JoinFilterAnalyzerTest extends BaseHashJoinSegmentStorageAdapterTes
);
}
@Test
public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnNullsUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.INNER));
Filter originalFilter = new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", null),
new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", null)
)
);
JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis(
joinableClauses,
originalFilter
);
HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
joinFilterPreAnalysis
);
JoinFilterSplit expectedFilterSplit = new JoinFilterSplit(
null,
new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", null),
new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", null)
)
),
ImmutableList.of()
);
JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis);
Assert.assertEquals(expectedFilterSplit, actualFilterSplit);
JoinTestHelper.verifyCursors(
adapter.makeCursors(
originalFilter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "k",
FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v"
),
ImmutableList.of()
);
}
@Test
public void test_filterPushDown_factToCountryFullWithFilterOnChannelAndCountryName()
{
@ -1282,6 +1593,61 @@ public class JoinFilterAnalyzerTest extends BaseHashJoinSegmentStorageAdapterTes
);
}
@Test
public void test_filterPushDown_factToCountryFullWithFilterOnChannelAndCountryNameUsingLookup()
{
Filter filter = new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", "#es.wikipedia"),
new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "El Salvador")
)
);
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.FULL));
JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis(
joinableClauses,
filter
);
HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
joinFilterPreAnalysis
);
JoinFilterSplit expectedFilterSplit = new JoinFilterSplit(
new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", "#es.wikipedia"),
new InDimFilter("countryIsoCode", ImmutableSet.of("SV"), null, null).toFilter()
)
),
new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "El Salvador"),
ImmutableList.of()
);
JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis);
Assert.assertEquals(expectedFilterSplit, actualFilterSplit);
JoinTestHelper.verifyCursors(
adapter.makeCursors(
filter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
"countryNumber",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of(
new Object[]{"Wendigo", "SV", 12L, "SV", "El Salvador"}
)
);
}
@Test
public void test_filterPushDown_factToCountryFullWithFilterOnNulls()
{
@ -1336,6 +1702,59 @@ public class JoinFilterAnalyzerTest extends BaseHashJoinSegmentStorageAdapterTes
);
}
@Test
public void test_filterPushDown_factToCountryFullWithFilterOnNullsUsingLookup()
{
List<JoinableClause> joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.FULL));
Filter originalFilter = new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", null),
new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null)
)
);
JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis(
joinableClauses,
originalFilter
);
HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter(
factSegment.asStorageAdapter(),
joinableClauses,
joinFilterPreAnalysis
);
JoinFilterSplit expectedFilterSplit = new JoinFilterSplit(
null,
new AndFilter(
ImmutableList.of(
new SelectorFilter("channel", null),
new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null)
)
),
ImmutableList.of()
);
JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis);
Assert.assertEquals(expectedFilterSplit, actualFilterSplit);
JoinTestHelper.verifyCursors(
adapter.makeCursors(
originalFilter,
Intervals.ETERNITY,
VirtualColumns.EMPTY,
Granularities.ALL,
false,
null
),
ImmutableList.of(
"page",
"countryIsoCode",
"countryNumber",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k",
FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v"
),
ImmutableList.of()
);
}
@Test
public void test_filterPushDown_factToRegionTwoColumnsToOneRHSColumnAndFilterOnRHS()
{

View File

@ -0,0 +1,169 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.apache.druid.segment.join.table;
import com.google.common.collect.ImmutableMap;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.math.expr.ExprMacroTable;
import org.apache.druid.query.dimension.DefaultDimensionSpec;
import org.apache.druid.query.dimension.DimensionSpec;
import org.apache.druid.query.lookup.LookupExtractor;
import org.apache.druid.segment.ColumnSelectorFactory;
import org.apache.druid.segment.DimensionSelector;
import org.apache.druid.segment.data.SingleIndexedInt;
import org.apache.druid.segment.join.JoinConditionAnalysis;
import org.apache.druid.segment.join.lookup.LookupJoinMatcher;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Map;
@RunWith(MockitoJUnitRunner.class)
public class LookupJoinMatcherTest
{
private final Map<String, String> lookupMap =
ImmutableMap.of("foo", "bar", "null", "", "empty String", "", "", "empty_string");
private static final String PREFIX = "j.";
@Mock
private LookupExtractor extractor;
@Mock
private ColumnSelectorFactory leftSelectorFactory;
@Mock
private DimensionSelector dimensionSelector;
private LookupJoinMatcher target;
@Before
public void setUp()
{
Mockito.doReturn(true).when(extractor).canIterate();
Mockito.doReturn(lookupMap.entrySet()).when(extractor).iterable();
}
@Test
public void testCreateConditionAlwaysFalseShouldReturnSuccessfullyAndNotThrowException()
{
JoinConditionAnalysis condition = JoinConditionAnalysis.forExpression("0", PREFIX, ExprMacroTable.nil());
target = LookupJoinMatcher.create(extractor, leftSelectorFactory, condition, false);
Assert.assertNotNull(target);
target = LookupJoinMatcher.create(extractor, leftSelectorFactory, condition, true);
Assert.assertNotNull(target);
}
@Test
public void testCreateConditionAlwaysTrueShouldReturnSuccessfullyAndNotThrowException()
{
JoinConditionAnalysis condition = JoinConditionAnalysis.forExpression("1", PREFIX, ExprMacroTable.nil());
Mockito.doReturn(true).when(extractor).canIterate();
target = LookupJoinMatcher.create(extractor, leftSelectorFactory, condition, false);
Assert.assertNotNull(target);
target = LookupJoinMatcher.create(extractor, leftSelectorFactory, condition, true);
Assert.assertNotNull(target);
}
@Test
public void testMatchConditionAlwaysTrue()
{
JoinConditionAnalysis condition = JoinConditionAnalysis.forExpression("1", PREFIX, ExprMacroTable.nil());
target = LookupJoinMatcher.create(extractor, leftSelectorFactory, condition, true);
// Test match first
target.matchCondition();
Assert.assertTrue(target.hasMatch());
verifyMatch("foo", "bar");
// Test match second
target.nextMatch();
Assert.assertTrue(target.hasMatch());
verifyMatch("null", "");
// Test match third
target.nextMatch();
Assert.assertTrue(target.hasMatch());
verifyMatch("empty String", "");
// Test match forth
target.nextMatch();
Assert.assertTrue(target.hasMatch());
verifyMatch("", "empty_string");
// Test no more
target.nextMatch();
Assert.assertFalse(target.hasMatch());
}
@Test
public void testMatchConditionAlwaysFalse()
{
JoinConditionAnalysis condition = JoinConditionAnalysis.forExpression("0", PREFIX, ExprMacroTable.nil());
target = LookupJoinMatcher.create(extractor, leftSelectorFactory, condition, true);
// Test match first
target.matchCondition();
Assert.assertFalse(target.hasMatch());
verifyMatch(null, null);
}
@Test
public void testMatchConditionSometimesTrueSometimesFalse()
{
final int index = 1;
SingleIndexedInt row = new SingleIndexedInt();
row.setValue(index);
Mockito.doReturn(dimensionSelector).when(leftSelectorFactory).makeDimensionSelector(ArgumentMatchers.any(DimensionSpec.class));
Mockito.doReturn(row).when(dimensionSelector).getRow();
Mockito.doReturn("foo").when(dimensionSelector).lookupName(index);
Mockito.doReturn("bar").when(extractor).apply("foo");
JoinConditionAnalysis condition = JoinConditionAnalysis.forExpression(
StringUtils.format("\"%sk\" == foo", PREFIX),
PREFIX,
ExprMacroTable.nil()
);
target = LookupJoinMatcher.create(extractor, leftSelectorFactory, condition, true);
// Test match
target.matchCondition();
Assert.assertTrue(target.hasMatch());
verifyMatch("foo", "bar");
// Test no more
target.nextMatch();
Assert.assertFalse(target.hasMatch());
}
private void verifyMatch(String expectedKey, String expectedValue)
{
DimensionSelector selector = target.getColumnSelectorFactory()
.makeDimensionSelector(DefaultDimensionSpec.of("k"));
Assert.assertEquals(-1, selector.getValueCardinality());
Assert.assertEquals(expectedKey, selector.lookupName(0));
Assert.assertEquals(expectedKey, selector.lookupName(0));
Assert.assertNull(selector.idLookup());
selector = target.getColumnSelectorFactory()
.makeDimensionSelector(DefaultDimensionSpec.of("v"));
Assert.assertEquals(-1, selector.getValueCardinality());
Assert.assertEquals(expectedValue, selector.lookupName(0));
Assert.assertEquals(expectedValue, selector.lookupName(0));
Assert.assertNull(selector.idLookup());
}
}