private List<Condition> createRetentionDeleteConditions(final List<DataRetentionRuleAction> ruleActions) {
        Objects.requireNonNull(ruleActions);
        if (ruleActions.isEmpty()) {
            throw new IllegalArgumentException("Expected one or more rules");
        }

        final List<Condition> conditions = new ArrayList<>();
        final byte statusIdUnlocked = MetaStatusId.getPrimitiveValue(Status.UNLOCKED);

        // Ensure we only 'delete' unlocked records, also ensures we don't touch
        // records we have already deleted in a previous pass
        conditions.add(meta.STATUS.eq(statusIdUnlocked));

        // What we are building is roughly:
        // WHERE (CASE
        //   WHEN <rule 3 condition is true> THEN <outcome => true|false>
        //   WHEN <rule 2 condition is true> THEN <outcome => true|false>
        //   WHEN <rule 1 condition is true> THEN <outcome => true|false>
        //   ELSE false
        //   ) = true
        // I.e. see if a rule matches the data in priority order
        // Needs to be a CASE so we can ensure the order of rule evaluation
        // and drop out when one matches. A matching rule may have an outcome
        // of delete (true) or retain (false)

        CaseConditionStep<Boolean> caseConditionStep = null;
        final List<Condition> orConditions = new ArrayList<>();
        final DataRetentionConfig dataRetentionConfig = dataRetentionConfigProvider.get();
        // Order is critical here as we are building a case statement
        // Highest priority rules first, i.e. largest rule number
        for (DataRetentionRuleAction ruleAction : ruleActions) {
            final Condition ruleCondition = expressionMapper.apply(ruleAction.getRule().getExpression());
            if (dataRetentionConfig.isUseQueryOptimisation() &&
                !DSL.noCondition().equals(ruleCondition)) {
                orConditions.add(ruleCondition);
            }

            // The rule will either result in true or false depending on if it needs
            // to delete or retain the data
            final boolean caseResult = ruleActionToBoolean(ruleAction);
            if (caseConditionStep == null) {
                caseConditionStep = DSL.when(ruleCondition, caseResult);
            } else {
                caseConditionStep.when(ruleCondition, caseResult);
            }
        }

        if (caseConditionStep != null) {
            // If none of the rules matches then we don't to delete so return false
            final Field<Boolean> caseField = caseConditionStep.otherwise(Boolean.FALSE);

            // Add our rule conditions
            conditions.add(caseField.eq(true));
        }

        // Now add all the rule conditions as an OR block
        // This is to improve the performance of the query as the OR can make use of other indexes,
        // e.g. range scanning the feedid+createTime index to reduce the number of rows scanned.
        // We are still reliant on the case statement block to get the right outcome for the rules.
        // It is possible this approach may slow things down as it makes the SQL more complex.
        if (dataRetentionConfig.isUseQueryOptimisation()) {
            if (orConditions.size() > 1) {
                conditions.add(DSL.or(orConditions));
            } else if (orConditions.size() == 1) {
                conditions.add(orConditions.get(0));
            }
        }

        LOGGER.debug("conditions {}", conditions);
        return conditions;
    }
@return True if any term in any rule expression uses at least one of {@code fields}.
stroom-meta/stroom-meta-impl-db/src/main/java/stroom/meta/impl/db/MetaDaoImpl.java