Skip to content

Commit

Permalink
Allow constant, inequalities and 'not' in XStore model joins (#3205)
Browse files Browse the repository at this point in the history
* Allow constant in model joins

Allow constant in model joins

* Update modelJoins.pure

* Update modelJoins.pure

* Support more operations

* Fix the 'not' operation in join condition

* Update modelJoins.pure

* Update modelJoins.pure

* Update modelJoins.pure

* Update modelJoins.pure

* Add tests

* Fix tests

* Fix test
  • Loading branch information
renuccif authored Oct 30, 2024
1 parent f921ce1 commit 08dd654
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,22 @@ function <<access.private>> meta::external::store::relational::modelJoins::getCl
$classMappings->filter(c| $c.id == $id)->toOne('More than one class mapping at the given id: ' + $id);
}

function <<access.private>> meta::external::store::relational::modelJoins::getAttributeRelationalOperationElement(propertyImplementations:SimpleFunctionExpression[*], thisOrThat:String[1], id:String[1], classMappings:RootRelationalInstanceSetImplementation[*]):RelationalOperationElement[1]
function <<access.private>> meta::external::store::relational::modelJoins::convertToRelationalElement(vs:ValueSpecification[1], sourceSet:RootRelationalInstanceSetImplementation[1], targetSet: RootRelationalInstanceSetImplementation[1]):RelationalOperationElement[1]
{
let propertyImplementation = $propertyImplementations->filter(p|$p.parametersValues->cast(@VariableExpression).name->toOne()==$thisOrThat).func->toOne();
let classMapping = $classMappings->getClassMappingAtId($id);
let relationalPropertyMapping = $classMapping.propertyMappings->filter(p|$p.property==$propertyImplementation)->cast(@RelationalPropertyMapping)->toOne();
$relationalPropertyMapping.relationalOperationElement->toOne('Localized relational model joins cannot support more than one relational operation element per property mapping. Found on class mapping id: ' + $id);
$vs->match([
p : SimpleFunctionExpression[1] |
assert($p.func->instanceOf(Property),|'Expected only property function calls in model joins.');
let var = $p.parametersValues->evaluateAndDeactivate()->at(0);
assert($var->instanceOf(VariableExpression) && $var->cast(@VariableExpression).name->in(['this','that']),|'Properties of $this or $that can be accessed in model joins.');
let classMapping = if($var->cast(@VariableExpression).name == 'this', | $sourceSet, | $targetSet);
let relationalPropertyMapping = $classMapping.propertyMappings->filter(x|$x.property==$p.func)->cast(@RelationalPropertyMapping)->toOne();
$relationalPropertyMapping.relationalOperationElement->toOne('Localized relational model joins cannot support more than one relational operation element per property mapping. Found on class mapping id: ' + $classMapping.id);,

v : InstanceValue[1] |
assert($v.values->size() == 1 && ($v.values->toOne()->instanceOf(String) || $v.values->toOne()->instanceOf(Number) || $v.values->toOne()->instanceOf(Date)),|'The join key should only have one mapping.');
^Literal(value=$v.values->toOne());
]);

}

function <<access.private>> meta::external::store::relational::modelJoins::transformExpressionSequenceIntoJoin(expressionSequence: SimpleFunctionExpression[1], sourceId:String[1], targetId:String[1], classMappings:RootRelationalInstanceSetImplementation[*]): Join[1]
Expand All @@ -85,16 +95,15 @@ function <<access.private>> meta::external::store::relational::modelJoins::trans
let targetDatabase = $targetMainTableAlias.database->toOne();
let aggregatedDatabase = ^Database(includes=[$sourceDatabase, $targetDatabase]);

assertContains(['equal', 'not', 'and', 'or'], $functionOperator, 'Failed to translate XStore Property into Relational Property because function operator is not in standard list');
assertContains(['equal', 'not', 'and', 'or', 'greaterThanEqual', 'greaterThan', 'lessThanEqual', 'lessThan' ], $functionOperator, 'Failed to translate XStore Property into Relational Property because function operator is not in standard list');

let join = if(
$functionOperator=='equal' || $functionOperator=='not',
| let propertyImplementations = $expressionSequence.parametersValues->cast(@SimpleFunctionExpression)->evaluateAndDeactivate();
$functionOperator->in(['equal', 'greaterThanEqual', 'greaterThan', 'lessThanEqual', 'lessThan' ]),
| let expressionParameters = $expressionSequence.parametersValues->evaluateAndDeactivate();
let sourceSet = getClassMappingAtId($classMappings, $sourceId);
let targetSet = getClassMappingAtId($classMappings, $targetId);

let sourceRelationalOperationElement = getAttributeRelationalOperationElement($propertyImplementations, 'this', $sourceId, $classMappings);
let targetRelationalOperationElement = getAttributeRelationalOperationElement($propertyImplementations, 'that', $targetId, $classMappings);

let parameters = $sourceRelationalOperationElement->concatenate($targetRelationalOperationElement);
let parameters = $expressionParameters->map(e | $e->convertToRelationalElement($sourceSet, $targetSet));
let operation = ^DynaFunction(name=$functionOperator, parameters=$parameters);
^Join(
name=$joinName,
Expand All @@ -117,4 +126,3 @@ function <<access.private>> meta::external::store::relational::modelJoins::trans
);

}

Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ import meta::pure::alloy::connections::alloy::specification::*;

function <<test.BeforePackage>> meta::external::store::relational::modelJoins::test::setUp():Boolean[1]
{
let connection = meta::external::store::relational::tests::testRuntime(LocalTradesDatabase).connectionStores.connection->toOne()->cast(@DatabaseConnection);
let connection = meta::external::store::relational::tests::testRuntime(LocalTradesDatabase).connectionStores.connection->toOne()->cast(@meta::external::store::relational::runtime::DatabaseConnection);

executeInDb('Drop table if exists LegalEntity;', $connection);
executeInDb('Create Table LegalEntity (ENTITY_ID INT, name VARCHAR(32));', $connection);
executeInDb('insert into LegalEntity (ENTITY_ID, name) values (1, \'Firm X\');', $connection);
executeInDb('insert into LegalEntity (ENTITY_ID, name) values (2, \'Firm A\');', $connection);
executeInDb('Create Table LegalEntity (ENTITY_ID INT, name VARCHAR(32), value INT);', $connection);
executeInDb('insert into LegalEntity (ENTITY_ID, name, value) values (1, \'Firm X\', 12);', $connection);
executeInDb('insert into LegalEntity (ENTITY_ID, name, value) values (2, \'Firm A\', 13);', $connection);

executeInDb('Drop table if exists Trade;', $connection);
executeInDb('Create Table Trade(id INT, ENTITY_NAME_FK VARCHAR(32), value INT, ENTITY_ID_FK INT);', $connection);
executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK) values (1, \'Firm X\', 8, 1);', $connection);
executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK) values (2, \'Firm A\', 9, 2);', $connection);
executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK) values (3, \'Firm X\', 10, 1);', $connection);
executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK) values (4, \'Firm X\', 11, 1);', $connection);
executeInDb('Create Table Trade(id INT, ENTITY_NAME_FK VARCHAR(32), value INT, ENTITY_ID_FK INT, date DATE);', $connection);
executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK, date) values (1, \'Firm X\', 8, 1, \'2022-01-01\');', $connection);
executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK, date) values (2, \'Firm A\', 9, 2, \'2023-01-01\');', $connection);
executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK, date) values (3, \'Firm X\', 10, 1, \'2024-01-01\');', $connection);
executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK, date) values (4, \'Firm X\', 11, 1, \'2025-01-01\');', $connection);
true;
}

Expand Down Expand Up @@ -69,6 +69,38 @@ function <<test.Test>> meta::external::store::relational::modelJoins::test::test
);
}


function <<test.Test>> meta::external::store::relational::modelJoins::test::testJoinWithConstantDouble():Boolean[1]
{
let query = {|Trade.all()->project([x|$x.value,x|$x.client.name],['Value','Client/Name']);};
let xstoreResult = executionPlan($query, XStoreTradesMapping_withConstantDouble, getXStoreRuntime(),meta::relational::extension::relationalExtensions());
assertEquals('select "root".value as "Value", "legalentity_view_0".name as "Client/Name" from Trades.Trade as "root" left outer join (select "root".ENTITY_ID as ENTITY_ID, "root".name as name, "root".value as value from Entity.LegalEntity as "root" group by "root".ENTITY_ID) as "legalentity_view_0" on ("root".ENTITY_ID_FK = "legalentity_view_0".ENTITY_ID and "root".value = 1 and (2 <> "root".value OR "root".value is null) and 1 = 1)',
$xstoreResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery);
}

function <<test.Test>> meta::external::store::relational::modelJoins::test::testJoinWithConstantString():Boolean[1]
{
let query = {|Trade.all()->project([x|$x.value,x|$x.client.name],['Value','Client/Name']);};
let xstoreResult = executionPlan($query, XStoreTradesMapping_withConstantString, getXStoreRuntime(),meta::relational::extension::relationalExtensions());
assertEquals('select "root".value as "Value", "legalentity_view_0".name as "Client/Name" from Trades.Trade as "root" left outer join (select "root".ENTITY_ID as ENTITY_ID, "root".name as name, "root".value as value from Entity.LegalEntity as "root" group by "root".ENTITY_ID) as "legalentity_view_0" on ("root".ENTITY_ID_FK = "legalentity_view_0".ENTITY_ID and "root".ENTITY_ID_FK = \'foo\' and (\'bar\' <> "root".ENTITY_ID_FK OR "root".ENTITY_ID_FK is null))', $xstoreResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery);
}

function <<test.Test>> meta::external::store::relational::modelJoins::test::testJoinWithConstantDate():Boolean[1]
{
let query = {|Trade.all()->project([x|$x.value,x|$x.client.name],['Value','Client/Name']);};
let xstoreResult = executionPlan($query, XStoreTradesMapping_withConstantDate, getXStoreRuntime(),meta::relational::extension::relationalExtensions());
assertEquals('select "root".value as "Value", "legalentity_view_0".name as "Client/Name" from Trades.Trade as "root" left outer join (select "root".ENTITY_ID as ENTITY_ID, "root".name as name, "root".value as value from Entity.LegalEntity as "root" group by "root".ENTITY_ID) as "legalentity_view_0" on ("root".ENTITY_ID_FK = "legalentity_view_0".ENTITY_ID and "root".date = DATE\'2024-01-01\' and (DATE\'2024-01-01\' <> "root".date OR "root".date is null))',
$xstoreResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery);
}

function <<test.Test>> meta::external::store::relational::modelJoins::test::testJoinWithInequalities():Boolean[1]
{
let query = {|Trade.all()->project([x|$x.value,x|$x.client.name],['Value','Client/Name']);};
let xstoreResult = executionPlan($query, XStoreTradesMapping_withInequalities, getXStoreRuntime(),meta::relational::extension::relationalExtensions());
assertEquals('select "root".value as "Value", "legalentity_view_0".name as "Client/Name" from Trades.Trade as "root" left outer join (select "root".ENTITY_ID as ENTITY_ID, "root".name as name, "root".value as value from Entity.LegalEntity as "root" group by "root".ENTITY_ID) as "legalentity_view_0" on ("root".value >= "legalentity_view_0".value and "root".value > "legalentity_view_0".value and "root".value <= "legalentity_view_0".value and "root".value < "legalentity_view_0".value)',
$xstoreResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery);
}

function <<test.Test>> meta::external::store::relational::modelJoins::test::testPersonToFirmUsingFromProject():Boolean[1]
{
let xstoreResult = executionPlan({|Trade.all()->from(XStoreTradesMapping, getXStoreRuntime())->project([x|$x.value,x|$x.client.name],['Value','Client/Name']);}, meta::relational::extension::relationalExtensions());
Expand Down Expand Up @@ -115,12 +147,14 @@ Class meta::external::store::relational::modelJoins::test::LegalEntity
{
entityId: String[1];
name: String[1];
value: Integer[1];
}

Class meta::external::store::relational::modelJoins::test::Trade
{
id: String[1];
value: Integer[1];
date: Date[1];
}

Class meta::external::store::relational::modelJoins::test::OtherEntity
Expand Down Expand Up @@ -149,14 +183,16 @@ Database meta::external::store::relational::modelJoins::test::EntityDatabase
Table LegalEntity
(
ENTITY_ID VARCHAR(32) PRIMARY KEY,
name VARCHAR(32) NOT NULL
name VARCHAR(32) NOT NULL,
value INT NOT NULL
)

View LegalEntity_View
(
~groupBy (Entity.LegalEntity.ENTITY_ID)
ENTITY_ID: Entity.LegalEntity.ENTITY_ID,
name: Entity.LegalEntity.name
name: Entity.LegalEntity.name,
value: Entity.LegalEntity.value
)
)
)
Expand Down Expand Up @@ -192,7 +228,8 @@ Database meta::external::store::relational::modelJoins::test::XStoreTradesDataba
id VARCHAR(32) PRIMARY KEY,
value INTEGER NOT NULL,
ENTITY_ID_FK VARCHAR(32) NOT NULL,
ENTITY_NAME_FK VARCHAR(32) NOT NULL
ENTITY_NAME_FK VARCHAR(32) NOT NULL,
date DATE NOT NULL
)
)
)
Expand All @@ -211,7 +248,8 @@ Mapping meta::external::store::relational::modelJoins::test::LegalEntityMapping
)
~mainTable [meta::external::store::relational::modelJoins::test::EntityDatabase]Entity.LegalEntity_View
entityId: [meta::external::store::relational::modelJoins::test::EntityDatabase]Entity.LegalEntity_View.ENTITY_ID,
name: [meta::external::store::relational::modelJoins::test::EntityDatabase]Entity.LegalEntity_View.name
name: [meta::external::store::relational::modelJoins::test::EntityDatabase]Entity.LegalEntity_View.name,
value : [meta::external::store::relational::modelJoins::test::EntityDatabase]Entity.LegalEntity_View.value
}
)

Expand Down Expand Up @@ -274,3 +312,68 @@ Mapping meta::external::store::relational::modelJoins::test::XStoreTradesMapping
$that.entityIdFk && $this.name == $that.entityNameFk
}
)

Mapping meta::external::store::relational::modelJoins::test::TradesMapping
(
Trade[trade]: Relational
{
~primaryKey
(
[XStoreTradesDatabase]Trades.Trade.id
)
~mainTable [XStoreTradesDatabase]Trades.Trade
id: [XStoreTradesDatabase]Trades.Trade.id,
value: [XStoreTradesDatabase]Trades.Trade.value,
date: [XStoreTradesDatabase]Trades.Trade.date,
+entityIdFk: String[1]: [XStoreTradesDatabase]Trades.Trade.ENTITY_ID_FK,
+entityNameFk: String[1]: [XStoreTradesDatabase]Trades.Trade.ENTITY_NAME_FK
}
)

Mapping meta::external::store::relational::modelJoins::test::XStoreTradesMapping_withConstantDouble
(
include meta::external::store::relational::modelJoins::test::LegalEntityMapping
include meta::external::store::relational::modelJoins::test::TradesMapping

meta::external::store::relational::modelJoins::test::Trade_LegalEntity: XStore
{
client[trade, legal_entity]: $this.entityIdFk == $that.entityId && $this.value == 1 && 2 != $this.value && 1 == 1,
trades[legal_entity, trade]: $this.entityId == $that.entityIdFk && $that.value == 1 && 2 != $that.value && 1 == 1
}
)

Mapping meta::external::store::relational::modelJoins::test::XStoreTradesMapping_withConstantString
(
include meta::external::store::relational::modelJoins::test::LegalEntityMapping
include meta::external::store::relational::modelJoins::test::TradesMapping

meta::external::store::relational::modelJoins::test::Trade_LegalEntity: XStore
{
client[trade, legal_entity]: $this.entityIdFk == $that.entityId && $this.entityIdFk == 'foo' && 'bar' != $this.entityIdFk,
trades[legal_entity, trade]: $this.entityId == $that.entityIdFk && $that.entityIdFk == 'foo' && 'bar' != $that.entityIdFk
}
)

Mapping meta::external::store::relational::modelJoins::test::XStoreTradesMapping_withConstantDate
(
include meta::external::store::relational::modelJoins::test::LegalEntityMapping
include meta::external::store::relational::modelJoins::test::TradesMapping

meta::external::store::relational::modelJoins::test::Trade_LegalEntity: XStore
{
client[trade, legal_entity]: $this.entityIdFk == $that.entityId && $this.date == %2024-01-01 && %2024-01-01 != $this.date,
trades[legal_entity, trade]: $this.entityId == $that.entityIdFk && $that.date == %2024-01-01 && %2024-01-01 != $that.date
}
)

Mapping meta::external::store::relational::modelJoins::test::XStoreTradesMapping_withInequalities
(
include meta::external::store::relational::modelJoins::test::LegalEntityMapping
include meta::external::store::relational::modelJoins::test::TradesMapping

meta::external::store::relational::modelJoins::test::Trade_LegalEntity: XStore
{
client[trade, legal_entity]: ($this.value >= $that.value) && ($this.value > $that.value) && ($this.value <= $that.value) && ($this.value < $that.value),
trades[legal_entity, trade]: ($that.value >= $this.value) && ($that.value > $this.value) && ($that.value <= $this.value) && ($that.value < $this.value)
}
)

0 comments on commit 08dd654

Please sign in to comment.