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  
22  package org.opencastproject.elasticsearch.index.objects.event;
23  
24  import static org.opencastproject.security.api.SecurityConstants.GLOBAL_ADMIN_ROLE;
25  
26  import org.opencastproject.elasticsearch.impl.AbstractSearchQuery;
27  import org.opencastproject.elasticsearch.impl.IndexSchema;
28  import org.opencastproject.security.api.Permissions;
29  import org.opencastproject.security.api.Permissions.Action;
30  import org.opencastproject.security.api.User;
31  import org.opencastproject.util.requests.SortCriterion.Order;
32  
33  import org.apache.commons.lang3.StringUtils;
34  
35  import java.util.ArrayList;
36  import java.util.Date;
37  import java.util.HashMap;
38  import java.util.HashSet;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Set;
42  
43  /**
44   * This interface defines a fluent api for a query object used to lookup recording events in the search index.
45   */
46  public class EventSearchQuery extends AbstractSearchQuery {
47  
48    private final List<String> identifiers = new ArrayList<String>();
49    private String organization = null;
50    private User user = null;
51    private String title = null;
52    private String description = null;
53    private final Set<String> actions = new HashSet<String>();
54    private final List<String> presenters = new ArrayList<String>();
55    private final List<String> contributors = new ArrayList<String>();
56    private String subject = null;
57    private Map<String, Map<String, List<String>>> extendedMetadata = new HashMap<>();
58    private String location = null;
59    private String seriesId = null;
60    private String seriesName = null;
61    private String language = null;
62    private String source = null;
63    private String created = null;
64    private Date startFrom = null;
65    private Date startTo = null;
66    private Date technicalStartFrom = null;
67    private Date technicalStartTo = null;
68    private String creator = null;
69    private String publisher = null;
70    private String license = null;
71    private String rights = null;
72    private String accessPolicy = null;
73    private String managedAcl = null;
74    private String workflowState = null;
75    private Long workflowId = null;
76    private String workflowDefinition = null;
77    private Long duration = null;
78    private String startDate = null;
79    private String eventStatus = null;
80    private Boolean hasComments = null;
81    private Boolean hasOpenComments = null;
82    private final List<String> comments = new ArrayList<>();
83    private Boolean needsCutting = null;
84    private final List<String> publications = new ArrayList<String>();
85    private Boolean isPublished = null;
86    private Long archiveVersion = null;
87    private String agentId = null;
88    private Date technicalStartTime = null;
89    private Date technicalEndTime = null;
90    private final List<String> technicalPresenters = new ArrayList<String>();
91    private final Map<String, String> accessControlEntries = new HashMap<>();
92  
93    private static final Map<String, String> SORT_FIELDS = Map.of(
94            EventIndexSchema.TITLE, EventIndexSchema.TITLE.concat(IndexSchema.SORT_FIELD_NAME_EXTENSION),
95            EventIndexSchema.CONTRIBUTOR, EventIndexSchema.CONTRIBUTOR.concat(IndexSchema.SORT_FIELD_NAME_EXTENSION),
96            EventIndexSchema.PRESENTER, EventIndexSchema.PRESENTER.concat(IndexSchema.SORT_FIELD_NAME_EXTENSION),
97            EventIndexSchema.SUBJECT, EventIndexSchema.SUBJECT.concat(IndexSchema.SORT_FIELD_NAME_EXTENSION),
98            EventIndexSchema.DESCRIPTION, EventIndexSchema.DESCRIPTION.concat(IndexSchema.SORT_FIELD_NAME_EXTENSION),
99            EventIndexSchema.LOCATION, EventIndexSchema.LOCATION.concat(IndexSchema.SORT_FIELD_NAME_EXTENSION),
100           EventIndexSchema.SERIES_NAME, EventIndexSchema.SERIES_NAME.concat(IndexSchema.SORT_FIELD_NAME_EXTENSION),
101           EventIndexSchema.CREATOR, EventIndexSchema.CREATOR.concat(IndexSchema.SORT_FIELD_NAME_EXTENSION),
102           EventIndexSchema.PUBLISHER, EventIndexSchema.PUBLISHER.concat(IndexSchema.SORT_FIELD_NAME_EXTENSION)
103   );
104 
105   @SuppressWarnings("unused")
106   private EventSearchQuery() {
107   }
108 
109   @Override
110   protected String sortOrderFieldName(String field) {
111     if (SORT_FIELDS.containsKey(field)) {
112       return SORT_FIELDS.get(field);
113     }
114     return field;
115   }
116 
117   /**
118    * Creates a query that will return event documents.
119    */
120   public EventSearchQuery(String organization, User user) {
121     super(Event.DOCUMENT_TYPE);
122     this.organization = organization;
123     this.user = user;
124     this.actions.add(Permissions.Action.READ.toString());
125     if (!user.hasRole(GLOBAL_ADMIN_ROLE)) {
126       if (!user.getOrganization().getId().equals(organization)) {
127         throw new IllegalStateException("User's organization must match search organization");
128       }
129     }
130   }
131 
132   /**
133    * Selects recording events with the given identifier.
134    * <p>
135    * Note that this method may be called multiple times to support selection of multiple recording events.
136    *
137    * @param id
138    *          the recording identifier
139    * @return the enhanced search query
140    */
141   public EventSearchQuery withIdentifier(String id) {
142     if (StringUtils.isBlank(id)) {
143       throw new IllegalArgumentException("Identifier cannot be null");
144     }
145     this.identifiers.add(id);
146     return this;
147   }
148 
149   /**
150    * Returns the list of recording identifiers or an empty array if no identifiers have been specified.
151    *
152    * @return the identifiers
153    */
154   public String[] getIdentifier() {
155     return identifiers.toArray(new String[identifiers.size()]);
156   }
157 
158   /**
159    * Returns the organization of the recording
160    *
161    * @return the organization
162    */
163   public String getOrganization() {
164     return organization;
165   }
166 
167   /**
168    * Returns the user of this search query
169    *
170    * @return the user of this search query
171    */
172   public User getUser() {
173     return user;
174   }
175 
176   /**
177    * Selects recordings with the given title.
178    *
179    * @param title
180    *          the title
181    * @return the enhanced search query
182    */
183   public EventSearchQuery withTitle(String title) {
184     this.title = title;
185     return this;
186   }
187 
188   /**
189    * Returns the title of the recording.
190    *
191    * @return the title
192    */
193   public String getTitle() {
194     return title;
195   }
196 
197   /**
198    * Filter the recording events without any action checked.
199    *
200    * @return the enhanced search query
201    */
202   public EventSearchQuery withoutActions() {
203     this.actions.clear();
204     return this;
205   }
206 
207   /**
208    * Filter the recording events with the given action.
209    * <p>
210    * Note that this method may be called multiple times to support filtering by multiple actions.
211    *
212    * @param action
213    *          the action
214    * @return the enhanced search query
215    */
216   public EventSearchQuery withAction(Action action) {
217     if (action == null) {
218       throw new IllegalArgumentException("Action cannot be null");
219     }
220     this.actions.add(action.toString());
221     return this;
222   }
223 
224   /**
225    * Returns the list of actions or an empty array if no actions have been specified.
226    *
227    * @return the actions
228    */
229   public String[] getActions() {
230     return actions.toArray(new String[actions.size()]);
231   }
232 
233   /**
234    * Filter the recording events with the given access control entry.
235    *
236    * @param role
237    * @param action
238    * @return
239    */
240   public EventSearchQuery withAccessControlEntry(String role, Action action) {
241     if (StringUtils.isBlank(role)) {
242       throw new IllegalArgumentException("Role cannot be null");
243     }
244     if (action == null) {
245       throw new IllegalArgumentException("Action cannot be null");
246     }
247     this.accessControlEntries.put(role, action.toString());
248     return this;
249   }
250 
251   /**
252    * Returns the list of access control entries or an empty map if no access control entries have been specified.
253    *
254    * @return the access control entries
255    */
256   public Map<String, String> getAccessControlEntries() {
257     return accessControlEntries;
258   }
259 
260   /**
261    * Selects recording events with the given presenter.
262    * <p>
263    * Note that this method may be called multiple times to support selection of multiple recording events.
264    *
265    * @param presenter
266    *          the presenter
267    * @return the enhanced search query
268    */
269   public EventSearchQuery withPresenter(String presenter) {
270     if (StringUtils.isBlank(presenter)) {
271       throw new IllegalArgumentException("Presenter cannot be null");
272     }
273     this.presenters.add(presenter);
274     return this;
275   }
276 
277   /**
278    * Returns the list of recording presenters or an empty array if no presenter have been specified.
279    *
280    * @return the presenters
281    */
282   public String[] getPresenters() {
283     return presenters.toArray(new String[presenters.size()]);
284   }
285 
286   /**
287    * Selects recording events with the given contributor.
288    * <p>
289    * Note that this method may be called multiple times to support selection of multiple recording events.
290    *
291    * @param contributor
292    *          the contributor
293    * @return the enhanced search query
294    */
295   public EventSearchQuery withContributor(String contributor) {
296     if (StringUtils.isBlank(contributor)) {
297       throw new IllegalArgumentException("Contributor cannot be null");
298     }
299     this.contributors.add(contributor);
300     return this;
301   }
302 
303   /**
304    * Returns the list of recording contributors or an empty array if no contributors have been specified.
305    *
306    * @return the contributors
307    */
308   public String[] getContributors() {
309     return contributors.toArray(new String[contributors.size()]);
310   }
311 
312   /**
313    * Selects recording events with the given subject.
314    *
315    * @param subject
316    *          the subject
317    * @return the enhanced search query
318    */
319   public EventSearchQuery withSubject(String subject) {
320     this.subject = subject;
321     return this;
322   }
323 
324   /**
325    * Returns the subject of the recording.
326    *
327    * @return the subject
328    */
329   public String getSubject() {
330     return subject;
331   }
332 
333   /**
334    * Selects recording events with the given subject.
335    *
336    * @param flavor
337    *          the flavor of the catalog
338    * @param key
339    *          the metadata field key
340    * @param value
341    *           the metadata field value
342    * @return the enhanced search query
343    */
344   public EventSearchQuery withExtendedMetadata(String flavor, String key, String value) {
345     this.extendedMetadata.computeIfAbsent(flavor, h -> new HashMap<>())
346         .computeIfAbsent(key, a -> new ArrayList<>())
347         .add(value);
348     return this;
349   }
350 
351   /**
352    * Returns the subject of the recording.
353    *
354    * @return the subject
355    */
356   public Map<String, Map<String, List<String>>> getExtendedMetadata() {
357     return extendedMetadata;
358   }
359 
360   /**
361    * Selects recordings with the given description.
362    *
363    * @param description
364    *          the description
365    * @return the enhanced search query
366    */
367   public EventSearchQuery withDescription(String description) {
368     this.description = description;
369     return this;
370   }
371 
372   /**
373    * Returns the description of the recording.
374    *
375    * @return the description
376    */
377   public String getDescription() {
378     return description;
379   }
380 
381   /**
382    * Selects recordings with the given location.
383    *
384    * @param location
385    *          the location
386    * @return the enhanced search query
387    */
388   public EventSearchQuery withLocation(String location) {
389     this.location = location;
390     return this;
391   }
392 
393   /**
394    * Returns the location of the recording.
395    *
396    * @return the location
397    */
398   public String getLocation() {
399     return location;
400   }
401 
402   /**
403    * Selects recordings with the given series identifier.
404    *
405    * @param seriesId
406    *          the series identifier
407    * @return the enhanced search query
408    */
409   public EventSearchQuery withSeriesId(String seriesId) {
410     this.seriesId = seriesId;
411     return this;
412   }
413 
414   /**
415    * Returns the series identifier of the recording.
416    *
417    * @return the series identifier
418    */
419   public String getSeriesId() {
420     return seriesId;
421   }
422 
423   /**
424    * Selects recordings with the given series name.
425    *
426    * @param seriesName
427    *          the series name
428    * @return the enhanced search query
429    */
430   public EventSearchQuery withSeriesName(String seriesName) {
431     this.seriesName = seriesName;
432     return this;
433   }
434 
435   /**
436    * Returns the series name of the recording.
437    *
438    * @return the series name
439    */
440   public String getSeriesName() {
441     return seriesName;
442   }
443 
444   /**
445    * Selects recordings with the given language.
446    *
447    * @param language
448    *          the language
449    * @return the enhanced search query
450    */
451   public EventSearchQuery withLanguage(String language) {
452     this.language = language;
453     return this;
454   }
455 
456   /**
457    * Returns the language of the recording.
458    *
459    * @return the language
460    */
461   public String getLanguage() {
462     return language;
463   }
464 
465   /**
466    * Selects recordings with the given source type.
467    *
468    * @param source
469    *          the source
470    * @return the enhanced search query
471    */
472   public EventSearchQuery withSource(String source) {
473     this.source = source;
474     return this;
475   }
476 
477   /**
478    * Returns the source of the recording.
479    *
480    * @return the source
481    */
482   public String getSource() {
483     return source;
484   }
485 
486   /**
487    * Selects recordings with the given creation date.
488    *
489    * @param created
490    *          the creation date
491    * @return the enhanced search query
492    */
493   public EventSearchQuery withCreated(String created) {
494     this.created = created;
495     return this;
496   }
497 
498   /**
499    * Returns the creation date of the recording.
500    *
501    * @return the creation date
502    */
503   public String getCreated() {
504     return created;
505   }
506 
507   /**
508    * The start date to start looking for events.
509    *
510    * @param startFrom
511    *          The start date to start looking for events
512    * @return the enhanced search query
513    */
514   public EventSearchQuery withStartFrom(Date startFrom) {
515     this.startFrom = startFrom;
516     return this;
517   }
518 
519   /**
520    * @return The Date after which all events returned should have been started
521    */
522   public Date getStartFrom() {
523     return startFrom;
524   }
525 
526   /**
527    * The start date to stop looking for events.
528    *
529    * @param startTo
530    *          The start date to stop looking for events
531    * @return the enhanced search query
532    */
533   public EventSearchQuery withStartTo(Date startTo) {
534     this.startTo = startTo;
535     return this;
536   }
537 
538   /**
539    * @return The Date before which all events returned should have been started
540    */
541   public Date getStartTo() {
542     return startTo;
543   }
544 
545   /**
546    * The technical start date to start looking for events.
547    *
548    * @param startFrom
549    *          The technical start date to start looking for events
550    * @return the enhanced search query
551    */
552   public EventSearchQuery withTechnicalStartFrom(Date startFrom) {
553     this.technicalStartFrom = startFrom;
554     return this;
555   }
556 
557   /**
558    * @return The technical date after which all events returned should have been started
559    */
560   public Date getTechnicalStartFrom() {
561     return technicalStartFrom;
562   }
563 
564   /**
565    * The technical start date to stop looking for events.
566    *
567    * @param startTo
568    *          The technical start date to stop looking for events
569    * @return the enhanced search query
570    */
571   public EventSearchQuery withTechnicalStartTo(Date startTo) {
572     this.technicalStartTo = startTo;
573     return this;
574   }
575 
576   /**
577    * @return The technical date before which all events returned should have been started
578    */
579   public Date getTechnicalStartTo() {
580     return technicalStartTo;
581   }
582 
583   /**
584    * Selects recordings with the given creator.
585    *
586    * @param creator
587    *          the creator
588    * @return the enhanced search query
589    */
590   public EventSearchQuery withCreator(String creator) {
591     this.creator = creator;
592     return this;
593   }
594 
595   /**
596    * Returns the creator of the recording.
597    *
598    * @return the creator
599    */
600   public String getCreator() {
601     return creator;
602   }
603 
604   /**
605    * Selects recordings with the given publisher.
606    *
607    * @param publisher
608    *          the publisher
609    * @return the enhanced search query
610    */
611   public EventSearchQuery withPublisher(String publisher) {
612     this.publisher = publisher;
613     return this;
614   }
615 
616   /**
617    * Returns the publisher of the recording.
618    *
619    * @return the publisher
620    */
621   public String getPublisher() {
622     return publisher;
623   }
624 
625   /**
626    * Selects recordings with the given license.
627    *
628    * @param license
629    *          the license
630    * @return the enhanced search query
631    */
632   public EventSearchQuery withLicense(String license) {
633     this.license = license;
634     return this;
635   }
636 
637   /**
638    * Returns the license of the recording.
639    *
640    * @return the license
641    */
642   public String getLicense() {
643     return license;
644   }
645 
646   /**
647    * Selects recordings with the given rights.
648    *
649    * @param rights
650    *          the rights
651    * @return the enhanced search query
652    */
653   public EventSearchQuery withRights(String rights) {
654     this.rights = rights;
655     return this;
656   }
657 
658   /**
659    * Returns the rights of the recording.
660    *
661    * @return the rights
662    */
663   public String getRights() {
664     return rights;
665   }
666 
667   /**
668    * Selects recordings with the given access policy.
669    *
670    * @param accessPolicy
671    *          the access policy
672    * @return the enhanced search query
673    */
674   public EventSearchQuery withAccessPolicy(String accessPolicy) {
675     this.accessPolicy = accessPolicy;
676     return this;
677   }
678 
679   /**
680    * Returns the access policy of the recording.
681    *
682    * @return the access policy
683    */
684   public String getAccessPolicy() {
685     return accessPolicy;
686   }
687 
688   /**
689    * Selects recordings with the given managed ACL name.
690    *
691    * @param managedAcl
692    *          the name of the managed ACL
693    * @return the enhanced search query
694    */
695   public EventSearchQuery withManagedAcl(String managedAcl) {
696     this.managedAcl = managedAcl;
697     return this;
698   }
699 
700   /**
701    * Returns the name of the managed ACL set to the recording.
702    *
703    * @return the name of the managed ACL
704    */
705   public String getManagedAcl() {
706     return managedAcl;
707   }
708 
709   /**
710    * Selects recordings with the given workflow state.
711    *
712    * @param workflowState
713    *          the workflow state
714    * @return the enhanced search query
715    */
716   public EventSearchQuery withWorkflowState(String workflowState) {
717     this.workflowState = workflowState;
718     return this;
719   }
720 
721   /**
722    * Returns the workflow state of the recording.
723    *
724    * @return the workflow state
725    */
726   public String getWorkflowState() {
727     return workflowState;
728   }
729 
730   /**
731    * Selects recordings with the given workflow id.
732    *
733    * @param workflowId
734    *          the workflow id
735    * @return the enhanced search query
736    */
737   public EventSearchQuery withWorkflowId(long workflowId) {
738     this.workflowId = workflowId;
739     return this;
740   }
741 
742   /**
743    * Returns the workflow id of the recording.
744    *
745    * @return the workflow id
746    */
747   public Long getWorkflowId() {
748     return workflowId;
749   }
750 
751   /**
752    * Selects recordings with the given workflow definition.
753    *
754    * @param workflowDefinition
755    *          the workflow definition
756    * @return the enhanced search query
757    */
758   public EventSearchQuery withWorkflowDefinition(String workflowDefinition) {
759     this.workflowDefinition = workflowDefinition;
760     return this;
761   }
762 
763   /**
764    * Returns the workflow definition of the recording.
765    *
766    * @return the workflow definition
767    */
768   public String getWorkflowDefinition() {
769     return workflowDefinition;
770   }
771 
772   /**
773    * Selects recordings with the given start date.
774    *
775    * @param startDate
776    *          the start date
777    * @return the enhanced search query
778    */
779   public EventSearchQuery withStartDate(String startDate) {
780     this.startDate = startDate;
781     return this;
782   }
783 
784   /**
785    * Returns the start date of the recording.
786    *
787    * @return the start date
788    */
789   public String getStartDate() {
790     return startDate;
791   }
792 
793   /**
794    * Selects recordings with the given duration.
795    *
796    * @param duration
797    *          the duration
798    * @return the enhanced search query
799    */
800   public EventSearchQuery withDuration(long duration) {
801     this.duration = duration;
802     return this;
803   }
804 
805   /**
806    * Returns the duration of the recording.
807    *
808    * @return the duration
809    */
810   public Long getDuration() {
811     return duration;
812   }
813 
814   /**
815    * Selects recordings with the given event status.
816    *
817    * @param eventStatus
818    *          the event status
819    * @return the enhanced search query
820    */
821   public EventSearchQuery withEventStatus(String eventStatus) {
822     this.eventStatus = eventStatus;
823     return this;
824   }
825 
826   /**
827    * Returns the event status of the recording.
828    *
829    * @return the event status
830    */
831   public String getEventStatus() {
832     return eventStatus;
833   }
834 
835   /**
836    * Selects recordings with the given has comments status.
837    *
838    * @param hasComments
839    *          the has comments status
840    * @return the enhanced search query
841    */
842   public EventSearchQuery withComments(boolean hasComments) {
843     this.hasComments = hasComments;
844     return this;
845   }
846 
847   /**
848    * Selects recordings with the given has open comments status.
849    *
850    * @param hasOpenComments
851    *          the has open comments status
852    * @return the enhanced search query
853    */
854   public EventSearchQuery withOpenComments(boolean hasOpenComments) {
855     this.hasOpenComments = hasOpenComments;
856     return this;
857   }
858 
859   /**
860    * Selects recordings with the given has comment need cutting status.
861    *
862    * @param needsCutting
863    *          the event has the comments status that it needs cutting
864    * @return the enhanced search query
865    */
866   public EventSearchQuery withNeedsCutting(boolean needsCutting) {
867     this.needsCutting = needsCutting;
868     return this;
869   }
870 
871   /**
872    * Returns the has comments status of the recording.
873    *
874    * @return the recording has comments status
875    */
876   public Boolean getHasComments() {
877     return hasComments;
878   }
879 
880   /**
881    * Returns the has open comments status of the recording.
882    *
883    * @return the recording has open comments status
884    */
885   public Boolean getHasOpenComments() {
886     return hasOpenComments;
887   }
888 
889   public EventSearchQuery withComments(String comment) {
890     if (StringUtils.isBlank(comment)) {
891       throw new IllegalArgumentException("Comment cannot be null");
892     }
893     this.comments.add(comment);
894     return this;
895   }
896 
897   public String[] getComments() {
898     return comments.toArray(new String[comments.size()]);
899   }
900 
901   /**
902    * Returns the has open comments reason that it needs cutting of the recording.
903    *
904    * @return the event has the open comments status that it needs cutting
905    */
906   public Boolean needsCutting() {
907     return needsCutting;
908   }
909 
910   /**
911    * Selects recording events with the given publication.
912    * <p>
913    * Note that this method may be called multiple times to support selection of multiple recording events.
914    *
915    * @param publication
916    *          the publication
917    * @return the enhanced search query
918    */
919   public EventSearchQuery withPublications(String publication) {
920     if (StringUtils.isBlank(publication)) {
921       throw new IllegalArgumentException("Publication cannot be null");
922     }
923     this.publications.add(publication);
924     return this;
925   }
926 
927   /**
928    * Returns the list of event publications or an empty array if no publications have been specified.
929    *
930    * @return the publications
931    */
932   public String[] getPublications() {
933     return publications.toArray(new String[publications.size()]);
934   }
935 
936   /**
937    * Selects recordings with the given is published status.
938    *
939    * @param isPublished
940    *          the is published status
941    * @return the enhanced search query
942    */
943   public EventSearchQuery withIsPublished(boolean isPublished) {
944     this.isPublished = isPublished;
945     return this;
946   }
947 
948   /**
949    * Returns the is published status of the recording.
950    *
951    * @return the recording is published status
952    */
953   public Boolean getIsPublished() {
954     return isPublished;
955   }
956 
957   /**
958    * Selects events with the given archive version.
959    *
960    * @param archiveVersion
961    *          the archive version
962    * @return the enhanced search query
963    */
964   public EventSearchQuery withArchiveVersion(long archiveVersion) {
965     this.archiveVersion = archiveVersion;
966     return this;
967   }
968 
969   /**
970    * Returns the archive version of the event.
971    *
972    * @return the archive version
973    */
974   public Long getArchiveVersion() {
975     return archiveVersion;
976   }
977 
978   /**
979    * Selects recordings with the given agent id.
980    *
981    * @param agentId
982    *          the agent id
983    * @return the enhanced search query
984    */
985   public EventSearchQuery withAgentId(String agentId) {
986     this.agentId = agentId;
987     return this;
988   }
989 
990   /**
991    * Returns the agent id of the recording.
992    *
993    * @return the agent id
994    */
995   public String getAgentId() {
996     return agentId;
997   }
998 
999   /**
1000    * Selects recordings with the given technical start date.
1001    *
1002    * @param technicalStartTime
1003    *          the start date
1004    * @return the enhanced search query
1005    */
1006   public EventSearchQuery withTechnicalStartTime(Date technicalStartTime) {
1007     this.technicalStartTime = technicalStartTime;
1008     return this;
1009   }
1010 
1011   /**
1012    * Returns the technical start date of the recording.
1013    *
1014    * @return the technical start date
1015    */
1016   public Date getTechnicalStartTime() {
1017     return technicalStartTime;
1018   }
1019 
1020   /**
1021    * Selects recordings with the given technical end date.
1022    *
1023    * @param technicalEndTime
1024    *          the end date
1025    * @return
1026    * @return the enhanced search query
1027    */
1028   public EventSearchQuery withTechnicalEndTime(Date technicalEndTime) {
1029     this.technicalEndTime = technicalEndTime;
1030     return this;
1031   }
1032 
1033   /**
1034    * Returns the technical end date of the recording.
1035    *
1036    * @return the technical end date
1037    */
1038   public Date getTechnicalEndTime() {
1039     return technicalEndTime;
1040   }
1041 
1042   /**
1043    * Selects recording events with the given technical presenters.
1044    * <p>
1045    * Note that this method may be called multiple times to support selection of multiple recording events.
1046    *
1047    * @param presenter
1048    *          the presenter
1049    * @return the enhanced search query
1050    */
1051   public EventSearchQuery withTechnicalPresenters(String presenter) {
1052     if (StringUtils.isBlank(presenter)) {
1053       throw new IllegalArgumentException("Presenter cannot be null");
1054     }
1055     this.technicalPresenters.add(presenter);
1056     return this;
1057   }
1058 
1059   /**
1060    * Returns the list of technical presenters or an empty array if no presenters have been specified.
1061    *
1062    * @return the technical presenters
1063    */
1064   public String[] getTechnicalPresenters() {
1065     return technicalPresenters.toArray(new String[technicalPresenters.size()]);
1066   }
1067 
1068   /**
1069    * Defines the sort order for the recording start date.
1070    *
1071    * @param order
1072    *          the order
1073    * @return the enhanced search query
1074    */
1075   public EventSearchQuery sortByStartDate(Order order) {
1076     withSortOrder(EventIndexSchema.START_DATE, order);
1077     return this;
1078   }
1079 
1080   /**
1081    * Returns the sort order for the recording start date.
1082    *
1083    * @return the sort order
1084    */
1085   public Order getStartDateSortOrder() {
1086     return getSortOrder(EventIndexSchema.START_DATE);
1087   }
1088 
1089   /**
1090    * Defines the sort order for the technical recording start date.
1091    *
1092    * @param order
1093    *          the order
1094    * @return the enhanced search query
1095    */
1096   public EventSearchQuery sortByTechnicalStartDate(Order order) {
1097     withSortOrder(EventIndexSchema.TECHNICAL_START, order);
1098     return this;
1099   }
1100 
1101   /**
1102    * Returns the sort order for the technical recording start date.
1103    *
1104    * @return the sort order
1105    */
1106   public Order getTechnicalStartDateSortOrder() {
1107     return getSortOrder(EventIndexSchema.TECHNICAL_START);
1108   }
1109 
1110   /**
1111    * Defines the sort order for the recording end date.
1112    *
1113    * @param order
1114    *          the order
1115    * @return the enhanced search query
1116    */
1117   public EventSearchQuery sortByEndDate(Order order) {
1118     withSortOrder(EventIndexSchema.END_DATE, order);
1119     return this;
1120   }
1121 
1122   /**
1123    * Returns the sort order for the recording end date.
1124    *
1125    * @return the sort order
1126    */
1127   public Order getEndDateSortOrder() {
1128     return getSortOrder(EventIndexSchema.END_DATE);
1129   }
1130 
1131   /**
1132    * Defines the sort order for the technical recording end date.
1133    *
1134    * @param order
1135    *          the order
1136    * @return the enhanced search query
1137    */
1138   public EventSearchQuery sortByTechnicalEndDate(Order order) {
1139     withSortOrder(EventIndexSchema.TECHNICAL_END, order);
1140     return this;
1141   }
1142 
1143   /**
1144    * Returns the sort order for the technical recording end date.
1145    *
1146    * @return the sort order
1147    */
1148   public Order getTechnicalEndDateSortOrder() {
1149     return getSortOrder(EventIndexSchema.TECHNICAL_END);
1150   }
1151 
1152   /**
1153    * Defines the sort order for the recording date.
1154    *
1155    * @param order
1156    *          the order
1157    * @return the enhanced search query
1158    */
1159   public EventSearchQuery sortByDate(Order order) {
1160     withSortOrder(EventIndexSchema.END_DATE, order);
1161     return this;
1162   }
1163 
1164   /**
1165    * Returns the sort order for the recording date.
1166    *
1167    * @return the sort order
1168    */
1169   public Order getDateSortOrder() {
1170     return getSortOrder(EventIndexSchema.END_DATE);
1171   }
1172 
1173   /**
1174    * Defines the sort order for the recording date.
1175    *
1176    * @param order
1177    *          the order
1178    * @return the enhanced search query
1179    */
1180   public EventSearchQuery sortByTitle(Order order) {
1181     withSortOrder(EventIndexSchema.TITLE, order);
1182     return this;
1183   }
1184 
1185   /**
1186    * Returns the sort order for the recording start date.
1187    *
1188    * @return the sort order
1189    */
1190   public Order getTitleSortOrder() {
1191     return getSortOrder(EventIndexSchema.TITLE);
1192   }
1193 
1194   public EventSearchQuery sortByUID(Order order) {
1195     withSortOrder(EventIndexSchema.UID, order);
1196     return this;
1197   }
1198 
1199   public Order getUIDSortOrder() {
1200     return getSortOrder(EventIndexSchema.UID);
1201   }
1202 
1203   /**
1204    * Defines the sort order for the recording date.
1205    *
1206    * @param order
1207    *          the order
1208    * @return the enhanced search query
1209    */
1210   public EventSearchQuery sortByPresenter(Order order) {
1211     withSortOrder(EventIndexSchema.PRESENTER, order);
1212     return this;
1213   }
1214 
1215   /**
1216    * Returns the sort order for the recording start date.
1217    *
1218    * @return the sort order
1219    */
1220   public Order getPresentersSortOrder() {
1221     return getSortOrder(EventIndexSchema.PRESENTER);
1222   }
1223 
1224   /**
1225    * Defines the sort order for the location.
1226    *
1227    * @param order
1228    *          the sort order
1229    * @return the updated query
1230    */
1231   public EventSearchQuery sortByLocation(Order order) {
1232     withSortOrder(EventIndexSchema.LOCATION, order);
1233     return this;
1234   }
1235 
1236   /**
1237    * Returns the sort order for the location.
1238    *
1239    * @return the sort order
1240    */
1241   public Order getLocationSortOrder() {
1242     return getSortOrder(EventIndexSchema.LOCATION);
1243   }
1244 
1245   /**
1246    * Defines the sort order for the series name.
1247    *
1248    * @param order
1249    *          the sort order
1250    * @return the updated query
1251    */
1252   public EventSearchQuery sortBySeriesName(Order order) {
1253     withSortOrder(EventIndexSchema.SERIES_NAME, order);
1254     return this;
1255   }
1256 
1257   /**
1258    * Returns the sort order for the series name.
1259    *
1260    * @return the sort order
1261    */
1262   public Order getSeriesNameSortOrder() {
1263     return getSortOrder(EventIndexSchema.SERIES_NAME);
1264   }
1265 
1266   /**
1267    * Defines the sort order for the managed ACL.
1268    *
1269    * @param order
1270    *          the order
1271    * @return the enhanced search query
1272    */
1273   public EventSearchQuery sortByManagedAcl(Order order) {
1274     withSortOrder(EventIndexSchema.MANAGED_ACL, order);
1275     return this;
1276   }
1277 
1278   /**
1279    * Returns the sort order for the series managed ACL.
1280    *
1281    * @return the sort order
1282    */
1283   public Order getManagedAclSortOrder() {
1284     return getSortOrder(EventIndexSchema.MANAGED_ACL);
1285   }
1286 
1287   /**
1288    * Defines the sort order for the workflow state.
1289    *
1290    * @param order
1291    *          the sort order
1292    * @return the updated query
1293    */
1294   public EventSearchQuery sortByWorkflowState(Order order) {
1295     withSortOrder(EventIndexSchema.WORKFLOW_STATE, order);
1296     return this;
1297   }
1298 
1299   /**
1300    * Returns the sort order for the workflow state.
1301    *
1302    * @return the sort order
1303    */
1304   public Order getWorkflowStateSortOrder() {
1305     return getSortOrder(EventIndexSchema.WORKFLOW_STATE);
1306   }
1307 
1308   /**
1309    * Defines the sort order for the event status.
1310    *
1311    * @param order
1312    *          the sort order
1313    * @return the updated query
1314    */
1315   public EventSearchQuery sortByEventStatus(Order order) {
1316     withSortOrder(EventIndexSchema.EVENT_STATUS, order);
1317     return this;
1318   }
1319 
1320   /**
1321    * Returns the sort order for the event status.
1322    *
1323    * @return the sort order
1324    */
1325   public Order getEventStatusSortOrder() {
1326     return getSortOrder(EventIndexSchema.EVENT_STATUS);
1327   }
1328 
1329   /**
1330    * Defines the sort order for publication.
1331    *
1332    * @param order
1333    *          the sort order
1334    * @return the updated query
1335    */
1336   public EventSearchQuery sortByPublicationIgnoringInternal(Order order) {
1337     // TODO implement sort By Publication Ignoring Internal
1338     withSortOrder(EventIndexSchema.PUBLICATION, order);
1339     return this;
1340   }
1341 
1342   /**
1343    * Returns the sort order for the publication.
1344    *
1345    * @return the sort order
1346    */
1347   public Order getPublicationSortOrder() {
1348     // TODO implement getPublicationSortOrder
1349     return getSortOrder(EventIndexSchema.PUBLICATION);
1350   }
1351 }