Search in sources :

Example 1 with S_ConditionEntry

use of com.evolveum.midpoint.prism.query.builder.S_ConditionEntry in project midpoint by Evolveum.

the class SqaleAuditService method iterativeSearchCondition.

/**
 * Similar to {@link SqaleRepositoryService#lastOidCondition}.
 *
 * TODO, lots of possible improvements:
 *
 * * Just like in repo iterative search this is added to the original filter with `AND`.
 * However, if `timestamp` is used in the original filter, it could be replaced by stricter
 * `timestamp` condition based on the `lastProcessedObject` - this is not implemented yet.
 * ** TODO: If provided, do we want to add another timestamp condition?
 * Would it help with partitions?
 * Probably, with ID condition + ordering, it's not a big deal to leave it out.
 *
 * * Support for multiple order specifications from the client is not supported.
 * Perhaps some short-term stateful filter/order object would be better to construct this,
 * especially if it could be used in both repo and audit (with strict order attribute
 * provided in constructor for example).
 *
 * [NOTE]
 * ====
 * Both `timestamp` and `repoId` is used for iterative search condition, but *only `repoId`*
 * is used for additional ordering to assure strict reliable ordering.
 * This can be further complicated by the fact that both `repoId` and `timestamp`
 * can be part of custom ordering from client (this is different from repository, where `oid`
 * is not valid for filter and ordering on the model level, even when it's usable for repository).
 * If used on the top-level `AND` group, they can be be replaced by next iteration condition,
 * which is likely more selective.
 *
 * As for the ordering, if multiple ordering is used (*not supported yet*) if `repoId` is used,
 * anything after it can be omitted as irrelevant.
 * ====
 *
 * TODO: Currently, single path ordering is supported. Finish multi-path too.
 * TODO: What about nullable columns?
 */
@Nullable
private ObjectFilter iterativeSearchCondition(@Nullable AuditEventRecordType lastProcessedObject, List<? extends ObjectOrdering> providedOrdering) {
    if (lastProcessedObject == null) {
        return null;
    }
    // TODO inspect originalFilter to detect timestamp/repoId conditions.
    // Only top level AND filter should be checked, anything else is irrelevant
    // for the decision whether to skip additional timestamp condition.
    // BTW: We CANNOT skip repoId condition, that one is CRITICAL for proper iterating.
    Long lastProcessedId = lastProcessedObject.getRepoId();
    XMLGregorianCalendar lastProcessedTimestamp = lastProcessedObject.getTimestamp();
    if (providedOrdering == null || providedOrdering.isEmpty()) {
        return prismContext().queryFor(AuditEventRecordType.class).item(AuditEventRecordType.F_REPO_ID).gt(lastProcessedId).and().item(AuditEventRecordType.F_TIMESTAMP).ge(lastProcessedTimestamp).buildFilter();
    }
    if (providedOrdering.size() == 1) {
        ObjectOrdering objectOrdering = providedOrdering.get(0);
        ItemPath orderByPath = objectOrdering.getOrderBy();
        boolean asc = objectOrdering.getDirection() == OrderDirection.ASCENDING;
        S_ConditionEntry filter = prismContext().queryFor(AuditEventRecordType.class).item(orderByPath);
        @SuppressWarnings("unchecked") Item<PrismValue, ItemDefinition<?>> item = lastProcessedObject.asPrismContainerValue().findItem(orderByPath);
        if (item.size() > 1) {
            throw new IllegalArgumentException("Multi-value property for ordering is forbidden - item: " + item);
        } else if (item.isEmpty()) {
        // TODO what if it's nullable? is it null-first or last?
        // See: https://www.postgresql.org/docs/13/queries-order.html
        // "By default, null values sort as if larger than any non-null value; that is,
        // NULLS FIRST is the default for DESC order, and NULLS LAST otherwise."
        } else {
            S_MatchingRuleEntry matchingRuleEntry = asc ? filter.gt(item.getRealValue()) : filter.lt(item.getRealValue());
            filter = matchingRuleEntry.or().block().item(orderByPath).eq(item.getRealValue()).and().item(AuditEventRecordType.F_REPO_ID);
            return (asc ? filter.gt(lastProcessedId) : filter.lt(lastProcessedId)).endBlock().buildFilter();
        }
    }
    throw new IllegalArgumentException("Shouldn't get here with check in executeSearchObjectsIterative()");
}
Also used : S_ConditionEntry(com.evolveum.midpoint.prism.query.builder.S_ConditionEntry) ItemDefinition(com.evolveum.midpoint.prism.ItemDefinition) S_MatchingRuleEntry(com.evolveum.midpoint.prism.query.builder.S_MatchingRuleEntry) PrismValue(com.evolveum.midpoint.prism.PrismValue) XMLGregorianCalendar(javax.xml.datatype.XMLGregorianCalendar) AuditEventRecordType(com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType) CanonicalItemPath(com.evolveum.midpoint.prism.path.CanonicalItemPath) ItemPath(com.evolveum.midpoint.prism.path.ItemPath) Nullable(org.jetbrains.annotations.Nullable)

Example 2 with S_ConditionEntry

use of com.evolveum.midpoint.prism.query.builder.S_ConditionEntry in project midpoint by Evolveum.

the class ValueSearchFilterItem method buildFilter.

public ObjectFilter buildFilter(PrismContext prismContext, Class<O> type) {
    S_ConditionEntry conditionEntry = prismContext.queryFor(type).item(propertyPath);
    ObjectFilter builtFilter = null;
    if (FilterName.EQUAL.equals(filterTypeName)) {
        builtFilter = conditionEntry.eq(value).buildFilter();
    } else if (FilterName.GREATER.equals(filterTypeName)) {
        builtFilter = conditionEntry.gt(value).buildFilter();
    } else if (FilterName.GREATER_OR_EQUAL.equals(filterTypeName)) {
        builtFilter = conditionEntry.ge(value).buildFilter();
    } else if (FilterName.LESS.equals(filterTypeName)) {
        builtFilter = conditionEntry.lt(value).buildFilter();
    } else if (FilterName.LESS_OR_EQUAL.equals(filterTypeName)) {
        builtFilter = conditionEntry.le(value).buildFilter();
    } else if (FilterName.REF.equals(filterTypeName)) {
        if (value != null) {
            PrismReferenceValue refVal = null;
            if (value instanceof PrismReferenceValue) {
                refVal = (PrismReferenceValue) value;
            } else if (value instanceof ObjectReferenceType) {
                refVal = ((ObjectReferenceType) value).asReferenceValue();
            }
            if (refVal.isEmpty() && expression != null) {
                builtFilter = conditionEntry.ref(expression).buildFilter();
            } else if (refVal.getParent() instanceof RefFilter) {
                builtFilter = (RefFilter) refVal.getParent();
            } else {
                builtFilter = conditionEntry.ref(refVal).buildFilter();
            }
        } else {
            builtFilter = conditionEntry.ref(Collections.emptyList()).buildFilter();
        }
    } else if (FilterName.SUBSTRING.equals(filterTypeName)) {
        builtFilter = conditionEntry.contains(value).buildFilter();
    } else if (FilterName.SUBSTRING_ANCHOR_START.equals(filterTypeName)) {
        builtFilter = conditionEntry.startsWith(value).buildFilter();
    } else if (FilterName.SUBSTRING_ANCHOR_END.equals(filterTypeName)) {
        builtFilter = conditionEntry.endsWith(value).buildFilter();
    }
    if (builtFilter instanceof ValueFilter && matchingRule != null) {
        ((ValueFilter) builtFilter).setMatchingRule(matchingRule.getMatchingRuleName());
    }
    if (builtFilter instanceof ValueFilter && expression != null) {
        ((ValueFilter) builtFilter).setExpression(expression);
    }
    if (isApplyNegation()) {
        builtFilter = prismContext.queryFactory().createNot(builtFilter);
    }
    return builtFilter != null ? builtFilter : prismContext.queryFor(type).buildFilter();
}
Also used : S_ConditionEntry(com.evolveum.midpoint.prism.query.builder.S_ConditionEntry) ObjectReferenceType(com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType)

Example 3 with S_ConditionEntry

use of com.evolveum.midpoint.prism.query.builder.S_ConditionEntry in project midpoint by Evolveum.

the class SqlAuditServiceImpl method iterativeSearchCondition.

/**
 * See {@code SqaleRepositoryService.iterativeSearchCondition()} for more info.
 * This is unsupported version only for Ninja usage.
 */
@Nullable
private ObjectFilter iterativeSearchCondition(@Nullable AuditEventRecordType lastProcessedObject, List<? extends ObjectOrdering> providedOrdering) {
    if (lastProcessedObject == null) {
        return null;
    }
    Long lastProcessedId = lastProcessedObject.getRepoId();
    XMLGregorianCalendar lastProcessedTimestamp = lastProcessedObject.getTimestamp();
    if (providedOrdering == null || providedOrdering.isEmpty()) {
        return schemaService.prismContext().queryFor(AuditEventRecordType.class).item(AuditEventRecordType.F_REPO_ID).gt(lastProcessedId).and().item(AuditEventRecordType.F_TIMESTAMP).ge(lastProcessedTimestamp).buildFilter();
    }
    if (providedOrdering.size() == 1) {
        ObjectOrdering objectOrdering = providedOrdering.get(0);
        ItemPath orderByPath = objectOrdering.getOrderBy();
        boolean asc = objectOrdering.getDirection() == OrderDirection.ASCENDING;
        S_ConditionEntry filter = schemaService.prismContext().queryFor(AuditEventRecordType.class).item(orderByPath);
        @SuppressWarnings("unchecked") Item<PrismValue, ItemDefinition<?>> item = lastProcessedObject.asPrismContainerValue().findItem(orderByPath);
        if (item.size() > 1) {
            throw new IllegalArgumentException("Multi-value property for ordering is forbidden - item: " + item);
        } else if (item.isEmpty()) {
        // TODO what if it's nullable? is it null-first or last?
        // See: https://www.postgresql.org/docs/13/queries-order.html
        // "By default, null values sort as if larger than any non-null value; that is,
        // NULLS FIRST is the default for DESC order, and NULLS LAST otherwise."
        } else {
            S_MatchingRuleEntry matchingRuleEntry = asc ? filter.gt(item.getRealValue()) : filter.lt(item.getRealValue());
            filter = matchingRuleEntry.or().block().item(orderByPath).eq(item.getRealValue()).and().item(AuditEventRecordType.F_REPO_ID);
            return (asc ? filter.gt(lastProcessedId) : filter.lt(lastProcessedId)).endBlock().buildFilter();
        }
    }
    throw new IllegalArgumentException("Shouldn't get here with check in executeSearchObjectsIterative()");
}
Also used : S_ConditionEntry(com.evolveum.midpoint.prism.query.builder.S_ConditionEntry) S_MatchingRuleEntry(com.evolveum.midpoint.prism.query.builder.S_MatchingRuleEntry) XMLGregorianCalendar(javax.xml.datatype.XMLGregorianCalendar) CanonicalItemPath(com.evolveum.midpoint.prism.path.CanonicalItemPath) ItemPath(com.evolveum.midpoint.prism.path.ItemPath) Nullable(org.jetbrains.annotations.Nullable)

Example 4 with S_ConditionEntry

use of com.evolveum.midpoint.prism.query.builder.S_ConditionEntry in project midpoint by Evolveum.

the class SqaleRepositoryService method lastOidCondition.

/**
 * Without requested ordering, this is easy: `WHERE oid > lastOid`
 *
 * But with outside ordering we need to respect it and for ordering by X, Y, Z use
 * (`original conditions AND` is taken care of outside of this method):
 *
 * ----
 * ... WHERE original conditions AND (
 * X > last.X
 * OR (X = last.X AND Y > last.Y)
 * OR (X = last.X AND Y = last.Y AND Z > last.Z)
 * OR (X = last.X AND Y = last.Y ...if all equal AND OID > last.OID)
 * ----
 *
 * This is suddenly much more fun, isn't it?
 * Of course the condition `>` or `<` depends on `ASC` vs `DESC`.
 *
 * TODO: Currently, single path ordering is supported. Finish multi-path too.
 * TODO: What about nullable columns?
 */
@Nullable
private <T extends ObjectType> ObjectFilter lastOidCondition(PrismObject<T> lastProcessedObject, List<? extends ObjectOrdering> providedOrdering) {
    if (lastProcessedObject == null) {
        return null;
    }
    String lastProcessedOid = lastProcessedObject.getOid();
    if (providedOrdering == null || providedOrdering.isEmpty()) {
        return prismContext().queryFor(lastProcessedObject.getCompileTimeClass()).item(OID_PATH).gt(lastProcessedOid).buildFilter();
    }
    if (providedOrdering.size() == 1) {
        ObjectOrdering objectOrdering = providedOrdering.get(0);
        ItemPath orderByPath = objectOrdering.getOrderBy();
        boolean asc = objectOrdering.getDirection() == OrderDirection.ASCENDING;
        S_ConditionEntry filter = prismContext().queryFor(lastProcessedObject.getCompileTimeClass()).item(orderByPath);
        // noinspection rawtypes
        Item<PrismValue, ItemDefinition<Item>> item = lastProcessedObject.findItem(orderByPath);
        if (item.size() > 1) {
            throw new IllegalArgumentException("Multi-value property for ordering is forbidden - item: " + item);
        } else if (item.isEmpty()) {
        // TODO what if it's nullable? is it null-first or last?
        // See: https://www.postgresql.org/docs/13/queries-order.html
        // "By default, null values sort as if larger than any non-null value; that is,
        // NULLS FIRST is the default for DESC order, and NULLS LAST otherwise."
        } else {
            S_MatchingRuleEntry matchingRuleEntry = asc ? filter.gt(item.getRealValue()) : filter.lt(item.getRealValue());
            filter = matchingRuleEntry.or().block().item(orderByPath).eq(item.getRealValue()).and().item(OID_PATH);
            return (asc ? filter.gt(lastProcessedOid) : filter.lt(lastProcessedOid)).endBlock().buildFilter();
        }
    }
    throw new IllegalArgumentException("Shouldn't get here with check in executeSearchObjectsIterative()");
/*
        TODO: Unfinished - this is painful with fluent API. Should I call
         prismContext().queryFor(lastProcessedObject.getCompileTimeClass()) for each component
         and then use ObjectQueryUtil.filterAnd/Or?
        // we need to handle the complicated case with externally provided ordering
        S_FilterEntryOrEmpty orBlock = prismContext()
                .queryFor(lastProcessedObject.getCompileTimeClass()).block();
        orLoop:
        for (ObjectOrdering orMasterOrdering : providedOrdering) {
            Iterator<? extends ObjectOrdering> iterator = providedOrdering.iterator();
            while (iterator.hasNext()) {
                S_FilterEntryOrEmpty andBlock = orBlock.block();
                ObjectOrdering ordering = iterator.next();
                if (ordering.equals(orMasterOrdering)) {
                    // ...
                    continue orLoop;
                }
                orBlock = andBlock.endBlock();
            }

        }
        return orBlock.endBlock().buildFilter();
        */
}
Also used : S_ConditionEntry(com.evolveum.midpoint.prism.query.builder.S_ConditionEntry) PolyString(com.evolveum.midpoint.prism.polystring.PolyString) S_MatchingRuleEntry(com.evolveum.midpoint.prism.query.builder.S_MatchingRuleEntry) ItemPath(com.evolveum.midpoint.prism.path.ItemPath) Nullable(org.jetbrains.annotations.Nullable)

Aggregations

S_ConditionEntry (com.evolveum.midpoint.prism.query.builder.S_ConditionEntry)4 ItemPath (com.evolveum.midpoint.prism.path.ItemPath)3 S_MatchingRuleEntry (com.evolveum.midpoint.prism.query.builder.S_MatchingRuleEntry)3 Nullable (org.jetbrains.annotations.Nullable)3 CanonicalItemPath (com.evolveum.midpoint.prism.path.CanonicalItemPath)2 XMLGregorianCalendar (javax.xml.datatype.XMLGregorianCalendar)2 ItemDefinition (com.evolveum.midpoint.prism.ItemDefinition)1 PrismValue (com.evolveum.midpoint.prism.PrismValue)1 PolyString (com.evolveum.midpoint.prism.polystring.PolyString)1 AuditEventRecordType (com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType)1 ObjectReferenceType (com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType)1