From 970943b712ee203083d724727e6de2bd2a3e47fd Mon Sep 17 00:00:00 2001 From: datomo Date: Mon, 4 Dec 2023 01:46:36 +0100 Subject: [PATCH] simplification for document, dont merge --- .../db/algebra/core/AggregateCall.java | 2 +- .../algebra/core/DocumentAggregateCall.java | 46 +++ .../core/document/DocumentAggregate.java | 34 +- .../core/document/DocumentProject.java | 4 +- .../DocumentAggregateToAggregateRule.java | 27 +- .../document/DocumentProjectToCalcRule.java | 3 +- .../document/LogicalDocumentAggregate.java | 32 +- .../db/algebra/type/DocumentType.java | 43 ++- .../java/org/polypheny/db/rex/RexNameRef.java | 5 + .../java/org/polypheny/db/rex/RexNode.java | 3 +- .../java/org/polypheny/db/PolyphenyDb.java | 2 +- .../shuttles/QueryParameterizer.java | 2 +- .../mongodb/rules/MongoDocumentAggregate.java | 108 +++--- .../mongodb/rules/MongoDocumentModify.java | 8 +- .../mongodb/rules/MongoDocumentProject.java | 12 +- .../adapter/mongodb/rules/MongoProject.java | 4 +- .../db/adapter/mongodb/rules/MongoRules.java | 326 ++---------------- .../db/adapter/mongodb/rules/MongoScan.java | 20 +- .../mongodb/util/RexToMongoTranslator.java | 317 +++++++++++++++++ .../languages/mql2alg/MqlToAlgConverter.java | 105 ++---- .../polypheny/db/webui/crud/LanguageCrud.java | 62 ++-- 21 files changed, 600 insertions(+), 565 deletions(-) create mode 100644 core/src/main/java/org/polypheny/db/algebra/core/DocumentAggregateCall.java create mode 100644 plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/util/RexToMongoTranslator.java diff --git a/core/src/main/java/org/polypheny/db/algebra/core/AggregateCall.java b/core/src/main/java/org/polypheny/db/algebra/core/AggregateCall.java index fcb2bac05a..10e92b9914 100644 --- a/core/src/main/java/org/polypheny/db/algebra/core/AggregateCall.java +++ b/core/src/main/java/org/polypheny/db/algebra/core/AggregateCall.java @@ -51,7 +51,7 @@ /** - * Call to an aggregate function within an {@link org.polypheny.db.algebra.core.Aggregate}. + * Call to an aggregate function within an {@link Aggregate}. */ public class AggregateCall { diff --git a/core/src/main/java/org/polypheny/db/algebra/core/DocumentAggregateCall.java b/core/src/main/java/org/polypheny/db/algebra/core/DocumentAggregateCall.java new file mode 100644 index 0000000000..67d8f8bd75 --- /dev/null +++ b/core/src/main/java/org/polypheny/db/algebra/core/DocumentAggregateCall.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2023 The Polypheny Project + * + * Licensed 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.polypheny.db.algebra.core; + +import java.util.Optional; +import org.polypheny.db.algebra.fun.AggFunction; +import org.polypheny.db.rex.RexNode; + +public class DocumentAggregateCall { + + public final String name; + public final AggFunction function; + private final RexNode input; + + + public DocumentAggregateCall( String name, AggFunction function, RexNode input ) { + this.name = name; + this.function = function; + this.input = input; + } + + + public static DocumentAggregateCall create( String name, AggFunction function, RexNode input ) { + return new DocumentAggregateCall( name, function, input ); + } + + + public Optional getInput() { + return Optional.ofNullable( input ); + } + +} diff --git a/core/src/main/java/org/polypheny/db/algebra/core/document/DocumentAggregate.java b/core/src/main/java/org/polypheny/db/algebra/core/document/DocumentAggregate.java index 23a7d9868d..32e116669c 100644 --- a/core/src/main/java/org/polypheny/db/algebra/core/document/DocumentAggregate.java +++ b/core/src/main/java/org/polypheny/db/algebra/core/document/DocumentAggregate.java @@ -21,44 +21,33 @@ import java.util.Objects; import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.SingleAlg; -import org.polypheny.db.algebra.core.AggregateCall; +import org.polypheny.db.algebra.core.DocumentAggregateCall; import org.polypheny.db.algebra.type.DocumentType; import org.polypheny.db.plan.AlgOptCluster; import org.polypheny.db.plan.AlgTraitSet; +import org.polypheny.db.rex.RexNode; import org.polypheny.db.schema.trait.ModelTrait; public class DocumentAggregate extends SingleAlg implements DocumentAlg { - public final boolean indicator; - public final List aggCalls; - public final List groupSet; - public final List> groupSets; - public final List names; + @NotNull + public final List aggCalls; + @Nullable // null means "group by all columns in input row" + public final RexNode group; /** * Creates a {@link DocumentAggregate}. * {@link ModelTrait#DOCUMENT} native node of an aggregate. */ - protected DocumentAggregate( AlgOptCluster cluster, AlgTraitSet traits, AlgNode child, boolean indicator, @NotNull List groupSet, List> groupSets, List aggCalls, List names ) { + protected DocumentAggregate( AlgOptCluster cluster, AlgTraitSet traits, AlgNode child, @Nullable RexNode group, List aggCalls ) { super( cluster, traits, child ); - this.indicator = indicator; // true is allowed, but discouraged + this.group = group; this.aggCalls = ImmutableList.copyOf( aggCalls ); - this.groupSet = Objects.requireNonNull( groupSet ); - this.names = names; - if ( groupSets == null ) { - this.groupSets = ImmutableList.of( groupSet ); - } else { - this.groupSets = ImmutableList.copyOf( groupSets ); - //assert ImmutableBitSet.ORDERING.isStrictlyOrdered( groupSets ) : groupSets; - /*for ( List set : groupSets ) { - assert groupSet.contains( set ); - }*/ - } - assert groupSet.size() <= child.getRowType().getFieldCount(); this.rowType = DocumentType.ofDoc(); } @@ -67,10 +56,7 @@ protected DocumentAggregate( AlgOptCluster cluster, AlgTraitSet traits, AlgNode public String algCompareString() { return this.getClass().getSimpleName() + "$" + input.algCompareString() + "$" + - (aggCalls != null ? aggCalls.stream().map( Objects::toString ).collect( Collectors.joining( " $ " ) ) : "") + "$" + - (groupSet != null ? groupSet.toString() : "") + "$" + - (groupSets != null ? groupSets.stream().map( Objects::toString ).collect( Collectors.joining( " $ " ) ) : "") + "$" + - indicator + "&"; + (aggCalls != null ? aggCalls.stream().map( Objects::toString ).collect( Collectors.joining( " $ " ) ) : "") + "&"; } diff --git a/core/src/main/java/org/polypheny/db/algebra/core/document/DocumentProject.java b/core/src/main/java/org/polypheny/db/algebra/core/document/DocumentProject.java index eb4ac5871c..7fb26f090e 100644 --- a/core/src/main/java/org/polypheny/db/algebra/core/document/DocumentProject.java +++ b/core/src/main/java/org/polypheny/db/algebra/core/document/DocumentProject.java @@ -57,13 +57,13 @@ protected DocumentProject( AlgOptCluster cluster, AlgTraitSet traits, AlgNode in super( cluster, traits, input ); this.includes = includes; this.excludes = excludes; - this.rowType = DocumentType.ofDoc(); + this.rowType = DocumentType.ofIncludes( includes ).ofExcludes( excludes ); } @Override public String algCompareString() { - return "$" + getClass().getSimpleName() + "$" + includes.hashCode() + "$" + getInput().algCompareString(); + return "$" + getClass().getSimpleName() + "$" + includes.hashCode() + "$" + excludes.hashCode() + getInput().algCompareString(); } diff --git a/core/src/main/java/org/polypheny/db/algebra/enumerable/document/DocumentAggregateToAggregateRule.java b/core/src/main/java/org/polypheny/db/algebra/enumerable/document/DocumentAggregateToAggregateRule.java index 2ba8cfd1be..3bc1122dd8 100644 --- a/core/src/main/java/org/polypheny/db/algebra/enumerable/document/DocumentAggregateToAggregateRule.java +++ b/core/src/main/java/org/polypheny/db/algebra/enumerable/document/DocumentAggregateToAggregateRule.java @@ -17,33 +17,13 @@ package org.polypheny.db.algebra.enumerable.document; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.polypheny.db.algebra.AlgNode; -import org.polypheny.db.algebra.enumerable.EnumerableConvention; -import org.polypheny.db.algebra.enumerable.EnumerableProject; import org.polypheny.db.algebra.logical.document.LogicalDocumentAggregate; -import org.polypheny.db.algebra.logical.document.LogicalDocumentProject; -import org.polypheny.db.algebra.logical.relational.LogicalProject; -import org.polypheny.db.algebra.operators.OperatorName; -import org.polypheny.db.algebra.type.AlgDataTypeField; -import org.polypheny.db.algebra.type.DocumentType; -import org.polypheny.db.languages.OperatorRegistry; -import org.polypheny.db.languages.QueryLanguage; import org.polypheny.db.plan.AlgOptRule; import org.polypheny.db.plan.AlgOptRuleCall; import org.polypheny.db.rex.RexIndexRef; import org.polypheny.db.rex.RexNode; -import org.polypheny.db.schema.trait.ModelTrait; import org.polypheny.db.tools.AlgBuilder; -import org.polypheny.db.tools.AlgBuilder.GroupKey; -import org.polypheny.db.type.PolyType; -import org.polypheny.db.type.entity.PolyList; -import org.polypheny.db.type.entity.PolyString; -import org.polypheny.db.util.ImmutableBitSet; public class DocumentAggregateToAggregateRule extends AlgOptRule { @@ -51,7 +31,7 @@ public class DocumentAggregateToAggregateRule extends AlgOptRule { public DocumentAggregateToAggregateRule() { - super( operand( LogicalDocumentAggregate.class, any() ), "DOCUMENT_AGGREGATE_TO_AGGREGATE" ); + super( operand( LogicalDocumentAggregate.class, any() ), DocumentAggregateToAggregateRule.class.getSimpleName() ); } @@ -68,10 +48,11 @@ public void onMatch( AlgOptRuleCall call ) { List nodes = new ArrayList<>(); List names = new ArrayList<>(); RexIndexRef parent = builder.getRexBuilder().makeInputRef( alg.getInput(), 0 ); + // todo dl fix //nodes.add( parent ); //names.add( alg.getInput().getRowType().getFieldNames().get( 0 ) ); - for ( String path : alg.groupSet ) { + /*for ( String path : alg.groupSet ) { RexNode node = builder.getRexBuilder().makeCall( DocumentType.ofId(), OperatorRegistry.get( QueryLanguage.from( "mongo" ), OperatorName.MQL_QUERY_VALUE ), @@ -119,7 +100,7 @@ public void onMatch( AlgOptRuleCall call ) { Map docs = enumerableAggregate.getRowType().getFields().stream().collect( Collectors.toMap( AlgDataTypeField::getName, e -> builder.getRexBuilder().makeInputRef( DocumentType.ofDoc(), e.getIndex() ) ) ); call.transformTo( LogicalDocumentProject.create( enumerableAggregate, docs, List.of() ) ); - // call.transformTo( LogicalAggregate.create( alg.getInput(), alg.groupSet, alg.groupSets, alg.aggCalls ) ); + // call.transformTo( LogicalAggregate.create( alg.getInput(), alg.groupSet, alg.groupSets, alg.aggCalls ) );*/ } } diff --git a/core/src/main/java/org/polypheny/db/algebra/enumerable/document/DocumentProjectToCalcRule.java b/core/src/main/java/org/polypheny/db/algebra/enumerable/document/DocumentProjectToCalcRule.java index 1f8af376e4..d96048ef8c 100644 --- a/core/src/main/java/org/polypheny/db/algebra/enumerable/document/DocumentProjectToCalcRule.java +++ b/core/src/main/java/org/polypheny/db/algebra/enumerable/document/DocumentProjectToCalcRule.java @@ -24,6 +24,7 @@ import org.polypheny.db.algebra.enumerable.EnumerableCalc; import org.polypheny.db.algebra.enumerable.EnumerableConvention; import org.polypheny.db.algebra.logical.document.LogicalDocumentProject; +import org.polypheny.db.algebra.type.DocumentType; import org.polypheny.db.plan.Convention; import org.polypheny.db.rex.RexFieldAccess; import org.polypheny.db.rex.RexNode; @@ -44,7 +45,7 @@ public DocumentProjectToCalcRule() { public AlgNode convert( AlgNode alg ) { final LogicalDocumentProject project = (LogicalDocumentProject) alg; final AlgNode input = project.getInput(); - final RexProgram program = RexProgram.create( input.getRowType(), List.of( replaceAccess( project.asSingleProject() ) ), null, project.getRowType(), project.getCluster().getRexBuilder() ); + final RexProgram program = RexProgram.create( input.getRowType(), List.of( replaceAccess( project.asSingleProject() ) ), null, DocumentType.ofId(), project.getCluster().getRexBuilder() ); return EnumerableCalc.create( convert( input, input.getTraitSet().replace( EnumerableConvention.INSTANCE ) ), program ); } diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/document/LogicalDocumentAggregate.java b/core/src/main/java/org/polypheny/db/algebra/logical/document/LogicalDocumentAggregate.java index cbc57a0f0f..8876e61103 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/document/LogicalDocumentAggregate.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/document/LogicalDocumentAggregate.java @@ -17,17 +17,17 @@ package org.polypheny.db.algebra.logical.document; import java.util.List; -import java.util.stream.IntStream; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.AlgShuttle; -import org.polypheny.db.algebra.core.Aggregate; -import org.polypheny.db.algebra.core.AggregateCall; +import org.polypheny.db.algebra.core.DocumentAggregateCall; import org.polypheny.db.algebra.core.document.DocumentAggregate; import org.polypheny.db.algebra.type.AlgDataType; +import org.polypheny.db.algebra.type.AlgDataTypeFactory; +import org.polypheny.db.algebra.type.DocumentType; import org.polypheny.db.plan.AlgOptCluster; import org.polypheny.db.plan.AlgTraitSet; import org.polypheny.db.plan.Convention; -import org.polypheny.db.util.ImmutableBitSet; +import org.polypheny.db.rex.RexNode; public class LogicalDocumentAggregate extends DocumentAggregate { @@ -35,40 +35,42 @@ public class LogicalDocumentAggregate extends DocumentAggregate { /** * Subclass of {@link DocumentAggregate} not targeted at any particular engine or calling convention. */ - protected LogicalDocumentAggregate( AlgOptCluster cluster, AlgTraitSet traits, AlgNode child, boolean indicator, List groupSet, List> groupSets, List aggCalls, List names ) { - super( cluster, traits, child, indicator, groupSet, groupSets, aggCalls, names ); + protected LogicalDocumentAggregate( AlgOptCluster cluster, AlgTraitSet traits, AlgNode child, RexNode group, List aggCalls ) { + super( cluster, traits, child, group, aggCalls ); } /** * Creates a LogicalAggregate. */ - public static LogicalDocumentAggregate create( final AlgNode input, List groupSet, List> groupSets, List aggCalls, List names ) { - return create_( input, false, groupSet, groupSets, aggCalls, names ); + public static LogicalDocumentAggregate create( final AlgNode input, RexNode group, List aggCalls ) { + return create_( input, group, aggCalls ); } - private static LogicalDocumentAggregate create_( final AlgNode input, boolean indicator, List groupSet, List> groupSets, List aggCalls, List names ) { + private static LogicalDocumentAggregate create_( final AlgNode input, RexNode group, List aggCalls ) { final AlgOptCluster cluster = input.getCluster(); final AlgTraitSet traitSet = cluster.traitSetOf( Convention.NONE ); - return new LogicalDocumentAggregate( cluster, traitSet, input, indicator, groupSet, groupSets, aggCalls, names ); + return new LogicalDocumentAggregate( cluster, traitSet, input, group, aggCalls ); } @Override protected AlgDataType deriveRowType() { - return Aggregate.deriveRowType( getCluster().getTypeFactory(), getInput().getRowType(), indicator, getBitGroupSet(), null, aggCalls ); - } + AlgDataTypeFactory.Builder builder = getCluster().getTypeFactory().builder(); + builder.add( "_id", null, DocumentType.ofDoc() ); + for ( DocumentAggregateCall aggCall : aggCalls ) { + builder.add( aggCall.name, null, DocumentType.ofDoc() ); + } - public ImmutableBitSet getBitGroupSet() { - return ImmutableBitSet.of( IntStream.range( 0, groupSet.size() ).toArray() ); + return builder.build(); } @Override public AlgNode copy( AlgTraitSet traitSet, List inputs ) { - return new LogicalDocumentAggregate( inputs.get( 0 ).getCluster(), traitSet, inputs.get( 0 ), indicator, groupSet, groupSets, aggCalls, names ); + return new LogicalDocumentAggregate( inputs.get( 0 ).getCluster(), traitSet, inputs.get( 0 ), group, aggCalls ); } diff --git a/core/src/main/java/org/polypheny/db/algebra/type/DocumentType.java b/core/src/main/java/org/polypheny/db/algebra/type/DocumentType.java index 55b7b8fdd6..b45307c9d8 100644 --- a/core/src/main/java/org/polypheny/db/algebra/type/DocumentType.java +++ b/core/src/main/java/org/polypheny/db/algebra/type/DocumentType.java @@ -20,14 +20,15 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import javax.annotation.Nonnull; import lombok.Data; +import org.jetbrains.annotations.NotNull; import org.polypheny.db.nodes.IntervalQualifier; import org.polypheny.db.rex.RexNode; import org.polypheny.db.type.PolyType; import org.polypheny.db.util.Collation; -import org.polypheny.db.util.Pair; @Data public class DocumentType implements AlgDataType, AlgDataTypeFamily { @@ -38,28 +39,32 @@ public class DocumentType implements AlgDataType, AlgDataTypeFamily { public static final Integer DATA_SIZE = 12024; public StructKind structKind; - public final List fixedFields; + public final List fixed; + public final List excluded; + + boolean isFixed = false; public String physicalName = null; public String digest; - public DocumentType( @Nonnull List fixedFields ) { - this.structKind = fixedFields.isEmpty() ? StructKind.NONE : StructKind.SEMI; - this.fixedFields = new ArrayList<>( fixedFields ); + public DocumentType( @NotNull List fixed, @NotNull List excluded ) { + this.structKind = fixed.isEmpty() ? StructKind.NONE : StructKind.SEMI; + this.excluded = excluded; + this.fixed = new ArrayList<>( fixed ); this.digest = computeDigest(); } - public DocumentType() { - this( List.of() ); + public DocumentType( @Nonnull List fixed ) { + this( fixed, List.of() ); } - public DocumentType( List ids, List names ) { - this( Streams.mapWithIndex( Pair.zip( ids, names ).stream(), ( p, i ) -> new AlgDataTypeFieldImpl( -1L, p.getRight(), (int) i, p.getLeft().getType() ) ).collect( Collectors.toList() ) ); + public DocumentType() { + this( List.of() ); } @@ -81,10 +86,20 @@ public static AlgDataType ofDoc() { } + public static DocumentType ofIncludes( Map includes ) { + return new DocumentType( Streams.mapWithIndex( includes.entrySet().stream(), ( e, i ) -> new AlgDataTypeFieldImpl( -1L, e.getKey(), (int) i, e.getValue().getType() ) ).collect( Collectors.toList() ) ); + } + + + public AlgDataType ofExcludes( List excludes ) { + return new DocumentType( fixed, excludes ); + } + private String computeDigest() { - assert fixedFields != null; + assert fixed != null; return getClass().getSimpleName() + - fixedFields.stream().map( f -> f.getType().getFullTypeString() ).collect( Collectors.joining( "$" ) ); + fixed.stream().map( f -> f.getType().getFullTypeString() ).collect( Collectors.joining( "$" ) ) + + String.join( "$", excluded ); } @@ -96,7 +111,7 @@ public boolean isStruct() { @Override public List getFields() { - return fixedFields; + return fixed; } @@ -108,7 +123,7 @@ public List getFieldNames() { @Override public List getFieldIds() { - return fixedFields.stream().map( AlgDataTypeField::getId ).collect( Collectors.toList() ); + return fixed.stream().map( AlgDataTypeField::getId ).collect( Collectors.toList() ); } @@ -126,7 +141,7 @@ public AlgDataTypeField getField( String fieldName, boolean caseSensitive, boole return getFields().get( index ); } AlgDataTypeFieldImpl added = new AlgDataTypeFieldImpl( -1L, fieldName, getFieldCount(), new DocumentType() ); - fixedFields.add( added ); + fixed.add( added ); computeDigest(); return added; diff --git a/core/src/main/java/org/polypheny/db/rex/RexNameRef.java b/core/src/main/java/org/polypheny/db/rex/RexNameRef.java index 0773b7523e..319c5bdcf3 100644 --- a/core/src/main/java/org/polypheny/db/rex/RexNameRef.java +++ b/core/src/main/java/org/polypheny/db/rex/RexNameRef.java @@ -37,6 +37,11 @@ public RexNameRef( List names, AlgDataType type ) { } + public RexNameRef( String name, AlgDataType type ) { + this( List.of( name.split( "\\." ) ), type ); + } + + public static RexNameRef create( List names, AlgDataType type ) { return new RexNameRef( names, type ); } diff --git a/core/src/main/java/org/polypheny/db/rex/RexNode.java b/core/src/main/java/org/polypheny/db/rex/RexNode.java index 23eac6e557..c60dd43a09 100644 --- a/core/src/main/java/org/polypheny/db/rex/RexNode.java +++ b/core/src/main/java/org/polypheny/db/rex/RexNode.java @@ -38,6 +38,7 @@ import org.polypheny.db.algebra.constant.Kind; import org.polypheny.db.algebra.type.AlgDataType; import org.polypheny.db.nodes.Node; +import org.polypheny.db.util.Wrapper; /** @@ -50,7 +51,7 @@ * * All sub-classes of RexNode are immutable. */ -public abstract class RexNode { +public abstract class RexNode implements Wrapper { // Effectively final. Set in each sub-class constructor, and never re-set. protected String digest; diff --git a/dbms/src/main/java/org/polypheny/db/PolyphenyDb.java b/dbms/src/main/java/org/polypheny/db/PolyphenyDb.java index 1d4cc135b9..af42d9d428 100644 --- a/dbms/src/main/java/org/polypheny/db/PolyphenyDb.java +++ b/dbms/src/main/java/org/polypheny/db/PolyphenyDb.java @@ -124,7 +124,7 @@ public class PolyphenyDb { public boolean daemonMode = false; @Option(name = { "-defaultStore" }, description = "Type of default storeId") - public String defaultStoreName = "hsqldb"; + public String defaultStoreName = "mongodb"; @Option(name = { "-defaultSource" }, description = "Type of default source") public String defaultSourceName = "csv"; diff --git a/dbms/src/main/java/org/polypheny/db/processing/shuttles/QueryParameterizer.java b/dbms/src/main/java/org/polypheny/db/processing/shuttles/QueryParameterizer.java index 127fc82326..68b1da3497 100644 --- a/dbms/src/main/java/org/polypheny/db/processing/shuttles/QueryParameterizer.java +++ b/dbms/src/main/java/org/polypheny/db/processing/shuttles/QueryParameterizer.java @@ -463,7 +463,7 @@ public RexNode visitLiteral( RexLiteral literal ) { @Override public RexNode visitCall( RexCall call ) { - if ( call.getKind().belongsTo( Kind.MQL_KIND ) && call.op.getOperatorName() != OperatorName.MQL_QUERY_VALUE ) { + if ( call.getKind().belongsTo( Kind.MQL_KIND ) && call.op.getOperatorName() == OperatorName.MQL_QUERY_VALUE ) { return call; } else if ( call.op.getKind() == Kind.ARRAY_VALUE_CONSTRUCTOR ) { int i = index.getAndIncrement(); diff --git a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentAggregate.java b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentAggregate.java index 4fcefca296..1b82ef988f 100644 --- a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentAggregate.java +++ b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentAggregate.java @@ -16,19 +16,19 @@ package org.polypheny.db.adapter.mongodb.rules; -import java.util.AbstractList; import java.util.ArrayList; import java.util.List; -import org.jetbrains.annotations.NotNull; import org.polypheny.db.adapter.mongodb.MongoAlg; import org.polypheny.db.algebra.AlgNode; -import org.polypheny.db.algebra.core.AggregateCall; +import org.polypheny.db.algebra.core.DocumentAggregateCall; import org.polypheny.db.algebra.core.document.DocumentAggregate; -import org.polypheny.db.algebra.fun.AggFunction; import org.polypheny.db.algebra.operators.OperatorName; +import org.polypheny.db.catalog.exceptions.GenericRuntimeException; import org.polypheny.db.languages.OperatorRegistry; import org.polypheny.db.plan.AlgOptCluster; import org.polypheny.db.plan.AlgTraitSet; +import org.polypheny.db.rex.RexNameRef; +import org.polypheny.db.rex.RexNode; import org.polypheny.db.schema.trait.ModelTrait; import org.polypheny.db.sql.language.fun.SqlSingleValueAggFunction; import org.polypheny.db.sql.language.fun.SqlSumAggFunction; @@ -41,17 +41,13 @@ public class MongoDocumentAggregate extends DocumentAggregate implements MongoAl * Creates a {@link DocumentAggregate}. * {@link ModelTrait#DOCUMENT} native node of an aggregate. * - * @param cluster - * @param traits - * @param child - * @param indicator - * @param groupSet - * @param groupSets - * @param aggCalls - * @param names + * @param cluster Cluster that this relational expression belongs to + * @param traits Traits of this expression + * @param child Input of this expression + * @param aggCalls Aggregate calls */ - protected MongoDocumentAggregate( AlgOptCluster cluster, AlgTraitSet traits, AlgNode child, boolean indicator, @NotNull List groupSet, List> groupSets, List aggCalls, List names ) { - super( cluster, traits, child, indicator, groupSet, groupSets, aggCalls, names ); + protected MongoDocumentAggregate( AlgOptCluster cluster, AlgTraitSet traits, AlgNode child, RexNode group, List aggCalls ) { + super( cluster, traits, child, group, aggCalls ); } @@ -59,78 +55,56 @@ protected MongoDocumentAggregate( AlgOptCluster cluster, AlgTraitSet traits, Alg public void implement( Implementor implementor ) { implementor.visitChild( 0, getInput() ); List list = new ArrayList<>(); - final List inNames = MongoRules.mongoFieldNames( getInput().getRowType() ); - final List outNames = MongoRules.mongoFieldNames( getRowType() ); - int i = 0; - final String inName = groupSet.get( 0 ); + final String inName = group.unwrap( RexNameRef.class ).orElseThrow().name; list.add( "_id: " + MongoRules.maybeQuote( "$" + inName ) ); - implementor.physicalMapper.add( inName ); - ++i; + //implementor.physicalMapper.add( inName ); - for ( AggregateCall aggCall : aggCalls ) { - list.add( MongoRules.maybeQuote( outNames.get( i++ ) ) + ": " + toMongo( aggCall.getAggregation(), inNames, aggCall.getArgList(), implementor ) ); + for ( DocumentAggregateCall aggCall : aggCalls ) { + list.add( MongoRules.maybeQuote( aggCall.name ) + ": " + toMongo( aggCall ) ); } implementor.add( null, "{$group: " + Util.toString( list, "{", ", ", "}" ) + "}" ); - final List fixups; - - fixups = new AbstractList<>() { - @Override - public String get( int index ) { - final String outName = outNames.get( index ); - return MongoRules.maybeQuote( outName ) + ": " + MongoRules.maybeQuote( "$" + (index == 0 ? "_id" : outName) ); - } - - - @Override - public int size() { - return outNames.size(); - } - }; - if ( !groupSet.isEmpty() ) { - implementor.add( null, "{$project: " + Util.toString( fixups, "{", ", ", "}" ) + "}" ); - } } - private String toMongo( AggFunction aggregation, List inNames, List args, Implementor implementor ) { - if ( aggregation.getOperatorName() == OperatorName.COUNT ) { - if ( args.size() == 0 ) { + private String toMongo( DocumentAggregateCall aggCall ) { + if ( aggCall.function.getOperatorName() == OperatorName.COUNT ) { + if ( aggCall.getInput().isEmpty() ) { return "{$sum: 1}"; } else { - assert args.size() == 1; - final String inName = inNames.get( args.get( 0 ) ); - implementor.physicalMapper.add( inName ); + assert aggCall.getInput().get().unwrap( RexNameRef.class ).orElseThrow().getNames().size() == 1; + final String inName = ((RexNameRef) aggCall.getInput().get()).name; return "{$sum: {$cond: [ {$eq: [" + MongoRules.quote( inName ) + ", null]}, 0, 1]}}"; } - } else if ( aggregation instanceof SqlSumAggFunction || aggregation instanceof SqlSumEmptyIsZeroAggFunction ) { - assert args.size() == 1; - final String inName = inNames.get( args.get( 0 ) ); - implementor.physicalMapper.add( inName ); + } else if ( aggCall.function instanceof SqlSumAggFunction || aggCall.function instanceof SqlSumEmptyIsZeroAggFunction ) { + assert aggCall.getInput().get().unwrap( RexNameRef.class ).orElseThrow().getNames().size() == 1; + final String inName = ((RexNameRef) aggCall.getInput().get()).name; return "{$sum: " + MongoRules.maybeQuote( "$" + inName ) + "}"; - } else if ( aggregation.getOperatorName() == OperatorName.MIN ) { - assert args.size() == 1; - final String inName = inNames.get( args.get( 0 ) ); - implementor.physicalMapper.add( inName ); + } else if ( aggCall.function.getOperatorName() == OperatorName.MIN ) { + assert aggCall.getInput().get().unwrap( RexNameRef.class ).orElseThrow().getNames().size() == 1; + final String inName = ((RexNameRef) aggCall.getInput().get()).name; return "{$min: " + MongoRules.maybeQuote( "$" + inName ) + "}"; - } else if ( aggregation.equals( OperatorRegistry.getAgg( OperatorName.MAX ) ) ) { - assert args.size() == 1; - final String inName = inNames.get( args.get( 0 ) ); - implementor.physicalMapper.add( inName ); + } else if ( aggCall.function.equals( OperatorRegistry.getAgg( OperatorName.MAX ) ) ) { + assert aggCall.getInput().get().unwrap( RexNameRef.class ).orElseThrow().getNames().size() == 1; + final String inName = ((RexNameRef) aggCall.getInput().get()).name; return "{$max: " + MongoRules.maybeQuote( "$" + inName ) + "}"; - } else if ( aggregation.getOperatorName() == OperatorName.AVG || aggregation.getKind() == OperatorRegistry.getAgg( OperatorName.AVG ).getKind() ) { - assert args.size() == 1; - final String inName = inNames.get( args.get( 0 ) ); - implementor.physicalMapper.add( inName ); + } else if ( aggCall.function.getOperatorName() == OperatorName.AVG || aggCall.function.getKind() == OperatorRegistry.getAgg( OperatorName.AVG ).getKind() ) { + assert aggCall.getInput().get().unwrap( RexNameRef.class ).orElseThrow().getNames().size() == 1; + final String inName = ((RexNameRef) aggCall.getInput().get()).name; return "{$avg: " + MongoRules.maybeQuote( "$" + inName ) + "}"; - } else if ( aggregation instanceof SqlSingleValueAggFunction ) { - assert args.size() == 1; - final String inName = inNames.get( args.get( 0 ) ); - implementor.physicalMapper.add( inName ); + } else if ( aggCall.function instanceof SqlSingleValueAggFunction ) { + assert aggCall.getInput().get().unwrap( RexNameRef.class ).orElseThrow().getNames().size() == 1; + final String inName = ((RexNameRef) aggCall.getInput().get()).name; return "{$sum:" + MongoRules.maybeQuote( "$" + inName ) + "}"; } else { - throw new AssertionError( "unknown aggregate " + aggregation ); + throw new GenericRuntimeException( "unknown aggregate " + aggCall.function ); } } + + @Override + public AlgNode copy( AlgTraitSet traitSet, List inputs ) { + return new MongoDocumentAggregate( getCluster(), traitSet, sole( inputs ), group, aggCalls ); + } + } diff --git a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentModify.java b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentModify.java index ec147b4f59..23a6ce425c 100644 --- a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentModify.java +++ b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentModify.java @@ -16,7 +16,6 @@ package org.polypheny.db.adapter.mongodb.rules; -import com.mongodb.client.gridfs.GridFSBucket; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -34,9 +33,6 @@ public class MongoDocumentModify extends DocumentModify implements MongoAlg { - private final GridFSBucket bucket; - private Implementor implementor; - protected MongoDocumentModify( AlgTraitSet traits, @@ -47,14 +43,12 @@ protected MongoDocumentModify( List removes, Map renames ) { super( traits, collection, input, operation, updates, removes, renames ); - this.bucket = entity.getMongoNamespace().getBucket(); } @Override public void implement( Implementor implementor ) { implementor.setDML( true ); - this.implementor = implementor; implementor.setEntity( entity ); implementor.setOperation( this.getOperation() ); @@ -102,7 +96,7 @@ private void handleInsert( Implementor implementor, MongoDocuments documents ) { implementor.operations = documents.documents .stream() .filter( PolyValue::isDocument ) - .map( d -> BsonDocument.parse( d.toTypedJson() ) ) + .map( d -> BsonDocument.parse( d.toJson() ) ) .collect( Collectors.toList() ); } diff --git a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentProject.java b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentProject.java index b3b1cd6f94..d69db68f6e 100644 --- a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentProject.java +++ b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoDocumentProject.java @@ -22,8 +22,10 @@ import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; import org.polypheny.db.adapter.mongodb.MongoAlg; +import org.polypheny.db.adapter.mongodb.util.RexToMongoTranslator; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.core.document.DocumentProject; +import org.polypheny.db.catalog.logistic.DataModel; import org.polypheny.db.plan.AlgOptCluster; import org.polypheny.db.plan.AlgTraitSet; import org.polypheny.db.rex.RexNode; @@ -52,9 +54,9 @@ public void implement( Implementor implementor ) { implementor.visitChild( 0, getInput() ); List> projects = new ArrayList<>(); - final MongoRules.RexToMongoTranslator translator = new MongoRules.RexToMongoTranslator( getCluster().getTypeFactory(), MongoRules.mongoFieldNames( getInput().getRowType() ), implementor ); + final RexToMongoTranslator translator = new RexToMongoTranslator( getCluster().getTypeFactory(), List.of(), implementor, DataModel.DOCUMENT ); includes.forEach( ( n, p ) -> projects.add( Pair.of( n, p.accept( translator ) ) ) ); - excludes.forEach( n -> projects.add( Pair.of( n, "1" ) ) ); + excludes.forEach( n -> projects.add( Pair.of( n, "0" ) ) ); String merged = projects.stream().map( p -> "\"" + p.left + "\":" + p.right ).collect( Collectors.joining( "," ) ); @@ -62,4 +64,10 @@ public void implement( Implementor implementor ) { } + + @Override + public AlgNode copy( AlgTraitSet traitSet, List inputs ) { + return new MongoDocumentProject( getCluster(), traitSet, sole( inputs ), includes, excludes ); + } + } diff --git a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoProject.java b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoProject.java index 3f975836cb..cc7cddd4cc 100644 --- a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoProject.java +++ b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoProject.java @@ -28,6 +28,7 @@ import org.bson.json.JsonWriterSettings; import org.polypheny.db.adapter.mongodb.MongoAlg; import org.polypheny.db.adapter.mongodb.bson.BsonFunctionHelper; +import org.polypheny.db.adapter.mongodb.util.RexToMongoTranslator; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.constant.Kind; import org.polypheny.db.algebra.core.Project; @@ -35,6 +36,7 @@ import org.polypheny.db.algebra.type.AlgDataType; import org.polypheny.db.algebra.type.AlgDataTypeFactory; import org.polypheny.db.algebra.type.AlgDataTypeField; +import org.polypheny.db.catalog.logistic.DataModel; import org.polypheny.db.plan.AlgOptCluster; import org.polypheny.db.plan.AlgOptCost; import org.polypheny.db.plan.AlgOptPlanner; @@ -90,7 +92,7 @@ public AlgOptCost computeSelfCost( AlgOptPlanner planner, AlgMetadataQuery mq ) public void implement( Implementor implementor ) { implementor.visitChild( 0, getInput() ); - final MongoRules.RexToMongoTranslator translator = new MongoRules.RexToMongoTranslator( getCluster().getTypeFactory(), MongoRules.mongoFieldNames( getInput().getRowType() ), implementor ); + final RexToMongoTranslator translator = new RexToMongoTranslator( getCluster().getTypeFactory(), MongoRules.mongoFieldNames( getInput().getRowType() ), implementor, DataModel.RELATIONAL ); final List items = new ArrayList<>(); final List excludes = new ArrayList<>(); final List unwinds = new ArrayList<>(); diff --git a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoRules.java b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoRules.java index c25be286e4..9a28e5319f 100644 --- a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoRules.java +++ b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoRules.java @@ -17,19 +17,14 @@ package org.polypheny.db.adapter.mongodb.rules; import java.util.AbstractList; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; import lombok.Getter; -import org.bson.BsonArray; import org.bson.BsonString; import org.bson.BsonValue; import org.polypheny.db.adapter.mongodb.MongoAlg; -import org.polypheny.db.adapter.mongodb.MongoAlg.Implementor; import org.polypheny.db.adapter.mongodb.MongoConvention; import org.polypheny.db.adapter.mongodb.MongoEntity; import org.polypheny.db.adapter.mongodb.bson.BsonDynamic; @@ -44,13 +39,12 @@ import org.polypheny.db.algebra.core.AlgFactories; import org.polypheny.db.algebra.core.Sort; import org.polypheny.db.algebra.core.Values; +import org.polypheny.db.algebra.core.document.DocumentAggregate; import org.polypheny.db.algebra.core.document.DocumentModify; import org.polypheny.db.algebra.core.document.DocumentSort; import org.polypheny.db.algebra.core.document.DocumentValues; import org.polypheny.db.algebra.core.relational.RelModify; import org.polypheny.db.algebra.core.relational.RelScan; -import org.polypheny.db.algebra.enumerable.RexImpTable; -import org.polypheny.db.algebra.enumerable.RexToLixTranslator; import org.polypheny.db.algebra.logical.document.LogicalDocumentAggregate; import org.polypheny.db.algebra.logical.document.LogicalDocumentFilter; import org.polypheny.db.algebra.logical.document.LogicalDocumentProject; @@ -59,8 +53,7 @@ import org.polypheny.db.algebra.logical.relational.LogicalProject; import org.polypheny.db.algebra.operators.OperatorName; import org.polypheny.db.algebra.type.AlgDataType; -import org.polypheny.db.algebra.type.AlgDataTypeFactory; -import org.polypheny.db.languages.OperatorRegistry; +import org.polypheny.db.catalog.logistic.DataModel; import org.polypheny.db.nodes.Operator; import org.polypheny.db.plan.AlgOptCluster; import org.polypheny.db.plan.AlgOptRule; @@ -82,7 +75,6 @@ import org.polypheny.db.type.entity.document.PolyDocument; import org.polypheny.db.util.Pair; import org.polypheny.db.util.UnsupportedRexCallVisitor; -import org.polypheny.db.util.Util; import org.polypheny.db.util.ValidatorUtil; import org.polypheny.db.util.trace.PolyphenyDbTrace; import org.slf4j.Logger; @@ -121,7 +113,7 @@ private MongoRules() { /** * Returns 'string' if it is a call to item['string'], null otherwise. */ - static String isItem( RexCall call ) { + public static String isItem( RexCall call ) { if ( call.getOperator().getOperatorName() != OperatorName.ITEM ) { return null; } @@ -161,7 +153,7 @@ public int size() { } - static String maybeQuote( String s ) { + public static String maybeQuote( String s ) { if ( !needsQuote( s ) ) { return s; } @@ -224,288 +216,14 @@ public static BsonValue translateDocValue( AlgDataType rowType, RexCall call, St return new BsonDynamic( (RexDynamicParam) call.operands.get( 1 ) ).setIsValue( true, prefix + rowType.getFieldNames().get( parent.getIndex() ) ); } RexCall names = (RexCall) call.operands.get( 1 ); - if ( names.operands.isEmpty() ) { - return new BsonString( prefix + rowType.getFieldNames().get( parent.getIndex() ) ); - } - return new BsonString( prefix + rowType.getFieldNames().get( parent.getIndex() ) - + "." - + names.operands + return new BsonString( prefix + names.operands .stream() .map( n -> ((RexLiteral) n).value.asString().value ) .collect( Collectors.joining( "." ) ) ); } - /** - * Translator from {@link RexNode} to strings in MongoDB's expression language. - */ - static class RexToMongoTranslator extends RexVisitorImpl { - - private final AlgDataTypeFactory typeFactory; - private final List inFields; - - static final Map MONGO_OPERATORS = new HashMap<>(); - - - static { - // Arithmetic - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.DIVIDE ), "$divide" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.MULTIPLY ), "$multiply" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.MOD ), "$mod" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.PLUS ), "$add" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.MINUS ), "$subtract" ); - // Boolean - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.AND ), "$and" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.OR ), "$or" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.NOT ), "$not" ); - // Comparison - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.EQUALS ), "$eq" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.NOT_EQUALS ), "$ne" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.GREATER_THAN ), "$gt" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.GREATER_THAN_OR_EQUAL ), "$gte" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.LESS_THAN ), "$lt" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.LESS_THAN_OR_EQUAL ), "$lte" ); - - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.FLOOR ), "$floor" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.CEIL ), "$ceil" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.EXP ), "$exp" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.LN ), "$ln" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.LOG10 ), "$log10" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ABS ), "$abs" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.CHAR_LENGTH ), "$strLenCP" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.SUBSTRING ), "$substrCP" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ROUND ), "$round" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ACOS ), "$acos" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.TAN ), "$tan" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.COS ), "$cos" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ASIN ), "$asin" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.SIN ), "$sin" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ATAN ), "$atan" ); - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ATAN2 ), "$atan2" ); - - MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.POWER ), "$pow" ); - } - - - private final Implementor implementor; - - - protected RexToMongoTranslator( AlgDataTypeFactory typeFactory, List inFields, Implementor implementor ) { - super( true ); - this.implementor = implementor; - this.typeFactory = typeFactory; - this.inFields = inFields; - } - - - @Override - public String visitLiteral( RexLiteral literal ) { - if ( literal.getValue() == null ) { - return "null"; - } - return "{$literal: " + RexToLixTranslator.translateLiteral( literal, literal.getType(), typeFactory, RexImpTable.NullAs.NOT_POSSIBLE ) + "}"; - } - - - @Override - public String visitDynamicParam( RexDynamicParam dynamicParam ) { - return new BsonDynamic( dynamicParam ).toJson(); - } - - - @Override - public String visitIndexRef( RexIndexRef inputRef ) { - implementor.physicalMapper.add( inFields.get( inputRef.getIndex() ) ); - return maybeQuote( "$" + inFields.get( inputRef.getIndex() ) ); - } - - - @Override - public String visitCall( RexCall call ) { - String name = isItem( call ); - if ( name != null ) { - return "'$" + name + "'"; - } - final List strings = translateList( call.operands ); - if ( call.getKind() == Kind.CAST ) { - return strings.get( 0 ); - } - String stdOperator = MONGO_OPERATORS.get( call.getOperator() ); - if ( stdOperator != null ) { - if ( call.getOperator().equals( OperatorRegistry.get( OperatorName.SUBSTRING ) ) ) { - String first = strings.get( 1 ); - first = "{\"$subtract\":[" + first + ", 1]}"; - strings.remove( 1 ); - strings.add( first ); - if ( call.getOperands().size() == 2 ) { - strings.add( " { \"$strLenCP\":" + strings.get( 0 ) + "}" ); - } - } - return "{" + stdOperator + ": [" + Util.commaList( strings ) + "]}"; - } - if ( call.getOperator().getOperatorName() == OperatorName.ITEM ) { - final RexNode op1 = call.operands.get( 1 ); - // normal - if ( op1 instanceof RexLiteral && op1.getType(). - - getPolyType() == PolyType.INTEGER ) { - return "{$arrayElemAt:[" + strings.get( 0 ) + "," + (((RexLiteral) op1).value.asNumber().intValue() - 1) + "]}"; - } - // prepared - if ( op1 instanceof RexDynamicParam ) { - return "{$arrayElemAt:[" + strings.get( 0 ) + ", {$subtract:[" + new BsonDynamic( (RexDynamicParam) op1 ).toJson() + ", 1]}]}"; - } - } - if ( call.getOperator().equals( OperatorRegistry.get( OperatorName.CASE ) ) ) { - StringBuilder sb = new StringBuilder(); - StringBuilder finish = new StringBuilder(); - // case(a, b, c) -> $cond:[a, b, c] - // case(a, b, c, d) -> $cond:[a, b, $cond:[c, d, null]] - // case(a, b, c, d, e) -> $cond:[a, b, $cond:[c, d, e]] - for ( - int i = 0; i < strings.size(); i += 2 ) { - sb.append( "{$cond:[" ); - finish.append( "]}" ); - - sb.append( strings.get( i ) ); - sb.append( ',' ); - sb.append( strings.get( i + 1 ) ); - sb.append( ',' ); - if ( i == strings.size() - 3 ) { - sb.append( strings.get( i + 2 ) ); - break; - } - if ( i == strings.size() - 2 ) { - sb.append( "null" ); - break; - } - } - sb.append( finish ); - return sb.toString(); - } - if ( call.op.equals( OperatorRegistry.get( OperatorName.UNARY_MINUS ) ) ) { - if ( strings.size() == 1 ) { - return "{\"$multiply\":[" + strings.get( 0 ) + ",-1]}"; - } - } - - String special = handleSpecialCases( call ); - if ( special != null ) { - return special; - } - /*if ( call.op.getOperatorName() == OperatorName.MQL ) { - return call.operands.get( 0 ).accept( this ); - }*/ - - if ( call.op.getOperatorName() == OperatorName.SIGN ) { - // x < 0, -1 - // x == 0, 0 - // x > 0, 1 - StringBuilder sb = new StringBuilder(); - String oper = call.operands.get( 0 ).accept( this ); - sb.append( "{\"$switch\":\n" - + " {\n" - + " \"branches\": [\n" - + " {\n" - + " \"case\": { \"$lt\" : [ " ); - sb.append( oper ); - sb.append( ", 0 ] },\n" - + " \"then\": -1.0" - + " },\n" - + " {\n" - + " \"case\": { \"$gt\" : [ " ); - sb.append( oper ); - sb.append( ", 0 ] },\n" - + " \"then\": 1.0" - + " },\n" - + " ],\n" - + " \"default\": 0.0" - + " }}" ); - - return sb.toString(); - } - - if ( call.op.equals( OperatorRegistry.get( OperatorName.IS_NOT_NULL ) ) ) { - return call.operands.get( 0 ). - - accept( this ); - - } - - throw new IllegalArgumentException( "Translation of " + call + " is not supported by MongoProject" ); - - } - - - public String handleSpecialCases( RexCall call ) { - if ( call.getType().getPolyType() == PolyType.ARRAY ) { - BsonArray array = new BsonArray(); - array.addAll( translateList( call.operands ).stream().map( BsonString::new ).collect( Collectors.toList() ) ); - return array.toString(); - } else if ( call.isA( Kind.MQL_QUERY_VALUE ) ) { - return translateDocValueAsKey( implementor.getRowType(), call, "$" ); - } else if ( call.isA( Kind.MQL_ITEM ) ) { - RexNode leftPre = call.operands.get( 0 ); - String left = leftPre.accept( this ); - - String right = call.operands.get( 1 ).accept( this ); - - return "{\"$arrayElemAt\":[" + left + "," + right + "]}"; - } else if ( call.isA( Kind.MQL_SLICE ) ) { - String left = call.operands.get( 0 ).accept( this ); - String skip = call.operands.get( 1 ).accept( this ); - String return_ = call.operands.get( 2 ).accept( this ); - - return "{\"$slice\":[ " + left + "," + skip + "," + return_ + "]}"; - } else if ( call.isA( Kind.MQL_EXCLUDE ) ) { - String parent = implementor - .getRowType() - .getFieldNames() - .get( ((RexIndexRef) call.operands.get( 0 )).getIndex() ); - - if ( !(call.operands.get( 1 ) instanceof RexCall) || call.operands.size() != 2 ) { - return null; - } - RexCall excludes = (RexCall) call.operands.get( 1 ); - List fields = new ArrayList<>(); - for ( RexNode operand : excludes.operands ) { - if ( !(operand instanceof RexCall) ) { - return null; - } - fields.add( "\"" + parent + "." + ((RexCall) operand) - .operands - .stream() - .map( op -> ((RexLiteral) op).value.asString().value ) - .collect( Collectors.joining( "." ) ) + "\": 0" ); - } - - return String.join( ",", fields ); - } else if ( call.isA( Kind.UNWIND ) ) { - return call.operands.get( 0 ).accept( this ); - } - return null; - } - - - private String stripQuotes( String s ) { - return s.startsWith( "'" ) && s.endsWith( "'" ) - ? s.substring( 1, s.length() - 1 ) - : s; - } - - - public List translateList( List list ) { - final List strings = new ArrayList<>(); - for ( RexNode node : list ) { - strings.add( node.accept( this ) ); - } - return strings; - } - - } - - /** * Base class for planner rules that convert a relational expression to MongoDB calling convention. */ @@ -514,7 +232,7 @@ abstract static class MongoConverterRule extends ConverterRule { protected final Convention out; - MongoConverterRule( Class clazz, Predicate supports, AlgTrait in, Convention out, String description ) { + MongoConverterRule( Class clazz, Predicate supports, AlgTrait in, Convention out, String description ) { super( clazz, supports, in, out, AlgFactories.LOGICAL_BUILDER, description ); this.out = out; } @@ -594,7 +312,7 @@ private MongoFilterRule() { project -> MongoConvention.mapsDocuments || !DocumentRules.containsDocument( project ), Convention.NONE, MongoAlg.CONVENTION, - "MongoFilterRule" ); + MongoFilterRule.class.getSimpleName() ); } @@ -623,7 +341,7 @@ private MongoDocumentFilterRule() { project -> MongoConvention.mapsDocuments || !DocumentRules.containsDocument( project ), Convention.NONE, MongoAlg.CONVENTION, - "MongoDocumentFilterRule" ); + MongoDocumentFilterRule.class.getSimpleName() ); } @@ -761,7 +479,7 @@ public static class MongoValuesRule extends MongoConverterRule { private MongoValuesRule() { - super( Values.class, r -> true, Convention.NONE, MongoAlg.CONVENTION, "MongoValuesRule." + MongoAlg.CONVENTION ); + super( Values.class, r -> true, Convention.NONE, MongoAlg.CONVENTION, MongoValuesRule.class.getSimpleName() ); } @@ -785,7 +503,7 @@ public static class MongoDocumentsRule extends MongoConverterRule { private MongoDocumentsRule() { - super( DocumentValues.class, r -> true, Convention.NONE, MongoAlg.CONVENTION, "MongoDocumentRule." + MongoAlg.CONVENTION ); + super( DocumentValues.class, r -> true, Convention.NONE, MongoAlg.CONVENTION, MongoDocumentsRule.class.getSimpleName() ); } @@ -817,6 +535,12 @@ public void implement( Implementor implementor ) { // empty on purpose } + + @Override + public AlgNode copy( AlgTraitSet traitSet, List inputs ) { + return new MongoDocuments( getCluster(), documents, traitSet ); + } + } @@ -826,7 +550,7 @@ private static class MongoTableModificationRule extends MongoConverterRule { MongoTableModificationRule() { - super( RelModify.class, MongoTableModificationRule::mongoSupported, Convention.NONE, MongoAlg.CONVENTION, "MongoTableModificationRule." + MongoAlg.CONVENTION ); + super( RelModify.class, MongoTableModificationRule::mongoSupported, Convention.NONE, MongoAlg.CONVENTION, MongoTableModificationRule.class.getSimpleName() ); } @@ -938,12 +662,13 @@ private static class MongoAggregateRule extends MongoConverterRule { private MongoAggregateRule() { super( LogicalAggregate.class, MongoAggregateRule::supported, Convention.NONE, MongoAlg.CONVENTION, - "MongoAggregateRule" ); + MongoAggregateRule.class.getSimpleName() ); } private static boolean supported( LogicalAggregate aggregate ) { - return aggregate.getAggCallList().stream().noneMatch( AggregateCall::isDistinct ); + return aggregate.getAggCallList().stream().noneMatch( AggregateCall::isDistinct ) + && aggregate.getModel() != DataModel.DOCUMENT; } @@ -975,8 +700,8 @@ private static class MongoDocumentAggregateRule extends MongoConverterRule { private MongoDocumentAggregateRule() { - super( LogicalDocumentAggregate.class, r -> true, Convention.NONE, MongoAlg.CONVENTION, - MongoDocumentAggregate.class.getSimpleName() ); + super( DocumentAggregate.class, r -> true, Convention.NONE, MongoAlg.CONVENTION, + MongoDocumentAggregateRule.class.getSimpleName() ); } @@ -988,11 +713,8 @@ public AlgNode convert( AlgNode alg ) { alg.getCluster(), traitSet, convert( agg.getInput(), traitSet.simplify() ), - agg.indicator, - agg.groupSet, - agg.groupSets, - agg.aggCalls, - agg.names ); + agg.group, + agg.aggCalls ); } } diff --git a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoScan.java b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoScan.java index bf131f7219..a3f8194093 100644 --- a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoScan.java +++ b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/rules/MongoScan.java @@ -25,14 +25,16 @@ import org.polypheny.db.adapter.mongodb.MongoAlg; import org.polypheny.db.adapter.mongodb.MongoEntity; import org.polypheny.db.algebra.AlgNode; -import org.polypheny.db.algebra.core.relational.RelScan; +import org.polypheny.db.algebra.core.common.Scan; import org.polypheny.db.algebra.metadata.AlgMetadataQuery; import org.polypheny.db.algebra.type.AlgDataType; +import org.polypheny.db.catalog.logistic.DataModel; import org.polypheny.db.plan.AlgOptCluster; import org.polypheny.db.plan.AlgOptCost; import org.polypheny.db.plan.AlgOptPlanner; import org.polypheny.db.plan.AlgOptRule; import org.polypheny.db.plan.AlgTraitSet; +import org.polypheny.db.schema.trait.ModelTraitDef; import org.polypheny.db.util.Pair; @@ -41,7 +43,7 @@ * * Additional operations might be applied, using the "find" or "aggregate" methods.

*/ -public class MongoScan extends RelScan implements MongoAlg { +public class MongoScan extends Scan implements MongoAlg { @@ -54,6 +56,7 @@ public class MongoScan extends RelScan implements MongoAlg { */ public MongoScan( AlgOptCluster cluster, AlgTraitSet traitSet, MongoEntity table ) { super( cluster, traitSet, table ); + this.rowType = table.getRowType( cluster.getTypeFactory() ); assert getConvention() == CONVENTION; } @@ -83,11 +86,18 @@ public AlgOptCost computeSelfCost( AlgOptPlanner planner, AlgMetadataQuery mq ) @Override public void register( AlgOptPlanner planner ) { for ( AlgOptRule rule : MongoRules.RULES ) { - planner.addRule( rule ); + planner.addRuleDuringRuntime( rule ); } } + @Override + public String algCompareString() { + return this.getClass().getSimpleName() + "$" + + entity.id + "&"; + } + + @Override public void implement( Implementor implementor ) { implementor.setEntity( entity ); @@ -97,7 +107,9 @@ public void implement( Implementor implementor ) { if ( implementor.isDML() ) { return; } - implementor.list.add( Pair.of( null, new BsonDocument( "$project", new BsonDocument( rowType.getFields().stream().map( p -> new BsonElement( p.getName(), new BsonString( "$" + p.getPhysicalName() ) ) ).collect( Collectors.toList() ) ) ).toJson() ) ); + if ( traitSet.getTrait( ModelTraitDef.INSTANCE ).getDataModel() == DataModel.RELATIONAL ) { + implementor.list.add( Pair.of( null, new BsonDocument( "$project", new BsonDocument( rowType.getFields().stream().map( p -> new BsonElement( p.getName(), new BsonString( "$" + p.getPhysicalName() ) ) ).collect( Collectors.toList() ) ) ).toJson() ) ); + } } } diff --git a/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/util/RexToMongoTranslator.java b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/util/RexToMongoTranslator.java new file mode 100644 index 0000000000..f809ac37b6 --- /dev/null +++ b/plugins/mongodb-adapter/src/main/java/org/polypheny/db/adapter/mongodb/util/RexToMongoTranslator.java @@ -0,0 +1,317 @@ +/* + * Copyright 2019-2023 The Polypheny Project + * + * Licensed 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.polypheny.db.adapter.mongodb.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.bson.BsonArray; +import org.bson.BsonString; +import org.polypheny.db.adapter.mongodb.MongoAlg.Implementor; +import org.polypheny.db.adapter.mongodb.bson.BsonDynamic; +import org.polypheny.db.adapter.mongodb.rules.MongoRules; +import org.polypheny.db.algebra.constant.Kind; +import org.polypheny.db.algebra.enumerable.RexImpTable; +import org.polypheny.db.algebra.enumerable.RexToLixTranslator; +import org.polypheny.db.algebra.operators.OperatorName; +import org.polypheny.db.algebra.type.AlgDataTypeFactory; +import org.polypheny.db.catalog.logistic.DataModel; +import org.polypheny.db.languages.OperatorRegistry; +import org.polypheny.db.nodes.Operator; +import org.polypheny.db.rex.RexCall; +import org.polypheny.db.rex.RexDynamicParam; +import org.polypheny.db.rex.RexIndexRef; +import org.polypheny.db.rex.RexLiteral; +import org.polypheny.db.rex.RexNode; +import org.polypheny.db.rex.RexVisitorImpl; +import org.polypheny.db.type.PolyType; +import org.polypheny.db.util.Util; + +/** + * Translator from {@link RexNode} to strings in MongoDB's expression language. + */ +public class RexToMongoTranslator extends RexVisitorImpl { + + private final AlgDataTypeFactory typeFactory; + private final List inFields; + + static final Map MONGO_OPERATORS = new HashMap<>(); + + + static { + // Arithmetic + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.DIVIDE ), "$divide" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.MULTIPLY ), "$multiply" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.MOD ), "$mod" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.PLUS ), "$add" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.MINUS ), "$subtract" ); + // Boolean + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.AND ), "$and" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.OR ), "$or" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.NOT ), "$not" ); + // Comparison + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.EQUALS ), "$eq" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.NOT_EQUALS ), "$ne" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.GREATER_THAN ), "$gt" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.GREATER_THAN_OR_EQUAL ), "$gte" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.LESS_THAN ), "$lt" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.LESS_THAN_OR_EQUAL ), "$lte" ); + + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.FLOOR ), "$floor" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.CEIL ), "$ceil" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.EXP ), "$exp" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.LN ), "$ln" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.LOG10 ), "$log10" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ABS ), "$abs" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.CHAR_LENGTH ), "$strLenCP" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.SUBSTRING ), "$substrCP" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ROUND ), "$round" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ACOS ), "$acos" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.TAN ), "$tan" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.COS ), "$cos" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ASIN ), "$asin" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.SIN ), "$sin" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ATAN ), "$atan" ); + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.ATAN2 ), "$atan2" ); + + MONGO_OPERATORS.put( OperatorRegistry.get( OperatorName.POWER ), "$pow" ); + } + + + private final Implementor implementor; + private final DataModel model; + + + public RexToMongoTranslator( AlgDataTypeFactory typeFactory, List inFields, Implementor implementor, DataModel model ) { + super( true ); + this.implementor = implementor; + this.typeFactory = typeFactory; + this.inFields = inFields; + this.model = model; + } + + + @Override + public String visitLiteral( RexLiteral literal ) { + if ( literal.getValue() == null ) { + return "null"; + } + return "{$literal: " + RexToLixTranslator.translateLiteral( literal, literal.getType(), typeFactory, RexImpTable.NullAs.NOT_POSSIBLE ) + "}"; + } + + + @Override + public String visitDynamicParam( RexDynamicParam dynamicParam ) { + return new BsonDynamic( dynamicParam ).toJson(); + } + + + @Override + public String visitIndexRef( RexIndexRef inputRef ) { + if ( model == DataModel.DOCUMENT ) { + return "1"; + } + implementor.physicalMapper.add( inFields.get( inputRef.getIndex() ) ); + return MongoRules.maybeQuote( "$" + inFields.get( inputRef.getIndex() ) ); + } + + + @Override + public String visitCall( RexCall call ) { + String name = MongoRules.isItem( call ); + if ( name != null ) { + return "'$" + name + "'"; + } + final List strings = translateList( call.operands ); + if ( call.getKind() == Kind.CAST ) { + return strings.get( 0 ); + } + String stdOperator = MONGO_OPERATORS.get( call.getOperator() ); + if ( stdOperator != null ) { + if ( call.getOperator().equals( OperatorRegistry.get( OperatorName.SUBSTRING ) ) ) { + String first = strings.get( 1 ); + first = "{\"$subtract\":[" + first + ", 1]}"; + strings.remove( 1 ); + strings.add( first ); + if ( call.getOperands().size() == 2 ) { + strings.add( " { \"$strLenCP\":" + strings.get( 0 ) + "}" ); + } + } + return "{" + stdOperator + ": [" + Util.commaList( strings ) + "]}"; + } + if ( call.getOperator().getOperatorName() == OperatorName.ITEM ) { + final RexNode op1 = call.operands.get( 1 ); + // normal + if ( op1 instanceof RexLiteral && op1.getType(). + + getPolyType() == PolyType.INTEGER ) { + return "{$arrayElemAt:[" + strings.get( 0 ) + "," + (((RexLiteral) op1).value.asNumber().intValue() - 1) + "]}"; + } + // prepared + if ( op1 instanceof RexDynamicParam ) { + return "{$arrayElemAt:[" + strings.get( 0 ) + ", {$subtract:[" + new BsonDynamic( (RexDynamicParam) op1 ).toJson() + ", 1]}]}"; + } + } + if ( call.getOperator().equals( OperatorRegistry.get( OperatorName.CASE ) ) ) { + StringBuilder sb = new StringBuilder(); + StringBuilder finish = new StringBuilder(); + // case(a, b, c) -> $cond:[a, b, c] + // case(a, b, c, d) -> $cond:[a, b, $cond:[c, d, null]] + // case(a, b, c, d, e) -> $cond:[a, b, $cond:[c, d, e]] + for ( + int i = 0; i < strings.size(); i += 2 ) { + sb.append( "{$cond:[" ); + finish.append( "]}" ); + + sb.append( strings.get( i ) ); + sb.append( ',' ); + sb.append( strings.get( i + 1 ) ); + sb.append( ',' ); + if ( i == strings.size() - 3 ) { + sb.append( strings.get( i + 2 ) ); + break; + } + if ( i == strings.size() - 2 ) { + sb.append( "null" ); + break; + } + } + sb.append( finish ); + return sb.toString(); + } + if ( call.op.equals( OperatorRegistry.get( OperatorName.UNARY_MINUS ) ) ) { + if ( strings.size() == 1 ) { + return "{\"$multiply\":[" + strings.get( 0 ) + ",-1]}"; + } + } + + String special = handleSpecialCases( call ); + if ( special != null ) { + return special; + } + /*if ( call.op.getOperatorName() == OperatorName.MQL ) { + return call.operands.get( 0 ).accept( this ); + }*/ + + if ( call.op.getOperatorName() == OperatorName.SIGN ) { + // x < 0, -1 + // x == 0, 0 + // x > 0, 1 + StringBuilder sb = new StringBuilder(); + String oper = call.operands.get( 0 ).accept( this ); + sb.append( "{\"$switch\":\n" + + " {\n" + + " \"branches\": [\n" + + " {\n" + + " \"case\": { \"$lt\" : [ " ); + sb.append( oper ); + sb.append( ", 0 ] },\n" + + " \"then\": -1.0" + + " },\n" + + " {\n" + + " \"case\": { \"$gt\" : [ " ); + sb.append( oper ); + sb.append( ", 0 ] },\n" + + " \"then\": 1.0" + + " },\n" + + " ],\n" + + " \"default\": 0.0" + + " }}" ); + + return sb.toString(); + } + + if ( call.op.equals( OperatorRegistry.get( OperatorName.IS_NOT_NULL ) ) ) { + return call.operands.get( 0 ). + + accept( this ); + + } + + throw new IllegalArgumentException( "Translation of " + call + " is not supported by MongoProject" ); + + } + + + public String handleSpecialCases( RexCall call ) { + if ( call.getType().getPolyType() == PolyType.ARRAY ) { + BsonArray array = new BsonArray(); + array.addAll( translateList( call.operands ).stream().map( BsonString::new ).collect( Collectors.toList() ) ); + return array.toString(); + } else if ( call.isA( Kind.MQL_QUERY_VALUE ) ) { + return MongoRules.translateDocValueAsKey( implementor.getRowType(), call, "$" ); + } else if ( call.isA( Kind.MQL_ITEM ) ) { + RexNode leftPre = call.operands.get( 0 ); + String left = leftPre.accept( this ); + + String right = call.operands.get( 1 ).accept( this ); + + return "{\"$arrayElemAt\":[" + left + "," + right + "]}"; + } else if ( call.isA( Kind.MQL_SLICE ) ) { + String left = call.operands.get( 0 ).accept( this ); + String skip = call.operands.get( 1 ).accept( this ); + String return_ = call.operands.get( 2 ).accept( this ); + + return "{\"$slice\":[ " + left + "," + skip + "," + return_ + "]}"; + } else if ( call.isA( Kind.MQL_EXCLUDE ) ) { + String parent = implementor + .getRowType() + .getFieldNames() + .get( ((RexIndexRef) call.operands.get( 0 )).getIndex() ); + + if ( !(call.operands.get( 1 ) instanceof RexCall) || call.operands.size() != 2 ) { + return null; + } + RexCall excludes = (RexCall) call.operands.get( 1 ); + List fields = new ArrayList<>(); + for ( RexNode operand : excludes.operands ) { + if ( !(operand instanceof RexCall) ) { + return null; + } + fields.add( "\"" + parent + "." + ((RexCall) operand) + .operands + .stream() + .map( op -> ((RexLiteral) op).value.asString().value ) + .collect( Collectors.joining( "." ) ) + "\": 0" ); + } + + return String.join( ",", fields ); + } else if ( call.isA( Kind.UNWIND ) ) { + return call.operands.get( 0 ).accept( this ); + } + return null; + } + + + private String stripQuotes( String s ) { + return s.startsWith( "'" ) && s.endsWith( "'" ) + ? s.substring( 1, s.length() - 1 ) + : s; + } + + + public List translateList( List list ) { + final List strings = new ArrayList<>(); + for ( RexNode node : list ) { + strings.add( node.accept( this ) ); + } + return strings; + } + +} diff --git a/plugins/mql-language/src/main/java/org/polypheny/db/languages/mql2alg/MqlToAlgConverter.java b/plugins/mql-language/src/main/java/org/polypheny/db/languages/mql2alg/MqlToAlgConverter.java index 55c4e024d8..54a5caf4ef 100644 --- a/plugins/mql-language/src/main/java/org/polypheny/db/languages/mql2alg/MqlToAlgConverter.java +++ b/plugins/mql-language/src/main/java/org/polypheny/db/languages/mql2alg/MqlToAlgConverter.java @@ -44,8 +44,8 @@ import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.AlgRoot; import org.polypheny.db.algebra.constant.Kind; -import org.polypheny.db.algebra.core.AggregateCall; import org.polypheny.db.algebra.core.CorrelationId; +import org.polypheny.db.algebra.core.DocumentAggregateCall; import org.polypheny.db.algebra.core.Values; import org.polypheny.db.algebra.core.common.Modify; import org.polypheny.db.algebra.core.common.Modify.Operation; @@ -89,6 +89,7 @@ import org.polypheny.db.rex.RexCall; import org.polypheny.db.rex.RexIndexRef; import org.polypheny.db.rex.RexLiteral; +import org.polypheny.db.rex.RexNameRef; import org.polypheny.db.rex.RexNode; import org.polypheny.db.schema.document.DocumentUtil; import org.polypheny.db.schema.document.DocumentUtil.UpdateOperation; @@ -729,20 +730,9 @@ private AlgNode convertCount( MqlCount query, AlgDataType rowType, AlgNode node return LogicalDocumentAggregate.create( node, - List.of(), null, Collections.singletonList( - AggregateCall.create( - OperatorRegistry.getAgg( OperatorName.COUNT ), - false, - query.isEstimate(), - new ArrayList<>(), - -1, - AlgCollations.EMPTY, - cluster.getTypeFactory().createPolyType( PolyType.BIGINT ), - query.isEstimate() ? "estimatedCount" : "count" - ) ), - List.of( "count" ) ); + DocumentAggregateCall.create( query.isEstimate() ? "estimatedCount" : "count", OperatorRegistry.getAgg( OperatorName.COUNT ), null ) ) ); } @@ -1074,24 +1064,22 @@ private AlgNode combineGroup( BsonValue value, AlgNode node, AlgDataType rowType throw new GenericRuntimeException( "$group pipeline stage needs a document after, which defines a _id" ); } - List ops = new ArrayList<>(); - List nodes = new ArrayList<>(); - List names = new ArrayList<>(); - List aggNames = new ArrayList<>(); + Map nameOps = new HashMap<>(); + //List aggNames = new ArrayList<>(); + Map nameNodes = new HashMap<>(); + //List names = new ArrayList<>(); for ( Entry entry : value.asDocument().entrySet() ) { if ( entry.getKey().equals( "_id" ) ) { if ( entry.getValue().isNull() ) { - names.addAll( 0, rowType.getFieldNames() ); - nodes.addAll( 0, rowType.getFields().stream().map( f -> RexIndexRef.of( f.getIndex(), rowType ) ).collect( Collectors.toList() ) ); - + nameNodes.put( "_id", new RexLiteral( null, nullableAny, PolyType.NULL ) ); } else if ( entry.getValue().isString() ) { - names.add( entry.getValue().asString().getValue().substring( 1 ) ); - nodes.add( getIdentifier( entry.getValue().asString().getValue().substring( 1 ), rowType ) ); + nameNodes.put( "_id", getIdentifier( entry.getValue().asString().getValue().substring( 1 ), rowType ) ); } else if ( entry.getValue().isDocument() ) { for ( Entry idEntry : entry.getValue().asDocument().entrySet() ) { - names.add( idEntry.getValue().asString().getValue().substring( 1 ) ); - nodes.add( getIdentifier( idEntry.getValue().asString().getValue().substring( 1 ), rowType ) ); + nameNodes.put( idEntry.getValue().asString().getValue().substring( 0 ), getIdentifier( idEntry.getValue().asString().getValue().substring( 1 ), rowType ) ); + //names.add( idEntry.getValue().asString().getValue().substring( 1 ) ); + //nodes.add( getIdentifier( idEntry.getValue().asString().getValue().substring( 1 ), rowType ) ); } } else { throw new GenericRuntimeException( "$group takes as _id values either a document or a string" ); @@ -1101,20 +1089,21 @@ private AlgNode combineGroup( BsonValue value, AlgNode node, AlgDataType rowType throw new GenericRuntimeException( "$group needs a document with an accumulator and an expression" ); } BsonDocument doc = entry.getValue().asDocument(); - ops.add( accumulators.get( doc.getFirstKey() ) ); - aggNames.add( entry.getKey() ); - names.add( entry.getKey() ); + nameOps.put( entry.getKey(), accumulators.get( doc.getFirstKey() ) ); + // ops.add( accumulators.get( doc.getFirstKey() ) ); + //aggNames.add( entry.getKey() ); + //names.add( entry.getKey() ); AlgDataType nullableDouble = cluster.getTypeFactory().createTypeWithNullability( cluster.getTypeFactory().createPolyType( PolyType.DOUBLE ), true ); // when using aggregations MongoQl automatically casts to doubles - nodes.add( cluster.getRexBuilder().makeAbstractCast( - nullableDouble, - convertExpression( doc.get( doc.getFirstKey() ), rowType ) ) ); + String key = doc.get( doc.getFirstKey() ).asString().getValue().substring( 1 ); + //nodes.add( new RexNameRef( key, nullableDouble ) ); + nameNodes.put( entry.getKey(), new RexNameRef( key, nullableDouble ) ); } } - node = LogicalDocumentProject.create( node, nodes, names ); + //node = LogicalDocumentProject.create( node, nodes, names ); - return groupBy( value, node, node.getRowType(), aggNames, ops ); + return groupBy( value, nameNodes, node, node.getRowType(), nameOps ); } @@ -1136,25 +1125,18 @@ private RexNode convertExpression( BsonValue value, AlgDataType rowType ) { } - private AlgNode groupBy( BsonValue value, AlgNode node, AlgDataType rowType, List names, List aggs ) { + private AlgNode groupBy( BsonValue value, Map nameNodes, AlgNode node, AlgDataType rowType, Map nameOps ) { BsonValue groupBy = value.asDocument().get( "_id" ); - List convertedAggs = new ArrayList<>(); - int pos = 0; - for ( String name : names ) { + List convertedAggs = new ArrayList<>(); + + for ( Entry agg : nameOps.entrySet() ) { convertedAggs.add( - AggregateCall.create( - aggs.get( pos ), - false, - false, - Collections.singletonList( 1 + pos ), // first is original - -1, - AlgCollations.EMPTY, - // when using aggregations MongoQl automatically casts to doubles - cluster.getTypeFactory().createTypeWithNullability( cluster.getTypeFactory().createPolyType( PolyType.DOUBLE ), true ), - name ) ); - pos++; + DocumentAggregateCall.create( + agg.getKey(), + agg.getValue(), + nameNodes.get( agg.getKey() ) ) ); } if ( !groupBy.isNull() ) { @@ -1163,17 +1145,13 @@ private AlgNode groupBy( BsonValue value, AlgNode node, AlgDataType rowType, Lis node = LogicalDocumentAggregate.create( node, - List.of( groupName ), - null, - convertedAggs, - names ); + new RexNameRef( groupName, DocumentType.ofDoc() ), + convertedAggs ); } else { node = LogicalDocumentAggregate.create( node, - List.of(), null, - convertedAggs, - names ); + convertedAggs ); } return node; @@ -1196,19 +1174,9 @@ private AlgNode combineCount( BsonValue value, AlgNode node ) { } return LogicalDocumentAggregate.create( node, - List.of(), null, Collections.singletonList( - AggregateCall.create( - OperatorRegistry.getAgg( OperatorName.COUNT ), - false, - false, - new ArrayList<>(), - -1, - AlgCollations.EMPTY, - cluster.getTypeFactory().createPolyType( PolyType.BIGINT ), - value.asString().getValue() - ) ), List.of( "count" ) ); + DocumentAggregateCall.create( value.asString().getValue(), OperatorRegistry.getAgg( OperatorName.COUNT ), null ) ) ); } @@ -2192,11 +2160,11 @@ private AlgNode combineProjection( BsonValue projectionValue, AlgNode node, AlgD throw new GenericRuntimeException( "The provided projection was not translatable" ); } - if ( includes.size() != 0 && excludes.size() != 0 ) { + if ( !includes.isEmpty() && !excludes.isEmpty() ) { throw new GenericRuntimeException( "Include projection and exclude projections are not possible at the same time." ); } - if ( excludes.size() > 0 ) { + if ( !excludes.isEmpty() ) { if ( _dataExists ) { return LogicalDocumentProject.create( node, includes, excludes ); @@ -2242,7 +2210,7 @@ private AlgNode combineProjection( BsonValue projectionValue, AlgNode node, AlgD } return node; - } else if ( includes.size() > 0 ) { + } else if ( !includes.isEmpty() ) { if ( !includes.containsKey( DocumentType.DOCUMENT_ID ) ) { includes.put( DocumentType.DOCUMENT_ID, getIdentifier( DocumentType.DOCUMENT_ID, rowType ) ); @@ -2338,5 +2306,4 @@ private void translateProjection( AlgDataType rowType, boolean isAddFields, bool } - } diff --git a/webui/src/main/java/org/polypheny/db/webui/crud/LanguageCrud.java b/webui/src/main/java/org/polypheny/db/webui/crud/LanguageCrud.java index c695381b47..8c25017880 100644 --- a/webui/src/main/java/org/polypheny/db/webui/crud/LanguageCrud.java +++ b/webui/src/main/java/org/polypheny/db/webui/crud/LanguageCrud.java @@ -358,28 +358,30 @@ public static List computeResultData( final List> rows public static ResultBuilder getGraphResult( ExecutedContext context, UIRequest request, Statement statement ) { ResultIterator iterator = context.getIterator(); - List data = iterator.getArrayRows(); + List data; try { + data = iterator.getArrayRows(); + iterator.close(); - } catch ( Exception e ) { - throw new GenericRuntimeException( e ); - } - GraphResultBuilder builder = GraphResult.builder() - .data( data.stream().map( r -> Arrays.stream( r ).map( LanguageCrud::toJson ).toArray( String[]::new ) ).toArray( String[][]::new ) ) - .header( context.getIterator().getImplementation().rowType.getFields().stream().map( FieldDefinition::of ).toArray( FieldDefinition[]::new ) ) - .query( context.getQuery().getQuery() ) - .language( context.getQuery().getLanguage() ) - .dataModel( context.getIterator().getImplementation().getDataModel() ) - .affectedTuples( data.size() ) - .xid( statement.getTransaction().getXid().toString() ) - .namespace( request.namespace ); + GraphResultBuilder builder = GraphResult.builder() + .data( data.stream().map( r -> Arrays.stream( r ).map( LanguageCrud::toJson ).toArray( String[]::new ) ).toArray( String[][]::new ) ) + .header( context.getIterator().getImplementation().rowType.getFields().stream().map( FieldDefinition::of ).toArray( FieldDefinition[]::new ) ) + .query( context.getQuery().getQuery() ) + .language( context.getQuery().getLanguage() ) + .dataModel( context.getIterator().getImplementation().getDataModel() ) + .affectedTuples( data.size() ) + .xid( statement.getTransaction().getXid().toString() ) + .namespace( request.namespace ); + + if ( Kind.DML.contains( context.getIterator().getImplementation().getKind() ) ) { + builder.affectedTuples( data.get( 0 )[0].asNumber().longValue() ); + } + return builder; - if ( Kind.DML.contains( context.getIterator().getImplementation().getKind() ) ) { - builder.affectedTuples( data.get( 0 )[0].asNumber().longValue() ); + } catch ( Exception e ) { + return buildErrorResult( statement.getTransaction(), context, e ); } - - return builder; } @@ -393,22 +395,22 @@ public static List computeResultData( final List> rows } iterator.close(); + + boolean hasMoreRows = context.getIterator().hasMoreRows(); + + return DocResult.builder() + .header( context.getIterator().getImplementation().rowType.getFields().stream().map( FieldDefinition::of ).toArray( FieldDefinition[]::new ) ) + .data( data.stream().map( d -> d.get( 0 ).toJson() ).toArray( String[]::new ) ) + .query( context.getQuery().getQuery() ) + .language( context.getQuery().getLanguage() ) + .hasMore( hasMoreRows ) + .affectedTuples( data.size() ) + .xid( statement.getTransaction().getXid().toString() ) + .dataModel( context.getIterator().getImplementation().getDataModel() ) + .namespace( request.namespace ); } catch ( Exception e ) { return buildErrorResult( statement.getTransaction(), context, e ); } - - boolean hasMoreRows = context.getIterator().hasMoreRows(); - - return DocResult.builder() - .header( context.getIterator().getImplementation().rowType.getFields().stream().map( FieldDefinition::of ).toArray( FieldDefinition[]::new ) ) - .data( data.stream().map( d -> d.get( 0 ).toJson() ).toArray( String[]::new ) ) - .query( context.getQuery().getQuery() ) - .language( context.getQuery().getLanguage() ) - .hasMore( hasMoreRows ) - .affectedTuples( data.size() ) - .xid( statement.getTransaction().getXid().toString() ) - .dataModel( context.getIterator().getImplementation().getDataModel() ) - .namespace( request.namespace ); }