Search in sources :

Example 1 with BooleanExpression

use of com.mysema.query.types.expr.BooleanExpression in project opencast by opencast.

the class AbstractASelectQuery method run.

private AResult run(JPAQueryFactory f) {
    // run query and map the result to records
    final long startTime = System.nanoTime();
    // resolve AST
    final SelectQueryContribution r = contributeSelect(f);
    final boolean toFetchProperties = r.fetch.exists(Booleans.<Expression<?>>eq(QPropertyDto.propertyDto));
    // # create Querydsl query
    final JPAQuery q = f.query();
    // # from
    {
        // Make sure that the snapshotDto is always contained in the from clause because the media package ID and
        // the ID are always selected.
        // Use a mutable hash set to be able to use the removeAll operation.
        final Set<EntityPath<?>> from = Stream.<EntityPath<?>>mk(Q_SNAPSHOT).append(// all collected from clauses
        r.from).append(// all from clauses from the joins
        r.join.map(Join.getFrom)).toSet(SetB.MH);
        // Now remove everything that will be joined. Adding them in both the from and a join
        // clause is not allowed.
        from.removeAll(r.join.map(Join.getJoin).toSet());
        q.from(JpaFns.toEntityPathArray(from));
    }
    // # join
    if (!r.join.isEmpty()) {
        // Group joins by entity and combine all "on" clauses with "or" expressions.
        // This way there is only one join clause per distinct entity which eliminates the need to alias entities
        // like this `new QPropertyDto("alias")`.
        // Entity aliasing produces many issues which seem to cause a huge rewrite of the query building mechanism
        // so it should be prevented at all costs.
        final Map<EntityPath<?>, BooleanExpression> joins = r.join.foldl(new HashMap<EntityPath<?>, BooleanExpression>(), new Fn2<Map<EntityPath<?>, BooleanExpression>, Join, Map<EntityPath<?>, BooleanExpression>>() {

            @Override
            public Map<EntityPath<?>, BooleanExpression> apply(Map<EntityPath<?>, BooleanExpression> sum, Join join) {
                // get the on expression saved with the join, may be null
                final BooleanExpression existing = sum.get(join.join);
                final BooleanExpression combined;
                // combine the existing and the current expression
                if (existing == null) {
                    combined = join.on;
                } else if (existing.equals(join.on)) {
                    // if both expressions are equal there is no need to combine them
                    combined = existing;
                } else {
                    // if different combine with logical "or"
                    combined = existing.or(join.on);
                }
                sum.put(join.join, combined);
                return sum;
            }
        });
        for (final Map.Entry<EntityPath<?>, BooleanExpression> j : joins.entrySet()) {
            q.leftJoin(j.getKey()).on(j.getValue());
        }
    }
    // # where
    q.where(r.where.orNull());
    // # paging
    for (Integer a : r.offset) {
        q.offset(a);
    }
    for (Integer a : r.limit) {
        q.limit(a);
    }
    // # order
    for (OrderSpecifier<?> a : r.order) {
        q.orderBy(a);
    }
    // # distinct
    if (!toFetchProperties) {
        // if no properties shall be fetched the result set can be distinct
        q.distinct();
    }
    // # fetch
    // create parameters for fetch clause, i.e. Querydsl's list() method
    final List<Expression<?>> fetch;
    {
        // check if the media package ID needs to be selected separately
        if (r.fetch.exists(MandatoryFetch.exists)) {
            fetch = r.fetch.toList();
        } else {
            fetch = r.fetch.append(MandatoryFetch.fetch).toList();
        }
    }
    // Run the query and transform the result into records
    final Stream<ARecordImpl> records;
    {
        // run query
        am.getDb().logQuery(q);
        final List<Tuple> result = q.list(JpaFns.toExpressionArray(fetch));
        logger.debug("Pure query ms " + (System.nanoTime() - startTime) / 1000000);
        // map result based on the fact whether properties have been fetched or not
        if (!toFetchProperties) {
            // No properties have been fetched -> each result row (tuple) is a distinct record (snapshot).
            records = $($(result).map(toARecord(r))).map(new Fn<ARecordImpl, ARecordImpl>() {

                @Override
                public ARecordImpl apply(ARecordImpl record) {
                    Opt<Snapshot> snapshotOpt = record.getSnapshot();
                    Snapshot snapshot = null;
                    if (snapshotOpt.isSome()) {
                        // make sure the delivered media package has valid URIs
                        snapshot = am.getHttpAssetProvider().prepareForDelivery(snapshotOpt.get());
                    }
                    return new ARecordImpl(record.getSnapshotId(), record.getMediaPackageId(), record.getProperties(), snapshot);
                }
            });
        } else {
            logger.trace("Fetched properties");
            // Properties have been fetched -> there may be multiple rows (tuples) per snapshot because of the join with the property table.
            // Extract records and properties and link them together.
            // group properties after their media package ID and make sure that no duplicate properties occur
            final Map<String, Set<Property>> propertiesPerMp = $(result).bind(toProperty).foldl(new HashMap<String, Set<Property>>(), new Fn2<Map<String, Set<Property>>, Property, Map<String, Set<Property>>>() {

                @Override
                public Map<String, Set<Property>> apply(Map<String, Set<Property>> sum, Property p) {
                    final String mpId = p.getId().getMediaPackageId();
                    final Set<Property> props = sum.get(mpId);
                    if (props != null) {
                        props.add(p);
                    } else {
                        sum.put(mpId, SetB.MH.mk(p));
                    }
                    return sum;
                }
            });
            // group records after their media package ID
            final Map<String, List<ARecordImpl>> distinctRecords = $($(result).map(toARecord(r)).toSet()).groupMulti(ARecordImpl.getMediaPackageId);
            records = $(distinctRecords.values()).bind(new Fn<List<ARecordImpl>, Iterable<ARecordImpl>>() {

                @Override
                public Iterable<ARecordImpl> apply(List<ARecordImpl> records) {
                    return $(records).map(new Fn<ARecordImpl, ARecordImpl>() {

                        @Override
                        public ARecordImpl apply(ARecordImpl record) {
                            final Set<Property> properties = propertiesPerMp.get(record.getMediaPackageId());
                            final Stream<Property> p = properties != null ? $(properties) : Stream.<Property>empty();
                            Snapshot snapshot = null;
                            Opt<Snapshot> snapshotOpt = record.getSnapshot();
                            if (snapshotOpt.isSome()) {
                                // make sure the delivered media package has valid URIs
                                snapshot = am.getHttpAssetProvider().prepareForDelivery(snapshotOpt.get());
                            }
                            return new ARecordImpl(record.getSnapshotId(), record.getMediaPackageId(), p, snapshot);
                        }
                    });
                }
            });
        }
    }
    final long searchTime = (System.nanoTime() - startTime) / 1000000;
    logger.debug("Complete query ms " + searchTime);
    return new AResultImpl(AbstractASelectQuery.<ARecord>vary(records), sizeOf(records), r.offset.getOr(0), r.limit.getOr(-1), searchTime);
}
Also used : Set(java.util.Set) HashMap(java.util.HashMap) Fn2(com.entwinemedia.fn.Fn2) BooleanExpression(com.mysema.query.types.expr.BooleanExpression) Opt(com.entwinemedia.fn.data.Opt) List(java.util.List) Property(org.opencastproject.assetmanager.api.Property) EntityPath(com.mysema.query.types.EntityPath) Fn(com.entwinemedia.fn.Fn) JPAQuery(com.mysema.query.jpa.impl.JPAQuery) Snapshot(org.opencastproject.assetmanager.api.Snapshot) BooleanExpression(com.mysema.query.types.expr.BooleanExpression) Expression(com.mysema.query.types.Expression) HashMap(java.util.HashMap) Map(java.util.Map)

Example 2 with BooleanExpression

use of com.mysema.query.types.expr.BooleanExpression in project opencast by opencast.

the class AbstractADeleteQuery method runQueries.

/**
 * Run this in a transaction.
 */
private DeletionResult runQueries(JPAQueryFactory jpa, DeleteQueryContribution c) {
    // # create Querydsl delete clause
    // # from
    // put into a set to remove duplicates
    final EntityPath<?> from;
    {
        final Set<EntityPath<?>> f = c.from.toSet(SetB.MH);
        if (f.size() == 1) {
            from = $(f).head2();
        } else {
            throw new RuntimeException("Only one entity is allowed in the from clause");
        }
    }
    // 
    if (from instanceof QSnapshotDto) {
        // from Snapshot
        // 
        final BooleanExpression where = Expressions.allOf(c.targetPredicate.orNull(), c.where.apply(Q_SNAPSHOT));
        // get snapshots to delete
        // TODO ATTENTION: this query has the potential to yield a massive amount of elements
        // return the list of snapshots to delete them outside the transaction since
        // it may take a while.
        final List<Tuple> deletedSnapshots = jpa.query().from(Q_SNAPSHOT).where(where).list(Q_SNAPSHOT.organizationId, Q_SNAPSHOT.mediaPackageId, Q_SNAPSHOT.version);
        // <BLOCK>
        // TODO database only approach to determine deleted episodes
        // TODO does not run with H2 so unit tests break
        /*
SELECT
  e.mediapackage_id,
  count(*) AS v
FROM oc_assets_snapshot e
GROUP BY e.mediapackage_id
HAVING v = (SELECT count(*)
            FROM oc_assets_snapshot e2
            WHERE e.mediapackage_id = e2.mediapackage_id
                  AND
                  -- delete where clause
                  (e2.version = 2 OR e2.mediapackage_id = '24ec925e-ea57-43a5-a7bb-58dc5aae54dd')
            GROUP BY mediapackage_id);
       */
        // final QSnapshotDto e2 = new QSnapshotDto("eee");
        // final List<String> deletedSnapshots = jpa.query()
        // .from(e2)
        // .groupBy(e2.mediaPackageId)
        // .having(e2.count().eq(
        // jpa.subQuery()
        // .from(Q_SNAPSHOT)
        // .where(Q_SNAPSHOT.mediaPackageId.eq(e2.mediaPackageId).and(where))
        // .groupBy(Q_SNAPSHOT.mediaPackageId)
        // .count()))
        // .list(e2.mediaPackageId);
        // </BLOCK>
        // delete assets from database
        final JPADeleteClause qAssets = jpa.delete(Q_ASSET).where(Q_ASSET.snapshotId.in(new JPASubQuery().from(Q_SNAPSHOT).where(where).list(Q_SNAPSHOT.id)));
        am.getDb().logDelete(formatQueryName(c.name, "delete assets"), qAssets);
        qAssets.execute();
        // main delete query
        final JPADeleteClause qMain = jpa.delete(Q_SNAPSHOT).where(where);
        am.getDb().logDelete(formatQueryName(c.name, "main"), qMain);
        final long deletedItems = qMain.execute();
        // delete orphaned properties
        deleteOrphanedProperties();
        // <BLOCK>
        // TODO Bad solution. Yields all media package IDs which can easily be thousands
        // TODO The above SQL solution does not work with H2 so I suspect the query is not 100% clean
        // TODO Rework the query and replace this code.
        // calculate deleted episodes, i.e. where all snapshots have been deleted
        final Set<String> deletedEpisodes;
        {
            final List<String> remainingSnapshots = jpa.query().from(Q_SNAPSHOT).distinct().list(Q_SNAPSHOT.mediaPackageId);
            final Set<String> d = $(deletedSnapshots).map(new Fn<Tuple, String>() {

                @Override
                public String apply(Tuple tuple) {
                    return tuple.get(Q_SNAPSHOT.mediaPackageId);
                }
            }).toSet(SetB.MH);
            d.removeAll(remainingSnapshots);
            deletedEpisodes = Collections.unmodifiableSet(d);
        }
        // </BLOCK>
        return new DeletionResult(deletedItems, deletedSnapshots, deletedEpisodes);
    } else if (from instanceof QPropertyDto) {
        // from Property
        // 
        final BooleanExpression where;
        {
            final BooleanExpression w = c.where.apply(Q_PROPERTY);
            if (w != null) {
                /* The original sub query used an "ON" clause to filter the join by mediapackage id [1].
             Unfortunately Eclipse link drops this clause completely when transforming the query
             into SQL. It creates a cross join instead of the inner join, which is perfectly legal
             if the "ON" clause would be moved to the "WHERE" clause.
             The example [2] shows that neither an "ON" clause nor an additional "WHERE" predicate is generated.

             [1]
             new JPASubQuery()
                .from(Q_PROPERTY)
                .join(Q_SNAPSHOT) <- inner join
                .on(Q_PROPERTY.mediaPackageId.eq(Q_SNAPSHOT.mediaPackageId)) <- dropped by Eclipse link
                .where(Q_PROPERTY.mediaPackageId.eq(Q_SNAPSHOT.mediaPackageId).and(w))
                .distinct()
                .list(Q_PROPERTY.mediaPackageId)

             [2]
             SELECT DISTINCT t1.mediapackage_id FROM oc_assets_snapshot t2, oc_assets_properties t1 WHERE (t2.organization_id = ?)
           */
                where = Q_PROPERTY.mediaPackageId.in(new JPASubQuery().from(Q_PROPERTY).join(Q_SNAPSHOT).where(Q_PROPERTY.mediaPackageId.eq(Q_SNAPSHOT.mediaPackageId).and(w)).distinct().list(Q_PROPERTY.mediaPackageId));
            } else {
                where = null;
            }
        }
        final JPADeleteClause qProperties = jpa.delete(from).where(Expressions.allOf(c.targetPredicate.orNull(), where));
        am.getDb().logDelete(formatQueryName(c.name, "main"), qProperties);
        final long deletedItems = qProperties.execute();
        return new DeletionResult(deletedItems, Collections.<Tuple>emptyList(), Collections.<String>emptySet());
    } else {
        // from contains an unsupported entity
        throw new RuntimeException("[Bug]");
    }
}
Also used : Set(java.util.Set) JPADeleteClause(com.mysema.query.jpa.impl.JPADeleteClause) QPropertyDto(org.opencastproject.assetmanager.impl.persistence.QPropertyDto) BooleanExpression(com.mysema.query.types.expr.BooleanExpression) JPASubQuery(com.mysema.query.jpa.JPASubQuery) QSnapshotDto(org.opencastproject.assetmanager.impl.persistence.QSnapshotDto) List(java.util.List) Tuple(com.mysema.query.Tuple)

Example 3 with BooleanExpression

use of com.mysema.query.types.expr.BooleanExpression in project opencast by opencast.

the class PropertyPredicates method mkWhereDelete.

public static Where mkWhereDelete(final Opt<String> namespace, final Opt<String> propertyName, final Fn<QPropertyDto, Opt<BooleanExpression>> mkValueExpression) {
    final Opt<BooleanExpression> valueExpression = mkValueExpression.apply(Q_PROPERTY);
    final BooleanExpression propertyPredicate = (namespace.isSome() ? Q_PROPERTY.namespace.eq(namespace.get()) : // The isNotNull predicate prevents this.
    Q_PROPERTY.namespace.isNotNull()).and(propertyName.isSome() ? Q_PROPERTY.propertyName.eq(propertyName.get()) : null).and(valueExpression.isSome() ? valueExpression.get() : null);
    // 
    return new Where() {

        @Override
        public BooleanExpression fromSnapshot(QSnapshotDto e) {
            return new JPASubQuery().from(Q_PROPERTY).where(e.mediaPackageId.eq(Q_PROPERTY.mediaPackageId).and(propertyPredicate)).exists();
        }

        @Override
        public BooleanExpression fromProperty(QPropertyDto p) {
            return p.mediaPackageId.in(new JPASubQuery().from(p).where(propertyPredicate).distinct().list(p.mediaPackageId));
        }
    };
}
Also used : QPropertyDto(org.opencastproject.assetmanager.impl.persistence.QPropertyDto) BooleanExpression(com.mysema.query.types.expr.BooleanExpression) JPASubQuery(com.mysema.query.jpa.JPASubQuery) QSnapshotDto(org.opencastproject.assetmanager.impl.persistence.QSnapshotDto) Where(org.opencastproject.assetmanager.impl.query.DeleteQueryContribution.Where)

Example 4 with BooleanExpression

use of com.mysema.query.types.expr.BooleanExpression in project dwoss by gg-net.

the class CustomerEao method find.

/**
 * A more specific search based on parameters and possible wildcard handlig.
 * All parameters witch are blank will be ignored. The search itself ist like based, so explicit as implicit wildcards are possible
 * <p>
 * @param companyName    the {@link Company#name} of {@link Customer#companies}
 * @param firstName      the {@link Contact#firstName} of {@link Customer#contacts}
 * @param lastName       the {@link Contact#lastName} of {@link Customer#contacts}
 * @param email          the {@link Communication#identifier} with {@link Communication#type} == {@link Type#EMAIL} of {@link Contact#communications} of
 *                       {@link Customer#contacts}
 * @param appendWildcard if true, adds a '%' wildcard sign to all set parameters
 * @return a list of customers matching the paramters.
 */
public List<Customer> find(String companyName, String firstName, String lastName, String email, boolean appendWildcard) {
    WildCardHelper W = new WildCardHelper(appendWildcard);
    JPAQuery query = new JPAQuery(em).from(customer);
    if (!isBlank(companyName)) {
        query.join(customer.companies, company).on(company.name.lower().like(W.trim(companyName)));
    }
    if (!isBlank(firstName) || !isBlank(lastName)) {
        query.join(customer.contacts, contact).on();
        BooleanExpression on = null;
        if (!isBlank(firstName)) {
            on = contact.firstName.lower().like(W.trim(firstName));
        }
        if (!isBlank(lastName)) {
            BooleanExpression second = contact.lastName.lower().like(W.trim(lastName));
            if (on != null)
                on = on.and(second);
            else
                on = second;
        }
        query.on(on);
    }
    if (!isBlank(email)) {
        query.join(customer.contacts, contact).join(contact.communications, communication).on(communication.type.eq(EMAIL).and(communication.identifier.lower().like(W.trim(email))));
    }
    L.debug("calling query");
    List<Customer> list = query.list(customer);
    L.debug("Query successful wiht {}", list);
    return list;
}
Also used : BooleanExpression(com.mysema.query.types.expr.BooleanExpression) JPAQuery(com.mysema.query.jpa.impl.JPAQuery)

Aggregations

BooleanExpression (com.mysema.query.types.expr.BooleanExpression)4 JPASubQuery (com.mysema.query.jpa.JPASubQuery)2 JPAQuery (com.mysema.query.jpa.impl.JPAQuery)2 List (java.util.List)2 Set (java.util.Set)2 QPropertyDto (org.opencastproject.assetmanager.impl.persistence.QPropertyDto)2 QSnapshotDto (org.opencastproject.assetmanager.impl.persistence.QSnapshotDto)2 Fn (com.entwinemedia.fn.Fn)1 Fn2 (com.entwinemedia.fn.Fn2)1 Opt (com.entwinemedia.fn.data.Opt)1 Tuple (com.mysema.query.Tuple)1 JPADeleteClause (com.mysema.query.jpa.impl.JPADeleteClause)1 EntityPath (com.mysema.query.types.EntityPath)1 Expression (com.mysema.query.types.Expression)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 Property (org.opencastproject.assetmanager.api.Property)1 Snapshot (org.opencastproject.assetmanager.api.Snapshot)1 Where (org.opencastproject.assetmanager.impl.query.DeleteQueryContribution.Where)1