View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  package org.opencastproject.assetmanager.impl.query;
22  
23  import static com.entwinemedia.fn.Stream.$;
24  import static org.opencastproject.assetmanager.impl.query.PropertyPredicates.NONE;
25  import static org.opencastproject.assetmanager.impl.query.PropertyPredicates.NO_VALUE;
26  
27  import org.opencastproject.assetmanager.api.Availability;
28  import org.opencastproject.assetmanager.api.PropertyName;
29  import org.opencastproject.assetmanager.api.Value.ValueType;
30  import org.opencastproject.assetmanager.api.query.ADeleteQuery;
31  import org.opencastproject.assetmanager.api.query.AQueryBuilder;
32  import org.opencastproject.assetmanager.api.query.ASelectQuery;
33  import org.opencastproject.assetmanager.api.query.Field;
34  import org.opencastproject.assetmanager.api.query.Predicate;
35  import org.opencastproject.assetmanager.api.query.PropertyField;
36  import org.opencastproject.assetmanager.api.query.Target;
37  import org.opencastproject.assetmanager.api.query.VersionField;
38  import org.opencastproject.assetmanager.impl.AssetManagerImpl;
39  import org.opencastproject.assetmanager.impl.RuntimeTypes;
40  import org.opencastproject.assetmanager.impl.persistence.EntityPaths;
41  import org.opencastproject.assetmanager.impl.persistence.QPropertyDto;
42  import org.opencastproject.assetmanager.impl.persistence.QSnapshotDto;
43  import org.opencastproject.assetmanager.impl.query.DeleteQueryContribution.Where;
44  import org.opencastproject.util.RequireUtil;
45  
46  import com.entwinemedia.fn.Fn;
47  import com.entwinemedia.fn.Stream;
48  import com.entwinemedia.fn.data.Opt;
49  import com.mysema.query.jpa.impl.JPAQueryFactory;
50  import com.mysema.query.support.Expressions;
51  import com.mysema.query.types.expr.BooleanExpression;
52  
53  import java.util.Date;
54  
55  import javax.annotation.Nonnull;
56  
57  public final class AQueryBuilderImpl implements AQueryBuilder, EntityPaths {
58    private static final Stream<QSnapshotDto> FROM_SNAPSHOT = $Q_SNAPSHOT;
59  
60    private final AssetManagerImpl am;
61  
62    public AQueryBuilderImpl(AssetManagerImpl am) {
63      this.am = am;
64    }
65  
66    /** Convert a {@link Target} into a Querydsl {@link com.mysema.query.types.Expression}. */
67    private static Fn<Target, SelectQueryContribution> contributeSelect(final JPAQueryFactory f) {
68      return new Fn<Target, SelectQueryContribution>() {
69        @Override public SelectQueryContribution apply(Target t) {
70          return RuntimeTypes.convert(t).contributeSelect(f);
71        }
72      };
73    }
74  
75    @Override public ASelectQuery select(final Target... target) {
76      return new AbstractASelectQuery(am) {
77        @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
78          final Stream<SelectQueryContribution> c = $(target).map(AQueryBuilderImpl.contributeSelect(f));
79          return SelectQueryContribution.mk()
80                  .fetch(c.bind(SelectQueryContribution.getFetch))
81                  .from(c.bind(SelectQueryContribution.getFrom))
82                  .join(c.bind(SelectQueryContribution.getJoin))
83                  .where(Opt.nul(JpaFns.allOf(c.bind(SelectQueryContribution.getWhere))));
84        }
85      };
86    }
87  
88    @Override public ADeleteQuery delete(final String owner, final Target target) {
89      RequireUtil.notEmpty(owner, "owner");
90      return new AbstractADeleteQuery(am, owner) {
91        @Override public DeleteQueryContribution contributeDelete(String owner) {
92          final DeleteQueryContribution c = RuntimeTypes.convert(target).contributeDelete(owner);
93          return DeleteQueryContribution.mk()
94                  .from(c.from)
95                  .targetPredicate(c.targetPredicate)
96                  .where(c.where);
97  //                .where(new Fn<EntityPath<?>, BooleanExpression>() {
98  //                  @Override public BooleanExpression apply(EntityPath<?> path) {
99  //                    // Wildcard deletion. Disabled as of ticket CERV-1158. Kept for potentially later reference.
100 //                    // return !"".equals(owner)
101 //                    //     ? Q_SNAPSHOT.owner.eq(owner).and(c.where.apply(path))
102 //                    //     : c.where.apply(path);
103 //                    return Q_SNAPSHOT.owner.eq(owner).and(c.where.apply(path));
104 //                  }
105 //                });
106       }
107     };
108   }
109 
110   /* -- */
111   @Override public Predicate mediaPackageIds(final String... mpIds) {
112     return new AbstractPredicate() {
113       /* SELECT */
114       @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
115         return SelectQueryContribution.mk().from(FROM_SNAPSHOT).where(Q_SNAPSHOT.mediaPackageId.in(mpIds));
116       }
117 
118       /* DELETE */
119       @Override public DeleteQueryContribution contributeDelete(String owner) {
120         return DeleteQueryContribution.mk().where(new Where() {
121           @Override public BooleanExpression fromSnapshot(@Nonnull QSnapshotDto e) {
122             return e.mediaPackageId.in(mpIds);
123           }
124 
125           @Override public BooleanExpression fromProperty(@Nonnull QPropertyDto p) {
126             return p.mediaPackageId.in(mpIds);
127           }
128         });
129       }
130 
131     };
132   }
133 
134   @Override public Predicate mediaPackageId(final String mpId) {
135     return new AbstractPredicate() {
136       /* SELECT */
137       @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
138         return SelectQueryContribution.mk().from(FROM_SNAPSHOT).where(Q_SNAPSHOT.mediaPackageId.eq(mpId));
139       }
140 
141       /* DELETE */
142       @Override public DeleteQueryContribution contributeDelete(String owner) {
143         return DeleteQueryContribution.mk().where(new Where() {
144           @Override public BooleanExpression fromSnapshot(@Nonnull QSnapshotDto e) {
145             return e.mediaPackageId.eq(mpId);
146           }
147 
148           @Override public BooleanExpression fromProperty(@Nonnull QPropertyDto p) {
149             return p.mediaPackageId.eq(mpId);
150           }
151         });
152       }
153 
154     };
155   }
156 
157   @Override public Field<String> mediapackageId() {
158     return new SimpleSnapshotField<>(Q_SNAPSHOT.mediaPackageId);
159   }
160 
161   /**
162    * A predicate that is based on a simple snapshot field expression.
163    */
164   private abstract static class SnapshotBasedPredicate extends AbstractPredicate {
165     /* SELECT */
166     @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
167       return SelectQueryContribution.mk().from(FROM_SNAPSHOT).where(mkSnapshotFieldPredicate(Q_SNAPSHOT));
168     }
169 
170     /* DELETE */
171     @Override public DeleteQueryContribution contributeDelete(String owner) {
172       return DeleteQueryContribution.mk().where(new Where() {
173         @Override public BooleanExpression fromSnapshot(@Nonnull QSnapshotDto e) {
174           return mkSnapshotFieldPredicate(e);
175         }
176 
177         @Override public BooleanExpression fromProperty(@Nonnull QPropertyDto p) {
178           return mkSnapshotFieldPredicate(Q_SNAPSHOT);
179         }
180       });
181     }
182 
183     protected abstract BooleanExpression mkSnapshotFieldPredicate(QSnapshotDto e);
184   }
185 
186   @Override public Field<String> seriesId() {
187     return new SimpleSnapshotField<>(Q_SNAPSHOT.seriesId);
188   }
189 
190   @Override public Predicate organizationId(final String orgId) {
191     return new SnapshotBasedPredicate() {
192       @Override protected BooleanExpression mkSnapshotFieldPredicate(QSnapshotDto e) {
193         return e.organizationId.eq(orgId);
194       }
195     };
196   }
197 
198   @Override public Field<String> organizationId() {
199     return new SimpleSnapshotField<>(Q_SNAPSHOT.organizationId);
200   }
201 
202   @Override public Field<String> owner() {
203     return new SimpleSnapshotField<>(Q_SNAPSHOT.owner);
204   }
205 
206   @Override public Predicate availability(final Availability availability) {
207     return new SnapshotBasedPredicate() {
208       @Override protected BooleanExpression mkSnapshotFieldPredicate(QSnapshotDto e) {
209         return e.availability.eq(availability.name());
210       }
211     };
212   }
213 
214   @Override public Predicate storage(final String storage) {
215     return new SnapshotBasedPredicate() {
216       @Override protected BooleanExpression mkSnapshotFieldPredicate(QSnapshotDto e) {
217         return e.storageId.eq(storage);
218       }
219     };
220   }
221 
222   /* -- */
223 
224   // TODO DRY with #hasProperties
225   @Override public Predicate hasPropertiesOf(final String namespace) {
226     return new AbstractPredicate() {
227       /* SELECT */
228       @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
229         return SelectQueryContribution.mk()
230                 .where(PropertyPredicates.mkWhereSelect(Opt.some(namespace), NONE, NO_VALUE));
231       }
232 
233       /* DELETE */
234       @Override public DeleteQueryContribution contributeDelete(String owner) {
235         return DeleteQueryContribution.mk()
236                 .where(PropertyPredicates.mkWhereDelete(Opt.some(namespace), NONE, NO_VALUE));
237       }
238     };
239   }
240 
241   // TODO DRY with #hasPropertiesOf
242   @Override public Predicate hasProperties() {
243     return new AbstractPredicate() {
244       /* SELECT */
245       @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
246         return SelectQueryContribution.mk()
247                 .where(PropertyPredicates.mkWhereSelect(NONE, NONE, NO_VALUE));
248       }
249 
250       /* DELETE */
251       @Override public DeleteQueryContribution contributeDelete(String owner) {
252         return DeleteQueryContribution.mk()
253                 .where(PropertyPredicates.mkWhereDelete(NONE, NONE, NO_VALUE));
254       }
255     };
256   }
257 
258   @Override public Field<Date> archived() {
259     return new SimpleSnapshotField<>(Q_SNAPSHOT.archivalDate);
260   }
261 
262   @Override public VersionField version() {
263     return new VersionFieldImpl();
264   }
265 
266   @Override public <A> PropertyField<A> property(ValueType<A> ev, String namespace, String name) {
267     return new PropertyFieldImpl<>(ev, PropertyName.mk(namespace, name));
268   }
269 
270   @Override public <A> PropertyField<A> property(ValueType<A> ev, PropertyName fqn) {
271     return new PropertyFieldImpl<>(ev, fqn);
272   }
273 
274   @Override public Target snapshot() {
275     return new AbstractTarget() {
276       @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
277         return SelectQueryContribution.mk().from(FROM_SNAPSHOT).fetch($Q_SNAPSHOT);
278       }
279 
280       @Override public DeleteQueryContribution contributeDelete(String owner) {
281         return DeleteQueryContribution.mk().from(FROM_SNAPSHOT).where(Q_SNAPSHOT.owner.eq(owner));
282       }
283     };
284   }
285 
286   @Override public Target propertiesOf(final String... namespace) {
287     return propertyTarget(namespace);
288   }
289 
290   @Override public Target properties(PropertyName... fqn) {
291     return propertyTarget(fqn);
292   }
293 
294   @Override public Target nothing() {
295     return new AbstractTarget() {
296       @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
297         return SelectQueryContribution.mk();
298       }
299 
300       @Override public DeleteQueryContribution contributeDelete(String owner) {
301         return DeleteQueryContribution.mk();
302       }
303 
304     };
305   }
306 
307   @Override public Predicate always() {
308     return new AbstractPredicate() {
309       /* SELECT */
310       @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
311         return SelectQueryContribution.mk().where(Expressions.booleanTemplate("true = true"));
312       }
313 
314       /* DELETE */
315       @Override public DeleteQueryContribution contributeDelete(String owner) {
316         return DeleteQueryContribution.mk().where(Expressions.booleanTemplate("true = true"));
317       }
318     };
319   }
320 
321   //
322   //
323   //
324 
325   static Target propertyTarget(String... namespace) {
326     final Stream<BooleanExpression> onExpressions = $(namespace).map(new Fn<String, BooleanExpression>() {
327       @Override public BooleanExpression apply(String namespace) {
328         return Q_PROPERTY.namespace.eq(namespace);
329       }
330     });
331     return propertyTarget(onExpressions);
332   }
333 
334   static Target propertyTarget(PropertyName... fqn) {
335     final Stream<BooleanExpression> onExpressions = $(fqn).map(new Fn<PropertyName, BooleanExpression>() {
336       @Override public BooleanExpression apply(PropertyName name) {
337         return Q_PROPERTY.namespace.eq(name.getNamespace()).and(Q_PROPERTY.propertyName.eq(name.getName()));
338       }
339     });
340     return propertyTarget(onExpressions);
341   }
342 
343   /**
344    * Create a property target using the given, additional expressions for the join's on clause.
345    * On expressions are combined with logical "or".
346    */
347   private static Target propertyTarget(final Stream<BooleanExpression> onExpressions) {
348     return new AbstractTarget() {
349       @Override public SelectQueryContribution contributeSelect(JPAQueryFactory f) {
350         // join on the media package ID and the given expressions
351         final BooleanExpression on = Q_PROPERTY.mediaPackageId
352             .eq(Q_SNAPSHOT.mediaPackageId)
353             .and(JpaFns.anyOf(onExpressions));
354         return SelectQueryContribution.mk().join($(new Join(Q_SNAPSHOT, Q_PROPERTY, on))).fetch($Q_PROPERTY);
355       }
356 
357       @Override public DeleteQueryContribution contributeDelete(String owner) {
358         return DeleteQueryContribution.mk().from($Q_PROPERTY).targetPredicate(JpaFns.anyOf(onExpressions));
359       }
360     };
361   }
362 }