This is an automated email from the ASF dual-hosted git repository. volodymyr pushed a commit to branch mongo in repository https://gitbox.apache.org/repos/asf/drill.git
commit b79f532d6261955e7ff0c84be2ce2bf410875cec Author: Volodymyr Vysotskyi <[email protected]> AuthorDate: Thu Jul 29 21:06:45 2021 +0300 Fix project --- .../store/mongo/plan/MongoPluginImplementor.java | 182 +++++++++-- .../drill/exec/store/mongo/plan/MongoRules.java | 356 ++------------------- .../drill/exec/store/mongo/TestMongoQueries.java | 14 +- 3 files changed, 191 insertions(+), 361 deletions(-) diff --git a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoPluginImplementor.java b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoPluginImplementor.java index 0fc30b4..d51447c 100644 --- a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoPluginImplementor.java +++ b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoPluginImplementor.java @@ -7,6 +7,7 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.sql.SqlKind; import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; import org.apache.drill.common.expression.LogicalExpression; @@ -15,13 +16,13 @@ import org.apache.drill.exec.physical.base.GroupScan; import org.apache.drill.exec.planner.logical.DrillOptiq; import org.apache.drill.exec.planner.logical.DrillParseContext; import org.apache.drill.exec.planner.physical.PrelUtil; +import org.apache.drill.exec.store.mongo.MongoAggregateUtils; import org.apache.drill.exec.store.mongo.MongoFilterBuilder; import org.apache.drill.exec.store.mongo.MongoGroupScan; -import org.apache.drill.exec.store.mongo.MongoAggregateUtils; import org.apache.drill.exec.store.mongo.MongoScanSpec; +import org.apache.drill.exec.store.plan.PluginImplementor; import org.apache.drill.exec.store.plan.rel.PluginAggregateRel; import org.apache.drill.exec.store.plan.rel.PluginFilterRel; -import org.apache.drill.exec.store.plan.PluginImplementor; import org.apache.drill.exec.store.plan.rel.PluginLimitRel; import org.apache.drill.exec.store.plan.rel.PluginProjectRel; import org.apache.drill.exec.store.plan.rel.PluginSortRel; @@ -71,6 +72,11 @@ public class MongoPluginImplementor implements PluginImplementor { new DrillParseContext(PrelUtil.getPlannerSettings(filter.getCluster().getPlanner())), filter.getInput(), filter.getCondition()); MongoFilterBuilder mongoFilterBuilder = new MongoFilterBuilder(groupScan, conditionExp); if (runAggregate) { +// MongoRules.RexToMongoTranslator translator = +// new MongoRules.RexToMongoTranslator( +// (JavaTypeFactory) filter.getCluster().getTypeFactory(), +// MongoRules.mongoFieldNames(filter.getInput().getRowType())); +// Bson convertedFilterExpression = Aggregates.match(filter.getCondition().accept(translator).asDocument()).toBsonDocument(); Bson convertedFilterExpression = Aggregates.match(mongoFilterBuilder.parseTree()).toBsonDocument(); operations.add(convertedFilterExpression); } else { @@ -78,6 +84,131 @@ public class MongoPluginImplementor implements PluginImplementor { } } +// private String translateMatch(RexNode condition) { +// Map<String, Object> map = new HashMap<>(); +// map.put("$match", translateOr(condition)); +// return builder.toJsonString(map); +// } +// +// private Object translateOr(RexNode condition) { +// final RexNode condition2 = +// RexUtil.expandSearch(rexBuilder, null, condition); +// +// List<Object> list = new ArrayList<>(); +// for (RexNode node : RelOptUtil.disjunctions(condition2)) { +// list.add(translateAnd(node)); +// } +// switch (list.size()) { +// case 1: +// return list.get(0); +// default: +// Map<String, Object> map = builder.map(); +// map.put("$or", list); +// return map; +// } +// } +// +// /** Translates a condition that may be an AND of other conditions. Gathers +// * together conditions that apply to the same field. */ +// private Map<String, Object> translateAnd(RexNode node0) { +// eqMap.clear(); +// multimap.clear(); +// for (RexNode node : RelOptUtil.conjunctions(node0)) { +// translateMatch2(node); +// } +// Map<String, Object> map = builder.map(); +// for (Map.Entry<String, RexLiteral> entry : eqMap.entrySet()) { +// multimap.removeAll(entry.getKey()); +// map.put(entry.getKey(), literalValue(entry.getValue())); +// } +// for (Map.Entry<String, Collection<Pair<String, RexLiteral>>> entry +// : multimap.asMap().entrySet()) { +// Map<String, Object> map2 = builder.map(); +// for (Pair<String, RexLiteral> s : entry.getValue()) { +// addPredicate(map2, s.left, literalValue(s.right)); +// } +// map.put(entry.getKey(), map2); +// } +// return map; +// } +// +// private Void translateMatch2(RexNode node) { +// switch (node.getKind()) { +// case EQUALS: +// return translateBinary(null, null, (RexCall) node); +// case LESS_THAN: +// return translateBinary("$lt", "$gt", (RexCall) node); +// case LESS_THAN_OR_EQUAL: +// return translateBinary("$lte", "$gte", (RexCall) node); +// case NOT_EQUALS: +// return translateBinary("$ne", "$ne", (RexCall) node); +// case GREATER_THAN: +// return translateBinary("$gt", "$lt", (RexCall) node); +// case GREATER_THAN_OR_EQUAL: +// return translateBinary("$gte", "$lte", (RexCall) node); +// default: +// throw new AssertionError("cannot translate " + node); +// } +// } +// +// /** Translates a call to a binary operator, reversing arguments if +// * necessary. */ +// private Void translateBinary(String op, String rop, RexCall call) { +// final RexNode left = call.operands.get(0); +// final RexNode right = call.operands.get(1); +// boolean b = translateBinary2(op, left, right); +// if (b) { +// return null; +// } +// b = translateBinary2(rop, right, left); +// if (b) { +// return null; +// } +// throw new AssertionError("cannot translate op " + op + " call " + call); +// } +// +// /** Translates a call to a binary operator. Returns whether successful. */ +// private boolean translateBinary2(String op, RexNode left, RexNode right) { +// switch (right.getKind()) { +// case LITERAL: +// break; +// default: +// return false; +// } +// final RexLiteral rightLiteral = (RexLiteral) right; +// switch (left.getKind()) { +// case INPUT_REF: +// final RexInputRef left1 = (RexInputRef) left; +// String name = fieldNames.get(left1.getIndex()); +// translateOp2(op, name, rightLiteral); +// return true; +// case CAST: +// return translateBinary2(op, ((RexCall) left).operands.get(0), right); +// case ITEM: +// String itemName = MongoRules.isItem((RexCall) left); +// if (itemName != null) { +// translateOp2(op, itemName, rightLiteral); +// return true; +// } +// // fall through +// default: +// return false; +// } +// } +// +// private void translateOp2(String op, String name, RexLiteral right) { +// if (op == null) { +// // E.g.: {deptno: 100} +// eqMap.put(name, right); +// } else { +// // E.g. {deptno: {$lt: 100}} +// // which may later be combined with other conditions: +// // E.g. {deptno: [$lt: 100, $gt: 50]} +// multimap.put(name, Pair.of(op, right)); +// } +// } + + @Override public void implement(PluginLimitRel limit) throws IOException { runAggregate = true; @@ -95,23 +226,26 @@ public class MongoPluginImplementor implements PluginImplementor { @Override public void implement(PluginProjectRel project) throws IOException { - runAggregate = true; + runAggregate = runAggregate || project.getProjects().stream() + .anyMatch(expression -> !expression.isA(SqlKind.INPUT_REF)); + visitChild(project.getInput()); - MongoRules.RexToMongoTranslator translator = - new MongoRules.RexToMongoTranslator( - (JavaTypeFactory) project.getCluster().getTypeFactory(), - MongoRules.mongoFieldNames(project.getInput().getRowType())); - List<BsonElement> items = new ArrayList<>(); - for (Pair<RexNode, String> pair : project.getNamedProjects()) { - String name = pair.right; - BsonValue expr = pair.left.accept(translator); - items.add(expr.equals(new BsonString("$" + name)) - ? new BsonElement(MongoRules.maybeQuote(name), new BsonInt32(1)) - : new BsonElement(MongoRules.maybeQuote(name), expr)); - } - BsonDocument projection = Aggregates.project(new BsonDocument(items)).toBsonDocument(); if (runAggregate) { + MongoRules.RexToMongoTranslator translator = + new MongoRules.RexToMongoTranslator( + (JavaTypeFactory) project.getCluster().getTypeFactory(), + MongoRules.mongoFieldNames(project.getInput().getRowType())); + List<BsonElement> items = new ArrayList<>(); + for (Pair<RexNode, String> pair : project.getNamedProjects()) { + String name = pair.right; + BsonValue expr = pair.left.accept(translator); + items.add(expr.equals(new BsonString("$" + name)) + ? new BsonElement(MongoRules.maybeQuote(name), new BsonInt32(1)) + : new BsonElement(MongoRules.maybeQuote(name), expr)); + } + BsonDocument projection = Aggregates.project(new BsonDocument(items)).toBsonDocument(); + operations.add(projection); List<String> outNames = MongoAggregateUtils.mongoFieldNames(project.getRowType()); this.columns = outNames.stream() @@ -123,22 +257,6 @@ public class MongoPluginImplementor implements PluginImplementor { .map(SchemaPath::getSimplePath) .collect(Collectors.toList()); } -// implementor.add(op.left, op.right); - -// List<String> outNames = MongoAggregateUtils.mongoFieldNames(project.getRowType()); -// Document fields = new Document(); -// fields.put(DrillMongoConstants.ID, 0); -// List<String> inNames = MongoAggregateUtils.mongoFieldNames(project.getInput().getRowType()); -// for (int i = 0; i < outNames.size(); i++) { -// String fieldName = outNames.get(i); -// fields.put(fieldName, inNames.get(((RexInputRef) project.getChildExps().get(i)).getIndex())); -// } -// -// operations.add(Aggregates.project(fields).toBsonDocument()); - -// this.columns = outNames.stream() -// .map(SchemaPath::getSimplePath) -// .collect(Collectors.toList()); } @Override diff --git a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoRules.java b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoRules.java index 7f0daf3..953983b 100644 --- a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoRules.java +++ b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoRules.java @@ -110,12 +110,45 @@ public class MongoRules { new HashMap<>(); static { - // Arithmetic MONGO_OPERATORS.put(SqlStdOperatorTable.DIVIDE, "$divide"); MONGO_OPERATORS.put(SqlStdOperatorTable.MULTIPLY, "$multiply"); + MONGO_OPERATORS.put(SqlStdOperatorTable.ABS, "$abs"); + MONGO_OPERATORS.put(SqlStdOperatorTable.ACOS, "$acos"); + MONGO_OPERATORS.put(SqlStdOperatorTable.ASIN, "$asin"); + MONGO_OPERATORS.put(SqlStdOperatorTable.ATAN, "$atan"); + MONGO_OPERATORS.put(SqlStdOperatorTable.ATAN2, "$atan2"); + MONGO_OPERATORS.put(SqlStdOperatorTable.CEIL, "$ceil"); + MONGO_OPERATORS.put(SqlStdOperatorTable.CONCAT, "$concat"); + MONGO_OPERATORS.put(SqlStdOperatorTable.COS, "$cos"); + MONGO_OPERATORS.put(SqlStdOperatorTable.DAYOFMONTH, "$dayOfMonth"); + MONGO_OPERATORS.put(SqlStdOperatorTable.WEEK, "$isoWeek"); + MONGO_OPERATORS.put(SqlStdOperatorTable.YEAR, "$isoWeekYear"); + MONGO_OPERATORS.put(SqlStdOperatorTable.DAYOFWEEK, "$isoDayOfWeek"); + MONGO_OPERATORS.put(SqlStdOperatorTable.DAYOFYEAR, "$dayOfYear"); + MONGO_OPERATORS.put(SqlStdOperatorTable.RADIANS, "$degreesToRadians"); + MONGO_OPERATORS.put(SqlStdOperatorTable.DENSE_RANK, "$denseRank"); + MONGO_OPERATORS.put(SqlStdOperatorTable.EXP, "$exp"); + MONGO_OPERATORS.put(SqlStdOperatorTable.FLOOR, "$floor"); + MONGO_OPERATORS.put(SqlStdOperatorTable.HOUR, "$hour"); + MONGO_OPERATORS.put(SqlStdOperatorTable.LN, "$ln"); + MONGO_OPERATORS.put(SqlStdOperatorTable.LOG10, "$log10"); + MONGO_OPERATORS.put(SqlStdOperatorTable.MINUTE, "$minute"); MONGO_OPERATORS.put(SqlStdOperatorTable.MOD, "$mod"); + MONGO_OPERATORS.put(SqlStdOperatorTable.MONTH, "$month"); + MONGO_OPERATORS.put(SqlStdOperatorTable.POWER, "$pow"); + MONGO_OPERATORS.put(SqlStdOperatorTable.DEGREES, "$radiansToDegrees"); + MONGO_OPERATORS.put(SqlStdOperatorTable.RAND, "$rand"); + MONGO_OPERATORS.put(SqlStdOperatorTable.REPLACE, "$replaceAll"); + MONGO_OPERATORS.put(SqlStdOperatorTable.ROUND, "$round"); + MONGO_OPERATORS.put(SqlStdOperatorTable.SECOND, "$second"); + MONGO_OPERATORS.put(SqlStdOperatorTable.SIN, "$sin"); + MONGO_OPERATORS.put(SqlStdOperatorTable.SQRT, "$sqrt"); + MONGO_OPERATORS.put(SqlStdOperatorTable.SUBSTRING, "$substr"); MONGO_OPERATORS.put(SqlStdOperatorTable.PLUS, "$add"); MONGO_OPERATORS.put(SqlStdOperatorTable.MINUS, "$subtract"); + MONGO_OPERATORS.put(SqlStdOperatorTable.TAN, "$tan"); + MONGO_OPERATORS.put(SqlStdOperatorTable.TRIM, "trim"); + MONGO_OPERATORS.put(SqlStdOperatorTable.TRUNCATE, "$trunc"); // Boolean MONGO_OPERATORS.put(SqlStdOperatorTable.AND, MongoOp.AND.getCompareOp()); MONGO_OPERATORS.put(SqlStdOperatorTable.OR, MongoOp.OR.getCompareOp()); @@ -206,326 +239,5 @@ public class MongoRules { throw new IllegalArgumentException("Translation of " + call + " is not supported by MongoProject"); } - - private static String stripQuotes(String s) { - return s.startsWith("'") && s.endsWith("'") - ? s.substring(1, s.length() - 1) - : s; - } - } - - /* - - /** - * Rule to convert a {@link LogicalCalc} to an - * {@link MongoCalcRel}. - o/ - public static class MongoCalcRule - extends MongoConverterRule { - private MongoCalcRule(MongoConvention out) { - super( - LogicalCalc.class, - Convention.NONE, - out, - "MongoCalcRule"); - } - - public RelNode convert(RelNode rel) { - final LogicalCalc calc = (LogicalCalc) rel; - - // If there's a multiset, let FarragoMultisetSplitter work on it - // first. - if (RexMultisetUtil.containsMultiset(calc.getProgram())) { - return null; - } - - return new MongoCalcRel( - rel.getCluster(), - rel.getTraitSet().replace(out), - convert( - calc.getChild(), - calc.getTraitSet().replace(out)), - calc.getProgram(), - Project.Flags.Boxed); - } - } - - public static class MongoCalcRel extends SingleRel implements MongoRel { - private final RexProgram program; - - /** - * Values defined in {@link org.apache.calcite.rel.core.Project.Flags}. - o/ - protected int flags; - - public MongoCalcRel( - RelOptCluster cluster, - RelTraitSet traitSet, - RelNode child, - RexProgram program, - int flags) { - super(cluster, traitSet, child); - assert getConvention() instanceof MongoConvention; - this.flags = flags; - this.program = program; - this.rowType = program.getOutputRowType(); - } - - public RelOptPlanWriter explainTerms(RelOptPlanWriter pw) { - return program.explainCalc(super.explainTerms(pw)); - } - - public double getRows() { - return LogicalFilter.estimateFilteredRows( - getChild(), program); - } - - public RelOptCost computeSelfCost(RelOptPlanner planner) { - double dRows = RelMetadataQuery.getRowCount(this); - double dCpu = - RelMetadataQuery.getRowCount(getChild()) - * program.getExprCount(); - double dIo = 0; - return planner.makeCost(dRows, dCpu, dIo); - } - - public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) { - return new MongoCalcRel( - getCluster(), - traitSet, - sole(inputs), - program.copy(), - getFlags()); - } - - public int getFlags() { - return flags; - } - - public RexProgram getProgram() { - return program; - } - - public SqlString implement(MongoImplementor implementor) { - final SqlBuilder buf = new SqlBuilder(implementor.dialect); - buf.append("SELECT "); - if (isStar(program)) { - buf.append("*"); - } else { - for (Ord<RexLocalRef> ref : Ord.zip(program.getProjectList())) { - buf.append(ref.i == 0 ? "" : ", "); - expr(buf, program, ref.e); - alias(buf, null, getRowType().getFieldNames().get(ref.i)); - } - } - implementor.newline(buf) - .append("FROM "); - implementor.subQuery(buf, 0, getChild(), "t"); - if (program.getCondition() != null) { - implementor.newline(buf); - buf.append("WHERE "); - expr(buf, program, program.getCondition()); - } - return buf.toSqlString(); - } - - private static boolean isStar(RexProgram program) { - int i = 0; - for (RexLocalRef ref : program.getProjectList()) { - if (ref.getIndex() != i++) { - return false; - } - } - return i == program.getInputRowType().getFieldCount(); - } - - private static void expr( - SqlBuilder buf, RexProgram program, RexNode rex) { - if (rex instanceof RexLocalRef) { - final int index = ((RexLocalRef) rex).getIndex(); - expr(buf, program, program.getExprList().get(index)); - } else if (rex instanceof RexInputRef) { - buf.identifier( - program.getInputRowType().getFieldNames().get( - ((RexInputRef) rex).getIndex())); - } else if (rex instanceof RexLiteral) { - toSql(buf, (RexLiteral) rex); - } else if (rex instanceof RexCall) { - final RexCall call = (RexCall) rex; - switch (call.getOperator().getSyntax()) { - case Binary: - expr(buf, program, call.getOperands().get(0)); - buf.append(' ') - .append(call.getOperator().toString()) - .append(' '); - expr(buf, program, call.getOperands().get(1)); - break; - default: - throw new AssertionError(call.getOperator()); - } - } else { - throw new AssertionError(rex); - } - } - } - - private static SqlBuilder toSql(SqlBuilder buf, RexLiteral rex) { - switch (rex.getTypeName()) { - case CHAR: - case VARCHAR: - return buf.append( - new NlsString(rex.getValue2().toString(), null, null) - .asSql(false, false)); - default: - return buf.append(rex.getValue2().toString()); - } - } - -*/ - - /* - /** - * Rule to convert an {@link org.apache.calcite.rel.logical.LogicalIntersect} - * to an {@link MongoIntersectRel}. - o/ - public static class MongoIntersectRule - extends MongoConverterRule { - private MongoIntersectRule(MongoConvention out) { - super( - LogicalIntersect.class, - Convention.NONE, - out, - "MongoIntersectRule"); - } - - public RelNode convert(RelNode rel) { - final LogicalIntersect intersect = (LogicalIntersect) rel; - if (intersect.all) { - return null; // INTERSECT ALL not implemented - } - final RelTraitSet traitSet = - intersect.getTraitSet().replace(out); - return new MongoIntersectRel( - rel.getCluster(), - traitSet, - convertList(intersect.getInputs(), traitSet), - intersect.all); - } - } - - public static class MongoIntersectRel - extends Intersect - implements MongoRel { - public MongoIntersectRel( - RelOptCluster cluster, - RelTraitSet traitSet, - List<RelNode> inputs, - boolean all) { - super(cluster, traitSet, inputs, all); - assert !all; - } - - public MongoIntersectRel copy( - RelTraitSet traitSet, List<RelNode> inputs, boolean all) { - return new MongoIntersectRel(getCluster(), traitSet, inputs, all); - } - - public SqlString implement(MongoImplementor implementor) { - return setOpSql(this, implementor, " intersect "); - } - } - - /** - * Rule to convert an {@link org.apache.calcite.rel.logical.LogicalMinus} - * to an {@link MongoMinusRel}. - o/ - public static class MongoMinusRule - extends MongoConverterRule { - private MongoMinusRule(MongoConvention out) { - super( - LogicalMinus.class, - Convention.NONE, - out, - "MongoMinusRule"); - } - - public RelNode convert(RelNode rel) { - final LogicalMinus minus = (LogicalMinus) rel; - if (minus.all) { - return null; // EXCEPT ALL not implemented - } - final RelTraitSet traitSet = - rel.getTraitSet().replace(out); - return new MongoMinusRel( - rel.getCluster(), - traitSet, - convertList(minus.getInputs(), traitSet), - minus.all); - } - } - - public static class MongoMinusRel - extends Minus - implements MongoRel { - public MongoMinusRel( - RelOptCluster cluster, - RelTraitSet traitSet, - List<RelNode> inputs, - boolean all) { - super(cluster, traitSet, inputs, all); - assert !all; - } - - public MongoMinusRel copy( - RelTraitSet traitSet, List<RelNode> inputs, boolean all) { - return new MongoMinusRel(getCluster(), traitSet, inputs, all); - } - - public SqlString implement(MongoImplementor implementor) { - return setOpSql(this, implementor, " minus "); - } - } - - public static class MongoValuesRule extends MongoConverterRule { - private MongoValuesRule(MongoConvention out) { - super( - LogicalValues.class, - Convention.NONE, - out, - "MongoValuesRule"); - } - - @Override public RelNode convert(RelNode rel) { - LogicalValues valuesRel = (LogicalValues) rel; - return new MongoValuesRel( - valuesRel.getCluster(), - valuesRel.getRowType(), - valuesRel.getTuples(), - valuesRel.getTraitSet().plus(out)); - } - } - - public static class MongoValuesRel - extends Values - implements MongoRel { - MongoValuesRel( - RelOptCluster cluster, - RelDataType rowType, - List<List<RexLiteral>> tuples, - RelTraitSet traitSet) { - super(cluster, rowType, tuples, traitSet); - } - - @Override public RelNode copy( - RelTraitSet traitSet, List<RelNode> inputs) { - assert inputs.isEmpty(); - return new MongoValuesRel( - getCluster(), rowType, tuples, traitSet); - } - - public SqlString implement(MongoImplementor implementor) { - throw new AssertionError(); // TODO: - } } -*/ } diff --git a/contrib/storage-mongo/src/test/java/org/apache/drill/exec/store/mongo/TestMongoQueries.java b/contrib/storage-mongo/src/test/java/org/apache/drill/exec/store/mongo/TestMongoQueries.java index e8f698b..08b34b9 100644 --- a/contrib/storage-mongo/src/test/java/org/apache/drill/exec/store/mongo/TestMongoQueries.java +++ b/contrib/storage-mongo/src/test/java/org/apache/drill/exec/store/mongo/TestMongoQueries.java @@ -252,7 +252,7 @@ public class TestMongoQueries extends MongoTestBase { @Test public void testProjectPushDown() throws Exception { - String query = "select t.id * t.id as c from mongo.%s.`%s` t"; + String query = "select t.sales * t.sales as c, t.name from mongo.%s.`%s` t"; queryBuilder() .sql(query, DONUTS_DB, DONUTS_COLLECTION) @@ -263,12 +263,12 @@ public class TestMongoQueries extends MongoTestBase { testBuilder() .sqlQuery(query, DONUTS_DB, DONUTS_COLLECTION) .unOrdered() - .baselineColumns("c") - .baselineValues(1) - .baselineValues(4) - .baselineValues(9) - .baselineValues(16) - .baselineValues(25) + .baselineColumns("c", "name") + .baselineValues(196, "Filled") + .baselineValues(1225, "Cake") + .baselineValues(21025, "Raised") + .baselineValues(90000, "Old Fashioned") + .baselineValues(490000, "Apple Fritter") .go(); } }
