From f5a69fbf5e7e343d094687263604ce18b7b6dae5 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sat, 30 Sep 2006 23:07:46 +0000 Subject: Finished join optimization. --- .../carbonado/qe/TestIndexedQueryAnalyzer.java | 179 +++++++++++++++++---- .../carbonado/qe/TestIndexedQueryExecutor.java | 119 ++++++++------ .../carbonado/qe/TestJoinedQueryExecutor.java | 129 +++++++++++++-- .../com/amazon/carbonado/qe/TestOrderingList.java | 15 ++ .../carbonado/qe/TestUnionQueryAnalyzer.java | 12 +- 5 files changed, 353 insertions(+), 101 deletions(-) (limited to 'src/test/java/com') diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java index ef1711f..6b1919a 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java +++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java @@ -27,12 +27,17 @@ import junit.framework.TestCase; import junit.framework.TestSuite; import com.amazon.carbonado.Cursor; +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.Query; import com.amazon.carbonado.Repository; +import com.amazon.carbonado.RepositoryException; import com.amazon.carbonado.Storable; import com.amazon.carbonado.Storage; +import com.amazon.carbonado.cursor.ArraySortBuffer; import com.amazon.carbonado.cursor.SortBuffer; +import com.amazon.carbonado.info.OrderedProperty; import com.amazon.carbonado.info.StorableIndex; import com.amazon.carbonado.filter.Filter; @@ -47,8 +52,6 @@ import com.amazon.carbonado.stored.Shipment; import com.amazon.carbonado.stored.Shipper; import com.amazon.carbonado.stored.StorableTestBasic; -import static com.amazon.carbonado.qe.TestIndexedQueryExecutor.Mock; - /** * * @@ -118,7 +121,8 @@ public class TestIndexedQueryAnalyzer extends TestCase { assertTrue(result.handlesAnything()); assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter()); - assertEquals(makeIndex(Shipment.class, "orderID"), result.getLocalIndex()); + assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), + result.getLocalIndex()); assertEquals(null, result.getForeignIndex()); assertEquals(null, result.getForeignProperty()); @@ -133,7 +137,8 @@ public class TestIndexedQueryAnalyzer extends TestCase { result.getCompositeScore().getFilteringScore().getRangeStartFilters(); assertEquals(1, rangeFilters.size()); assertEquals(filter, rangeFilters.get(0)); - assertEquals(makeIndex(Shipment.class, "orderID"), result.getLocalIndex()); + assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), + result.getLocalIndex()); assertEquals(null, result.getForeignIndex()); assertEquals(null, result.getForeignProperty()); } @@ -183,7 +188,8 @@ public class TestIndexedQueryAnalyzer extends TestCase { assertTrue(result.getCompositeScore().getFilteringScore().hasRangeStart()); assertEquals(Filter.filterFor(Shipment.class, "order.orderTotal >= ?").bind(), result.getCompositeScore().getFilteringScore().getRemainderFilter()); - assertEquals(makeIndex(Shipment.class, "orderID"), result.getLocalIndex()); + assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), + result.getLocalIndex()); assertEquals(null, result.getForeignIndex()); assertEquals(null, result.getForeignProperty()); } @@ -198,20 +204,19 @@ public class TestIndexedQueryAnalyzer extends TestCase { assertTrue(result.handlesAnything()); assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter()); assertEquals(null, result.getLocalIndex()); - assertEquals(makeIndex(Address.class, "addressState"), result.getForeignIndex()); + assertEquals(makeIndex(Address.class, "addressState", "-addressCity"), + result.getForeignIndex()); assertEquals("order.address", result.getForeignProperty().toString()); } public void testChainedJoinExecutor() throws Exception { - Repository repo = new ToyRepository(); - IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, RepoAccess.INSTANCE); Filter filter = Filter.filterFor (Shipment.class, "order.address.addressState = ? & order.address.addressZip = ?"); FilterValues values = filter.initialFilterValues(); filter = values.getFilter(); - IndexedQueryAnalyzer.Result result = iqa.analyze(filter, null); + IndexedQueryAnalyzer.Result result = iqa.analyze(filter, null); assertTrue(result.handlesAnything()); assertEquals(Filter.filterFor(Shipment.class, "order.address.addressState = ?").bind(), @@ -219,30 +224,117 @@ public class TestIndexedQueryAnalyzer extends TestCase { assertEquals(Filter.filterFor(Shipment.class, "order.address.addressZip = ?").bind(), result.getCompositeScore().getFilteringScore().getRemainderFilter()); assertEquals(null, result.getLocalIndex()); - assertEquals(makeIndex(Address.class, "addressState"), result.getForeignIndex()); + assertEquals(makeIndex(Address.class, "addressState", "-addressCity"), + result.getForeignIndex()); assertEquals("order.address", result.getForeignProperty().toString()); - Mock ixExec = new Mock(result.getForeignIndex(), result.getCompositeScore()); + QueryExecutor joinExec = JoinedQueryExecutor.build + (RepoAccess.INSTANCE, + result.getForeignProperty(), result.getFilter(), result.getOrdering()); + + FilterValues fv = values.with("WA").with("12345"); - QueryExecutor joinExec = new JoinedQueryExecutor - (repo, result.getForeignProperty(), ixExec); + StringBuffer buf = new StringBuffer(); + joinExec.printPlan(buf, 0, fv); + String plan = buf.toString(); - QueryExecutor filteredExec = new FilteredQueryExecutor - (joinExec, result.getCompositeScore().getFilteringScore().getRemainderFilter()); + // This is actually a pretty terrible plan due to the iterators. This + // is expected however, since we lied and said we had indexes. + String expected = + "join: com.amazon.carbonado.stored.Shipment\n" + + "...inner loop: order\n" + + " filter: orderID = ?\n" + + " collection iterator: com.amazon.carbonado.stored.Shipment\n" + + "...outer loop\n" + + " join: com.amazon.carbonado.stored.Order\n" + + " ...inner loop: address\n" + + " filter: addressID = ?\n" + + " collection iterator: com.amazon.carbonado.stored.Order\n" + + " ...outer loop\n" + + " filter: addressState = WA & addressZip = 12345\n" + + " collection iterator: com.amazon.carbonado.stored.Address\n"; - //System.out.println(); - //filteredExec.printPlan(System.out, 0, null); + assertEquals(expected, plan); - joinExec.fetch(values.with("WA")); + joinExec.fetch(fv); + + } + + public void testComplexChainedJoinExecutor() throws Exception { + IndexedQueryAnalyzer iqa = + new IndexedQueryAnalyzer(Shipment.class, RepoAccess.INSTANCE); + Filter filter = Filter.filterFor + (Shipment.class, + "order.address.addressState = ? & order.address.addressID != ? " + + "& order.address.addressZip = ? & order.orderTotal > ? & shipmentNotes <= ? " + + "& order.addressID > ?"); + FilterValues values = filter.initialFilterValues(); + filter = values.getFilter(); + OrderingList ordering = OrderingList + .get(Shipment.class, "order.address.addressCity", "shipmentNotes", "order.orderTotal"); + IndexedQueryAnalyzer.Result result = iqa.analyze(filter, ordering); + + assertTrue(result.handlesAnything()); + assertEquals(Filter.filterFor(Shipment.class, "order.address.addressState = ?").bind(), + result.getCompositeScore().getFilteringScore().getIdentityFilter()); + assertEquals(null, result.getLocalIndex()); + assertEquals(makeIndex(Address.class, "addressState", "-addressCity"), + result.getForeignIndex()); + assertEquals("order.address", result.getForeignProperty().toString()); - assertEquals(1, ixExec.mIdentityValues.length); - assertEquals("WA", ixExec.mIdentityValues[0]); - assertEquals(BoundaryType.OPEN, ixExec.mRangeStartBoundary); - assertEquals(null, ixExec.mRangeStartValue); - assertEquals(BoundaryType.OPEN, ixExec.mRangeEndBoundary); - assertEquals(null, ixExec.mRangeEndValue); - assertFalse(ixExec.mReverseRange); - assertFalse(ixExec.mReverseOrder); + QueryExecutor joinExec = JoinedQueryExecutor.build + (RepoAccess.INSTANCE, + result.getForeignProperty(), result.getFilter(), result.getOrdering()); + + FilterValues fv = + values.with("WA").with(45).with("12345").with(100).with("Z").with(2); + + StringBuffer buf = new StringBuffer(); + joinExec.printPlan(buf, 0, fv); + String plan = buf.toString(); + + // This is actually a pretty terrible plan due to the iterators. This + // is expected however, since we lied and said we had indexes. + String expected = + "sort: [+order.address.addressCity, +shipmentNotes], [+order.orderTotal]\n" + + " join: com.amazon.carbonado.stored.Shipment\n" + + " ...inner loop: order\n" + + " sort: [+shipmentNotes]\n" + + " filter: shipmentNotes <= Z & orderID = ?\n" + + " collection iterator: com.amazon.carbonado.stored.Shipment\n" + + " ...outer loop\n" + + " join: com.amazon.carbonado.stored.Order\n" + + " ...inner loop: address\n" + + " filter: orderTotal > 100 & addressID > 2 & addressID = ?\n" + + " collection iterator: com.amazon.carbonado.stored.Order\n" + + " ...outer loop\n" + + " sort: [+addressCity]\n" + + " filter: addressState = WA & addressID != 45 & addressZip = 12345\n" + + " collection iterator: com.amazon.carbonado.stored.Address\n"; + + //System.out.println(plan); + assertEquals(expected, plan); + + joinExec.fetch(fv); + + // Now do it the easier way and compare plans. + QueryExecutor joinExec2 = result.createExecutor(); + + StringBuffer buf2 = new StringBuffer(); + joinExec2.printPlan(buf2, 0, fv); + String plan2 = buf2.toString(); + + assertEquals(expected, plan2); + + Filter expectedFilter = Filter.filterFor + (Shipment.class, + "order.address.addressState = ? & order.address.addressID != ? " + + "& order.address.addressZip = ? & order.orderTotal > ? " + + "& order.addressID > ?" + + "& shipmentNotes <= ? "); + + assertEquals(expectedFilter.disjunctiveNormalForm(), + joinExec2.getFilter().unbind().disjunctiveNormalForm()); } static class RepoAccess implements RepositoryAccess { @@ -261,7 +353,9 @@ public class TestIndexedQueryAnalyzer extends TestCase { * Partially implemented StorageAccess which only supplies information * about indexes. */ - static class StoreAccess implements StorageAccess { + static class StoreAccess + implements StorageAccess, QueryExecutorFactory + { private final Class mType; StoreAccess(Class type) { @@ -272,24 +366,45 @@ public class TestIndexedQueryAnalyzer extends TestCase { return mType; } + public QueryExecutorFactory getQueryExecutorFactory() { + return this; + } + + public QueryExecutor executor(Filter filter, OrderingList ordering) { + Iterable iterable = Collections.emptyList(); + + QueryExecutor exec = new IterableQueryExecutor + (filter.getStorableType(), iterable); + + if (filter != null) { + exec = new FilteredQueryExecutor(exec, filter); + } + + if (ordering != null && ordering.size() > 0) { + exec = new SortedQueryExecutor(null, exec, null, ordering); + } + + return exec; + } + public Collection> getAllIndexes() { StorableIndex[] indexes; if (Address.class.isAssignableFrom(mType)) { indexes = new StorableIndex[] { makeIndex(mType, "addressID"), - makeIndex(mType, "addressState") + makeIndex(mType, "addressState", "-addressCity") }; } else if (Order.class.isAssignableFrom(mType)) { indexes = new StorableIndex[] { makeIndex(mType, "orderID"), makeIndex(mType, "orderTotal"), - makeIndex(mType, "addressID") + makeIndex(mType, "addressID", "orderTotal") }; } else if (Shipment.class.isAssignableFrom(mType)) { indexes = new StorableIndex[] { makeIndex(mType, "shipmentID"), - makeIndex(mType, "orderID"), + makeIndex(mType, "orderID", "shipmentNotes"), }; } else if (Shipper.class.isAssignableFrom(mType)) { indexes = new StorableIndex[] { @@ -315,6 +430,10 @@ public class TestIndexedQueryAnalyzer extends TestCase { } public SortBuffer createSortBuffer() { + return new ArraySortBuffer(); + } + + public long countAll() { throw new UnsupportedOperationException(); } diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java index 8908825..a68fbac 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java +++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java @@ -76,7 +76,7 @@ public class TestIndexedQueryExecutor extends TestCase { CompositeScore score = CompositeScore.evaluate(index, filter, null); - Mock executor = new Mock(index, score); + Mock executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -96,7 +96,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100).with(5)); @@ -117,7 +117,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(200)); @@ -140,7 +140,7 @@ public class TestIndexedQueryExecutor extends TestCase { CompositeScore score = CompositeScore.evaluate(index, filter, null); - Mock executor = new Mock(index, score); + Mock executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -159,7 +159,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -178,7 +178,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(10).with(30)); @@ -197,7 +197,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(10).with(30)); @@ -216,7 +216,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(10).with(10)); @@ -235,7 +235,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(30).with(10)); @@ -255,7 +255,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, makeOrdering(StorableTestBasic.class, "-intProp")); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100).with(30)); @@ -276,7 +276,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -296,7 +296,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, makeOrdering(StorableTestBasic.class, "-intProp")); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -316,7 +316,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, makeOrdering(StorableTestBasic.class, "intProp")); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -339,7 +339,7 @@ public class TestIndexedQueryExecutor extends TestCase { CompositeScore score = CompositeScore.evaluate(index, filter, null); - Mock executor = new Mock(index, score); + Mock executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -358,7 +358,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -377,7 +377,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(10).with(30)); @@ -396,7 +396,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(10).with(30)); @@ -415,7 +415,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(10).with(10)); @@ -434,7 +434,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(30).with(10)); @@ -454,7 +454,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, makeOrdering(StorableTestBasic.class, "-intProp")); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100).with(30)); @@ -475,7 +475,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -495,7 +495,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, makeOrdering(StorableTestBasic.class, "-intProp")); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -515,7 +515,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, makeOrdering(StorableTestBasic.class, "intProp")); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100)); @@ -541,7 +541,7 @@ public class TestIndexedQueryExecutor extends TestCase { CompositeScore score = CompositeScore.evaluate(index, filter, null); - Mock executor = new Mock(index, score); + Mock executor = new Mock(index, score); executor.fetch(values.with(100).with(200)); @@ -560,7 +560,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(100).with(10)); @@ -580,7 +580,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(10).with(100).with(30)); @@ -612,7 +612,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, null); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(3).with(56.5).with(200.2)); @@ -628,7 +628,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, makeOrdering(StorableTestBasic.class, "doubleProp")); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(3).with(56.5).with(200.2)); @@ -644,7 +644,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, makeOrdering(StorableTestBasic.class, "stringProp")); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(3).with(56.5).with(200.2)); @@ -665,7 +665,7 @@ public class TestIndexedQueryExecutor extends TestCase { score = CompositeScore.evaluate(index, filter, makeOrdering(StorableTestBasic.class, "-stringProp")); - executor = new Mock(index, score); + executor = new Mock(index, score); executor.fetch(values.with(3).with(56.5).with("foo")); @@ -684,11 +684,40 @@ public class TestIndexedQueryExecutor extends TestCase { assertEquals(expectedOrdering, executor.getOrdering()); } + public void testHandledOrdering() throws Exception { + // Tests that ordering of executor only reveals what it actually uses. + + StorableIndex index; + Filter filter; + FilterValues values; + CompositeScore score; + Mock executor; + + index = makeIndex(StorableTestBasic.class, "intProp", "-doubleProp", "stringProp"); + + filter = Filter.filterFor(StorableTestBasic.class, "intProp = ?"); + values = filter.initialFilterValues(); + filter = values.getFilter(); + + score = CompositeScore.evaluate + (index, filter, + makeOrdering(StorableTestBasic.class, "intProp", "doubleProp")); + + executor = new Mock(index, score); + + assertEquals(values.getFilter(), executor.getFilter()); + List> expectedOrdering = + makeOrdering(StorableTestBasic.class, "+doubleProp"); + assertEquals(expectedOrdering, executor.getOrdering()); + } + /** * Mock object doesn't really open a cursor -- it just captures the passed * parameters. */ - static class Mock extends IndexedQueryExecutor { + static class Mock extends IndexedQueryExecutor + implements IndexedQueryExecutor.Support + { Object[] mIdentityValues; BoundaryType mRangeStartBoundary; Object mRangeStartValue; @@ -697,19 +726,9 @@ public class TestIndexedQueryExecutor extends TestCase { boolean mReverseRange; boolean mReverseOrder; - Mock(StorableIndex index, CompositeScore score) { - this(index, score, new MockSupport[1]); - } - - Mock(StorableIndex index, CompositeScore score, MockSupport[] ref) { - // Extremely bizarre hack to allow support to know about us. - super(ref[0] = new MockSupport(), index, score); - ((MockSupport) ref[0]).mMock = this; + public Mock(StorableIndex index, CompositeScore score) { + super(null, index, score); } - } - - static class MockSupport implements IndexedQueryExecutor.Support { - Mock mMock; public Cursor fetchSubset(StorableIndex index, Object[] identityValues, @@ -720,13 +739,13 @@ public class TestIndexedQueryExecutor extends TestCase { boolean reverseRange, boolean reverseOrder) { - mMock.mIdentityValues = identityValues; - mMock.mRangeStartBoundary = rangeStartBoundary; - mMock.mRangeStartValue = rangeStartValue; - mMock.mRangeEndBoundary = rangeEndBoundary; - mMock.mRangeEndValue = rangeEndValue; - mMock.mReverseRange = reverseRange; - mMock.mReverseOrder = reverseOrder; + mIdentityValues = identityValues; + mRangeStartBoundary = rangeStartBoundary; + mRangeStartValue = rangeStartValue; + mRangeEndBoundary = rangeEndBoundary; + mRangeEndValue = rangeEndValue; + mReverseRange = reverseRange; + mReverseOrder = reverseOrder; Collection empty = Collections.emptyList(); return new IteratorCursor(empty); diff --git a/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java index 423cd74..e1abca9 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java +++ b/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java @@ -18,8 +18,12 @@ package com.amazon.carbonado.qe; +import java.util.Arrays; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; import junit.framework.TestSuite; @@ -27,14 +31,21 @@ import com.amazon.carbonado.Cursor; import com.amazon.carbonado.FetchException; import com.amazon.carbonado.Query; import com.amazon.carbonado.Repository; +import com.amazon.carbonado.RepositoryException; import com.amazon.carbonado.Storable; import com.amazon.carbonado.Storage; +import com.amazon.carbonado.cursor.ArraySortBuffer; +import com.amazon.carbonado.cursor.SortBuffer; + import com.amazon.carbonado.filter.Filter; import com.amazon.carbonado.filter.FilterValues; +import com.amazon.carbonado.info.ChainedProperty; import com.amazon.carbonado.info.Direction; import com.amazon.carbonado.info.OrderedProperty; +import com.amazon.carbonado.info.StorableIndex; +import com.amazon.carbonado.info.StorableInfo; import com.amazon.carbonado.info.StorableIntrospector; import com.amazon.carbonado.info.StorableProperty; @@ -69,10 +80,23 @@ public class TestJoinedQueryExecutor extends TestQueryExecutor { } public void testJoin() throws Exception { - QueryExecutor addressExecutor = addressExecutor(); + StorableInfo info = StorableIntrospector.examine(UserInfo.class); + Map> properties = info.getAllProperties(); + + RepositoryAccess repoAccess = new RepoAccess(); + + ChainedProperty targetToSourceProperty = + ChainedProperty.get(properties.get("address")); + + Filter targetFilter = Filter.filterFor(UserInfo.class, "address.state = ?"); + OrderingList targetOrdering = + OrderingList.get(UserInfo.class, "+address.country"); + + QueryExecutor userExecutor = JoinedQueryExecutor.build + (repoAccess, targetToSourceProperty, targetFilter, targetOrdering); - QueryExecutor userExecutor = new JoinedQueryExecutor - (mRepository, UserInfo.class, "address", addressExecutor); + //System.out.println(); + //userExecutor.printPlan(System.out, 0, null); assertEquals("address.state = ?", userExecutor.getFilter().toString()); assertEquals("+address.country", userExecutor.getOrdering().get(0).toString()); @@ -146,8 +170,13 @@ public class TestJoinedQueryExecutor extends TestQueryExecutor { // Now do a multi join, finding everyone with an explicit neighbor in IL. - userExecutor = new JoinedQueryExecutor - (mRepository, UserInfo.class, "address.neighbor", addressExecutor); + targetToSourceProperty = ChainedProperty.parse(info, "address.neighbor"); + + targetFilter = Filter.filterFor(UserInfo.class, "address.neighbor.state = ?"); + targetOrdering = OrderingList.get(UserInfo.class, "+address.neighbor.country"); + + userExecutor = JoinedQueryExecutor.build + (repoAccess, targetToSourceProperty, targetFilter, targetOrdering); assertEquals("address.neighbor.state = ?", userExecutor.getFilter().toString()); assertEquals("+address.neighbor.country", userExecutor.getOrdering().get(0).toString()); @@ -163,24 +192,88 @@ public class TestJoinedQueryExecutor extends TestQueryExecutor { cursor.close(); assertEquals(1L, userExecutor.count(values)); - } - protected QueryExecutor addressExecutor() throws Exception { - Storage addressStorage = mRepository.storageFor(UserAddress.class); + class RepoAccess implements RepositoryAccess { + public Repository getRootRepository() { + return mRepository; + } - QueryExecutor addressExecutor = new FullScanQueryExecutor - (new ScanQuerySupport(addressStorage.query())); + public StorageAccess storageAccessFor(Class type) { + return new StoreAccess(type); + } + } + + class StoreAccess implements StorageAccess, QueryExecutorFactory { + private final Class mType; + + StoreAccess(Class type) { + mType = type; + } + + public Class getStorableType() { + return mType; + } + + public QueryExecutorFactory getQueryExecutorFactory() { + return this; + } - addressExecutor = new FilteredQueryExecutor - (addressExecutor, Filter.filterFor(UserAddress.class, "state = ?")); + public QueryExecutor executor(Filter filter, OrderingList ordering) + throws RepositoryException + { + Storage storage = mRepository.storageFor(mType); - OrderingList ordering = OrderingList.get(UserAddress.class, "+country"); + QueryExecutor exec = new FullScanQueryExecutor + (new ScanQuerySupport(storage.query())); - addressExecutor = new SortedQueryExecutor - (null, addressExecutor, null, ordering); + if (filter != null) { + exec = new FilteredQueryExecutor(exec, filter); + } - return addressExecutor; + if (ordering != null && ordering.size() > 0) { + exec = new SortedQueryExecutor(null, exec, null, ordering); + } + + return exec; + } + + public Collection> getAllIndexes() { + StorableIndex[] indexes = new StorableIndex[0]; + return Arrays.asList(indexes); + } + + public Storage storageDelegate(StorableIndex index) { + return null; + } + + public SortBuffer createSortBuffer() { + return new ArraySortBuffer(); + } + + public long countAll() { + throw new UnsupportedOperationException(); + } + + public Cursor fetchAll() { + throw new UnsupportedOperationException(); + } + + public Cursor fetchOne(StorableIndex index, Object[] identityValues) { + throw new UnsupportedOperationException(); + } + + public Cursor fetchSubset(StorableIndex index, + Object[] identityValues, + BoundaryType rangeStartBoundary, + Object rangeStartValue, + BoundaryType rangeEndBoundary, + Object rangeEndValue, + boolean reverseRange, + boolean reverseOrder) + { + throw new UnsupportedOperationException(); + } } static class ScanQuerySupport implements FullScanQueryExecutor.Support { @@ -194,6 +287,10 @@ public class TestJoinedQueryExecutor extends TestQueryExecutor { return mQuery.getStorableType(); } + public long countAll() throws FetchException { + return mQuery.count(); + } + public Cursor fetchAll() throws FetchException { return mQuery.fetch(); } diff --git a/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java b/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java index ea9bc10..8af7a2e 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java +++ b/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java @@ -184,6 +184,21 @@ public class TestOrderingList extends TestCase { assertTrue(list_1 == list_2); } + public void testSubList() throws Exception { + OrderingList list_1 = + OrderingList.get(StorableTestBasic.class, "date", "-intProp", "~stringProp"); + + assertEquals(0, list_1.subList(0, 0).size()); + assertEquals(list_1, list_1.subList(0, 3)); + + OrderingList sub = list_1.subList(0, 1); + assertEquals(1, sub.size()); + assertEquals("+date", sub.get(0).toString()); + + sub = list_1.subList(1, 3); + assertEquals(2, sub.size()); + } + public void testAsArray() throws Exception { OrderingList list = OrderingList.get(StorableTestBasic.class, "date", "intProp", "stringProp"); diff --git a/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java index e9cc8f9..7c92772 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java +++ b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java @@ -131,7 +131,8 @@ public class TestUnionQueryAnalyzer extends TestCase { assertTrue(res_1.handlesAnything()); assertEquals(Filter.filterFor(Shipment.class, "orderID = ?").bind(), res_1.getCompositeScore().getFilteringScore().getIdentityFilter()); - assertEquals(makeIndex(Shipment.class, "orderID"), res_1.getLocalIndex()); + assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), + res_1.getLocalIndex()); assertEquals(null, res_1.getForeignIndex()); assertEquals(null, res_1.getForeignProperty()); assertEquals(1, res_1.getRemainderOrdering().size()); @@ -163,7 +164,8 @@ public class TestUnionQueryAnalyzer extends TestCase { assertTrue(res_1.handlesAnything()); assertTrue(res_1.getCompositeScore().getFilteringScore().hasRangeStart()); assertFalse(res_1.getCompositeScore().getFilteringScore().hasRangeEnd()); - assertEquals(makeIndex(Shipment.class, "orderID"), res_1.getLocalIndex()); + assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), + res_1.getLocalIndex()); assertEquals(null, res_1.getForeignIndex()); assertEquals(null, res_1.getForeignProperty()); assertEquals(1, res_1.getRemainderOrdering().size()); @@ -205,7 +207,7 @@ public class TestUnionQueryAnalyzer extends TestCase { rangeFilters = res_1.getCompositeScore().getFilteringScore().getRangeEndFilters(); assertEquals(1, rangeFilters.size()); assertEquals(Filter.filterFor(Shipment.class, "orderID <= ?").bind(), rangeFilters.get(0)); - assertEquals(makeIndex(Shipment.class, "orderID"), res_1.getLocalIndex()); + assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), res_1.getLocalIndex()); assertEquals(null, res_1.getForeignIndex()); assertEquals(null, res_1.getForeignProperty()); // Sort operation required because the "shipmentID" index was not chosen. @@ -249,7 +251,7 @@ public class TestUnionQueryAnalyzer extends TestCase { assertTrue(res_1.handlesAnything()); assertEquals(Filter.filterFor(Shipment.class, "orderID = ?").bind(), res_1.getCompositeScore().getFilteringScore().getIdentityFilter()); - assertEquals(makeIndex(Shipment.class, "orderID"), res_1.getLocalIndex()); + assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), res_1.getLocalIndex()); assertEquals(null, res_1.getForeignIndex()); assertEquals(null, res_1.getForeignProperty()); assertEquals(1, res_1.getRemainderOrdering().size()); @@ -342,7 +344,7 @@ public class TestUnionQueryAnalyzer extends TestCase { IndexedQueryAnalyzer.Result res_1 = subResults.get(1); assertTrue(res_0.handlesAnything()); - assertEquals(makeIndex(Shipment.class, "orderID"), res_0.getLocalIndex()); + assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), res_0.getLocalIndex()); assertEquals(null, res_0.getForeignIndex()); assertEquals(null, res_0.getForeignProperty()); assertEquals(1, res_0.getRemainderOrdering().size()); -- cgit v1.2.3