Skip to content

Commit

Permalink
OrgFilterProcessor: added isChild support, closure now has o->o rows
Browse files Browse the repository at this point in the history
  • Loading branch information
virgo47 committed May 15, 2021
1 parent 63ae1e9 commit 58b35a6
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 17 deletions.
26 changes: 17 additions & 9 deletions repo/repo-sqale/sql/pgnew-repo.sql
Expand Up @@ -597,22 +597,30 @@ CREATE INDEX m_ref_object_parent_org_targetOid_relation_id_idx
ON m_ref_object_parent_org (targetOid, relation_id);

-- region org-closure
-- Trigger on m_ref_object_parent_org refreshes this view.
-- This is not most performant, but it is *correct* and it's still WIP.

/*
Trigger on m_ref_object_parent_org refreshes this view.
This is not most performant, but it is *correct* and it's still WIP.
Closure contains also identity (org = org) entries because:
* It's easier to do optimized matrix-multiplication based refresh with them later.
* It actually makes some query easier and requires AND instead of OR conditions.
* While the table shows that o => o (=> means "is parent of"), this is not the semantics
of isParent/ChildOf searches and they never return parameter OID as a result.
*/
CREATE MATERIALIZED VIEW m_org_closure AS
WITH RECURSIVE org_h (
ancestor_oid, -- ref.targetoid
descendant_oid --ref.owner_oid
-- paths -- number of different paths, not used for materialized view version
-- TODO depth? if so, cycles must be checked in recursive term
-- depth -- possible later, but cycle detected must be added to the recursive term
) AS (
-- gather all organizations with parents
SELECT r.targetoid, r.owner_oid
FROM m_ref_object_parent_org r
WHERE r.owner_type = 'ORG'
-- non-recursive term:
-- Gather all organization oids and initialize identity lines (o => o).
SELECT o.oid, o.oid FROM m_org o
-- It's possible to exclude orgs not in parent-org-refs (either owner or target!),
-- but it's not a big deal and makes things simple for JOINs (no outer needed).
UNION
-- generate their parents
-- recursive (iterative) term:
-- Generate their parents (anc => desc, that is target => owner), => means "is parent of".
SELECT par.targetoid, chi.descendant_oid -- leaving original child there generates closure
FROM m_ref_object_parent_org as par, org_h as chi
WHERE par.owner_oid = chi.ancestor_oid
Expand Down
Expand Up @@ -17,6 +17,7 @@
import com.evolveum.midpoint.repo.sqale.SqaleQueryContext;
import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject;
import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject;
import com.evolveum.midpoint.repo.sqale.qmodel.org.QOrgClosure;
import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReference;
import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping;
import com.evolveum.midpoint.repo.sqlbase.QueryException;
Expand Down Expand Up @@ -45,8 +46,7 @@ public Predicate process(OrgFilter filter) throws QueryException {
QObject<?> objectPath = (QObject<?>) path;
if (filter.isRoot()) {
QObjectReference<MObject> ref = getNewRefAlias();
return new SQLQuery<>().select(Expressions.constant(1))
.from(ref)
return subQuery(ref)
.where(ref.ownerOid.eq(objectPath.oid))
.notExists();
}
Expand All @@ -68,17 +68,24 @@ public Predicate process(OrgFilter filter) throws QueryException {

if (filter.getScope() == OrgFilter.Scope.ONE_LEVEL) {
QObjectReference<MObject> ref = getNewRefAlias();
SQLQuery<Integer> subQuery = new SQLQuery<>().select(Expressions.constant(1))
.from(ref)
SQLQuery<Integer> subQuery = subQuery(ref)
.where(ref.ownerOid.eq(objectPath.oid)
.and(ref.targetOid.eq(UUID.fromString(oidParam))));
if (relationId != null) {
subQuery.where(ref.relationId.eq(relationId));
}
return subQuery.exists();
} else if (filter.getScope() == OrgFilter.Scope.SUBTREE) {
throw new UnsupportedOperationException();
// TODO
QObjectReference<MObject> ref = getNewRefAlias();
QOrgClosure oc = getNewClosureAlias();
SQLQuery<Integer> subQuery = subQuery(ref)
.join(oc).on(oc.descendantOid.eq(ref.targetOid))
.where(ref.ownerOid.eq(objectPath.oid)
.and(oc.ancestorOid.eq(UUID.fromString(oidParam))));
if (relationId != null) {
subQuery.where(ref.relationId.eq(relationId));
}
return subQuery.exists();
} else if (filter.getScope() == OrgFilter.Scope.ANCESTORS) {
throw new UnsupportedOperationException();
// TODO
Expand All @@ -87,10 +94,21 @@ public Predicate process(OrgFilter filter) throws QueryException {
}
}

private SQLQuery<Integer> subQuery(FlexibleRelationalPathBase<?> entityPath) {
return new SQLQuery<>().select(Expressions.constant(1))
.from(entityPath);
}

private QObjectReference<MObject> getNewRefAlias() {
var refMapping = QObjectReferenceMapping.getForParentOrg();
QObjectReferenceMapping<QObject<MObject>, MObject> refMapping =
QObjectReferenceMapping.getForParentOrg();
QObjectReference<MObject> ref = refMapping.newAlias(
context.uniqueAliasName(refMapping.defaultAliasName()));
return ref;
}

private QOrgClosure getNewClosureAlias() {
return new QOrgClosure(
context.uniqueAliasName(QOrgClosure.DEFAULT_ALIAS_NAME));
}
}
Expand Up @@ -44,8 +44,10 @@ public class SqaleRepoSearchObjectTest extends SqaleRepoBaseTest {
private String org21Oid;
private String orgXOid; // under two orgs

private String user1Oid; // typical object
private String user1Oid; // user without org
private String user2Oid; // different user, this one is in org
private String user3Oid; // another user in org
private String user4Oid; // another user in org
private String task1Oid; // task has more attribute type variability
private String shadow1Oid; // ditto
private String service1Oid; // object with integer attribute
Expand Down Expand Up @@ -89,6 +91,7 @@ public void initObjects() throws Exception {
null, result);
org21Oid = repositoryService.addObject(
new OrgType(prismContext).name("org-2-1")
.costCenter("5")
.parentOrgRef(org2Oid, OrgType.COMPLEX_TYPE)
.asPrismObject(),
null, result);
Expand Down Expand Up @@ -117,6 +120,19 @@ public void initObjects() throws Exception {
.parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE, relation1)
.asPrismObject(),
null, result);
user3Oid = repositoryService.addObject(
new UserType(prismContext).name("user-3")
.costCenter("50")
.parentOrgRef(orgXOid, OrgType.COMPLEX_TYPE)
.parentOrgRef(org21Oid, OrgType.COMPLEX_TYPE, relation1)
.asPrismObject(),
null, result);
user4Oid = repositoryService.addObject(
new UserType(prismContext).name("user-4")
.costCenter("51")
.parentOrgRef(org111Oid, OrgType.COMPLEX_TYPE)
.asPrismObject(),
null, result);
task1Oid = repositoryService.addObject(
new TaskType(prismContext).name("task-1").asPrismObject(),
null, result);
Expand Down Expand Up @@ -258,6 +274,60 @@ public void test212QueryForDirectChildrenOfAnyTypeWithRelation() throws SchemaEx
.containsExactlyInAnyOrder(org112Oid, user2Oid);
}

@Test
public void test215QueryForChildrenOfAnyType() throws SchemaException {
when("searching objects anywhere under an org");
OperationResult operationResult = createOperationResult();
SearchResultList<ObjectType> result = searchObjects(ObjectType.class,
prismContext.queryFor(ObjectType.class)
.isChildOf(org2Oid)
.build(),
operationResult);

then("all objects under the specified organization are returned");
assertThatOperationResult(operationResult).isSuccess();
assertThat(result).hasSize(4)
.extracting(o -> o.getOid())
.containsExactlyInAnyOrder(org21Oid, orgXOid, user2Oid, user3Oid);
}

@Test
public void test216QueryForChildrenOfAnyTypeWithRelation() throws SchemaException {
when("searching objects anywhere under an org with specific relation");
OperationResult operationResult = createOperationResult();
SearchResultList<ObjectType> result = searchObjects(ObjectType.class,
prismContext.queryFor(ObjectType.class)
.isChildOf(prismContext.itemFactory()
.createReferenceValue(org2Oid).relation(relation1))
.build(),
operationResult);

then("all objects under the specified organization with specified relation are returned");
assertThatOperationResult(operationResult).isSuccess();
assertThat(result).hasSize(1)
.extracting(o -> o.getOid())
.containsExactlyInAnyOrder(user3Oid);
// user-2 has another parent link with relation1, but not under org-2
}

@Test
public void test230QueryForChildrenOfAnyTypeWithAnotherCondition() throws SchemaException {
when("searching objects anywhere under an org");
OperationResult operationResult = createOperationResult();
SearchResultList<FocusType> result = searchObjects(FocusType.class,
prismContext.queryFor(FocusType.class)
.isChildOf(org2Oid)
.and().item(FocusType.F_COST_CENTER).startsWith("5")
.build(),
operationResult);

then("all objects under the specified organization matching other conditions are returned");
assertThatOperationResult(operationResult).isSuccess();
assertThat(result).hasSize(2)
.extracting(o -> o.getOid())
.containsExactlyInAnyOrder(org21Oid, user3Oid);
}

// TODO child/parent tests
// endregion

Expand Down

0 comments on commit 58b35a6

Please sign in to comment.