AccessionFilter.java
/*
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.server.service.filter;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.validation.constraints.Pattern;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.genesys.blocks.model.filters.NumberFilter;
import org.genesys.blocks.model.filters.StringFilter;
import org.genesys.blocks.model.filters.UuidModelFilter;
import org.genesys.blocks.util.CurrentApplicationContext;
import org.genesys.blocks.util.FilterUtils;
import org.genesys.server.model.dataset.QDataset;
import org.genesys.server.model.genesys.Accession;
import org.genesys.server.model.genesys.AccessionData;
import org.genesys.server.model.genesys.QAccession;
import org.genesys.server.model.genesys.QAccessionId;
import org.genesys.server.model.genesys.QTaxonomy2;
import org.genesys.server.model.impl.DiversityTreeAccessionRef;
import org.genesys.server.model.impl.FaoInstitute;
import org.genesys.server.model.impl.QDiversityTree;
import org.genesys.server.model.impl.QSubset;
import org.genesys.server.service.DiversityTreeService;
import org.genesys.server.service.PGRFANetworkService;
import org.genesys.server.service.PartnerService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.UUIDDeserializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.JPQLQuery;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
/**
* Filters for {@link Accession}.
*/
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@Accessors(fluent = true)
public class AccessionFilter extends UuidModelFilter<AccessionFilter, Accession> implements IFullTextFilter {
private static final long serialVersionUID = -1441103961567816877L;
public static final String[] ES_BOOSTED_FIELDS = { "accessionNumber", "accessionName", "aliases.name", "coll.collNumb" };
private static final Map<String, String> REMAPPED_PROPERTIES = Map.of("taxonomy.grinTaxonName", "taxonomy.grinTaxonomySpecies", "taxonomy.grinCurrentName", "taxonomy.currentTaxonomySpecies");
/** Any text. */
public String _text;
/** The historic. */
public Boolean historic = false;
/** The aegis. */
public Boolean aegis;
/** The mls status. */
public Boolean mlsStatus;
/** The available. */
public Boolean available;
/** The crop. */
public Set<String> crop;
/** The crop name. */
public StringFilter cropName;
/** The institute. */
public InstituteFilter institute;
/** The acce numb. */
public StringFilter accessionNumber;
/** The accession numbers. */
public Set<String> accessionNumbers;
/** The doi. */
public Set<String> doi;
/** The seq no. */
public NumberFilter<Double> seqNo;
/** The taxonomy. */
public TaxonomyFilter taxonomy;
/** The samp stat. */
public Set<Integer> sampStat;
/** The origin. */
public CountryFilter countryOfOrigin;
/** The geo. */
public AccessionGeoFilter geo;
/** The coll. */
public AccessionCollectFilter coll;
/** The storage. */
public Set<Integer> storage;
/** The sgsv. */
public Boolean sgsv;
/** The art 15. */
public Boolean inTrust;
/** The pdci. */
public NumberFilter<Double> pdci;
/** acce.lists uuid */
public Set<UUID> lists;
/** accession aliases */
public StringFilter aliases;
/** accessions with images */
public Boolean images;
/** has DOI assigned */
public Boolean hasDoi;
/** is in a Dataset */
public Boolean hasDataset;
/** is in a Subset */
public Boolean hasSubset;
/** The duplSite. */
public Set<@Pattern(regexp = "[A-Z]{3}\\d{3,4}") String> duplSite;
/** The donorCode. */
public Set<@Pattern(regexp = "[A-Z]{3}\\d{3,4}") String> donorCode;
/** The breederCode. */
public Set<@Pattern(regexp = "[A-Z]{3}\\d{3,4}") String> breederCode;
/** Subset. */
@JsonDeserialize(contentUsing = UUIDDeserializer.class)
public Set<UUID> subsets;
/** Dataset. */
@JsonDeserialize(contentUsing = UUIDDeserializer.class)
public Set<UUID> datasets;
/** DiversityTree. */
@JsonDeserialize(contentUsing = UUIDDeserializer.class)
public Set<UUID> diversityTrees;
/** The nodeKey. See {@link DiversityTreeAccessionRef#nodeKey} */
public String nodeKey;
/** The networks. */
public Set<String> networks;
/** The partner. */
public Set<UUID> partner;
/** The curation type. */
public Set<AccessionData.CurationType> curationType;
public StringFilter acquisitionDate;
public AccessionFilter() {
// Use defaults
}
public AccessionFilter(Boolean historical) {
this.historic = historical;
}
/*
* (non-Javadoc)
* @see org.genesys.blocks.model.filters.BasicModelFilter#buildQuery()
*/
public List<Predicate> collectPredicates() {
return collectPredicates(QAccession.accession);
}
public <T> JPQLQuery<T> buildJpaQuery(JPQLQuery<T> query, QAccession accession) {
QAccessionId qAccessionId = accession.accessionId();
query.innerJoin(qAccessionId);
final List<Predicate> predicates = super.collectPredicates(accession, accession._super._super);
if (!FilterUtils.isEmpty(taxonomy)) {
QTaxonomy2 taxonomyJoin = new QTaxonomy2("T");
query.innerJoin(accession.taxonomy(), taxonomyJoin);
// Prevent cross-joins
query.leftJoin(taxonomyJoin.grinTaxonomySpecies());
query.leftJoin(taxonomyJoin.currentTaxonomySpecies());
predicates.addAll(taxonomy.collectPredicates(taxonomyJoin));
}
if (!FilterUtils.isEmpty(crop)) {
query.leftJoin(accession.crop());
predicates.add(accession.crop().shortName.in(crop));
}
if (!FilterUtils.isEmpty(institute)) {
query.innerJoin(accession.institute());
predicates.addAll(institute.collectPredicates(accession.institute()));
// predicates.add(accession.institute().id.in(fetchInstituteIds(institute)));
}
if (!FilterUtils.isEmpty(countryOfOrigin)) {
query.leftJoin(accession.countryOfOrigin());
predicates.addAll(countryOfOrigin.collectPredicates(accession.countryOfOrigin()));
}
if (!FilterUtils.isEmpty(geo)) {
// query.leftJoin(qAccessionId.geo);
if (!FilterUtils.isEmpty(geo.climate())) {
query.leftJoin(qAccessionId.climate());
}
predicates.addAll(geo.collectPredicates(qAccessionId));
}
if (!FilterUtils.isEmpty(coll)) {
query.leftJoin(qAccessionId.coll());
predicates.addAll(coll.collectPredicates(qAccessionId.coll()));
}
if (!FilterUtils.isEmpty(pdci)) {
query.leftJoin(qAccessionId.pdci());
predicates.add(pdci.buildQuery(qAccessionId.pdci().score));
}
if (CollectionUtils.isNotEmpty(subsets)) {
QSubset qSubset = new QSubset("subsets");
query.leftJoin(accession.subsets, qSubset);
predicates.add(qSubset.uuid.in(subsets));
}
if (CollectionUtils.isNotEmpty(datasets)) {
QDataset qDataset = new QDataset("datasets");
query.leftJoin(accession.datasets, qDataset);
predicates.add(qDataset.uuid.in(datasets));
}
if (CollectionUtils.isNotEmpty(diversityTrees)) {
QDiversityTree qDiversityTree = new QDiversityTree("diversityTrees");
query.innerJoin(accession.diversityTrees, qDiversityTree);
BooleanExpression predicate = qDiversityTree.uuid.in(diversityTrees);
if (StringUtils.isNotBlank(nodeKey)) {
Set<Long> treeAccessions = getDivTreeAccessionIds(diversityTrees, nodeKey);
predicate = qAccessionId.id.in(treeAccessions);
}
predicates.add(predicate);
}
// Fields
if (historic != null) {
predicates.add(accession.historic.eq(historic));
}
if (aegis != null) {
predicates.add(accession.aegis.eq(aegis));
}
if (cropName != null) {
predicates.add(cropName.buildQuery(accession.cropName));
}
if (CollectionUtils.isNotEmpty(doi)) {
predicates.add(accession.doi.in(doi));
}
if (CollectionUtils.isNotEmpty(accessionNumbers)) {
predicates.add(accession.accessionNumber.in(accessionNumbers));
}
if (accessionNumber != null) {
predicates.add(accessionNumber.buildQuery(accession.accessionNumber));
}
if (seqNo != null) {
predicates.add(seqNo.buildQuery(accession.seqNo));
}
if (CollectionUtils.isNotEmpty(sampStat)) {
predicates.add(accession.sampStat.in(sampStat));
}
if (CollectionUtils.isNotEmpty(storage)) {
predicates.add(qAccessionId.storage.any().in(storage));
}
if (mlsStatus != null) {
predicates.add(accession.mlsStatus.eq(mlsStatus));
}
if (available != null) {
predicates.add(accession.available.eq(available));
}
if (inTrust != null) {
predicates.add(accession.inTrust.eq(inTrust));
}
if (CollectionUtils.isNotEmpty(lists)) {
predicates.add(qAccessionId.lists.any().uuid.in(lists));
}
if (CollectionUtils.isNotEmpty(uuid)) {
predicates.add(qAccessionId.uuid.in(uuid));
}
if (aliases != null) {
predicates.add(aliases.buildQuery(qAccessionId.aliases.any().name));
}
if (CollectionUtils.isNotEmpty(duplSite)) {
predicates.add(qAccessionId.duplSite.any().in(duplSite));
}
if (sgsv != null) {
predicates.add(accession.inSvalbard.eq(sgsv));
}
if (CollectionUtils.isNotEmpty(curationType)) {
predicates.add(accession.curationType.in(curationType));
}
if (CollectionUtils.isNotEmpty(donorCode)) {
predicates.add(accession.donorCode.in(donorCode));
}
if (CollectionUtils.isNotEmpty(breederCode)) {
predicates.add(qAccessionId.breederCode.any().in(breederCode));
}
if (acquisitionDate != null) {
predicates.add(acquisitionDate.buildQuery(accession.acquisitionDate));
}
if (images != null) {
if (images) {
predicates.add(qAccessionId.imageCount.gt(0));
} else {
predicates.add(qAccessionId.imageCount.eq(0));
}
}
if (CollectionUtils.isNotEmpty(networks)) {
Set<Long> networkMembers = getNetworkMemberIds(networks);
predicates.add(accession.institute().id.in(networkMembers));
}
if (CollectionUtils.isNotEmpty(partner)) {
Set<Long> partnerInstitutes = getPartnerInstituteIds(partner);
predicates.add(accession.institute().id.in(partnerInstitutes));
}
if (hasDoi != null) {
if (hasDoi) {
predicates.add(accession.doi.isNotNull());
} else {
predicates.add(accession.doi.isNull());
}
}
if (hasSubset != null) {
if (hasSubset) {
predicates.add(qAccessionId.subsetCount.gt(0));
} else {
predicates.add(qAccessionId.subsetCount.eq(0l));
}
}
if (hasDataset != null) {
if (hasDataset) {
predicates.add(qAccessionId.datasetCount.gt(0));
} else {
predicates.add(qAccessionId.datasetCount.eq(0l));
}
}
var builder = new BooleanBuilder(ExpressionUtils.allOf(predicates));
if (NOT != null) {
// This is not a regular NOT operation where not(A & B) = not(A) or not(B)
// This is not(A, B) = not(A) and not(B)
builder.and(ExpressionUtils.anyOf(NOT.collectPredicates()).not());
}
if (AND != null) {
builder.and(AND.buildPredicate());
}
if (OR != null) {
builder.or(OR.buildPredicate());
}
query.where(builder);
return query;
}
/**
* Builds the query.
*
* @param accession the accession
* @return the predicate
*/
public List<Predicate> collectPredicates(QAccession accession) {
final List<Predicate> predicates = super.collectPredicates(accession, accession._super._super);
QAccessionId qAccessionId = accession.accessionId();
if (taxonomy != null) {
predicates.addAll(taxonomy.collectPredicates(accession.taxonomy()));
}
if (crop != null && !crop.isEmpty()) {
predicates.add(accession.crop().isNotNull().and(accession.crop().shortName.in(crop)));
}
if (institute != null) {
predicates.addAll(institute.collectPredicates(accession.institute()));
// predicates.add(accession.institute().id.in(fetchInstituteIds(institute)));
}
if (countryOfOrigin != null) {
predicates.addAll(countryOfOrigin.collectPredicates(accession.countryOfOrigin()));
}
if (geo != null) {
predicates.addAll(geo.collectPredicates(qAccessionId));
}
if (coll != null) {
predicates.addAll(coll.collectPredicates(qAccessionId.coll()));
}
if (pdci != null) {
predicates.add(pdci.buildQuery(qAccessionId.pdci().score));
}
if (subsets != null) {
predicates.add(accession.subsets.isNotEmpty().and(accession.subsets.any().uuid.in(subsets)));
}
if (datasets != null) {
predicates.add(accession.datasets.isNotEmpty().and(accession.datasets.any().uuid.in(datasets)));
}
if (diversityTrees != null) {
BooleanExpression predicate = accession.diversityTrees.isNotEmpty().and(accession.diversityTrees.any().uuid.in(diversityTrees));
if (StringUtils.isNotBlank(nodeKey)) {
Set<Long> treeAccessions = getDivTreeAccessionIds(diversityTrees, nodeKey);
predicate = qAccessionId.id.in(treeAccessions);
}
predicates.add(predicate);
}
// Fields
if (historic != null) {
predicates.add(accession.historic.eq(historic));
}
if (aegis != null) {
predicates.add(accession.aegis.eq(aegis));
}
if (cropName != null) {
predicates.add(cropName.buildQuery(accession.cropName));
}
if (CollectionUtils.isNotEmpty(doi)) {
predicates.add(accession.doi.isNotNull().and(accession.doi.in(doi)));
}
if (CollectionUtils.isNotEmpty(accessionNumbers)) {
predicates.add(accession.accessionNumber.in(accessionNumbers));
}
if (accessionNumber != null) {
predicates.add(accessionNumber.buildQuery(accession.accessionNumber));
}
if (seqNo != null) {
predicates.add(seqNo.buildQuery(accession.seqNo));
}
if (CollectionUtils.isNotEmpty(sampStat)) {
predicates.add(accession.sampStat.isNotNull().and(accession.sampStat.in(sampStat)));
}
if (CollectionUtils.isNotEmpty(storage)) {
predicates.add(qAccessionId.storage.size().gt(0).and(qAccessionId.storage.any().in(storage)));
}
if (mlsStatus != null) {
predicates.add(accession.mlsStatus.eq(mlsStatus));
}
if (available != null) {
predicates.add(accession.available.eq(available));
}
if (inTrust != null) {
predicates.add(accession.inTrust.eq(inTrust));
}
if (CollectionUtils.isNotEmpty(lists)) {
predicates.add(qAccessionId.lists.isNotEmpty().and(qAccessionId.lists.any().uuid.in(lists)));
}
if (CollectionUtils.isNotEmpty(uuid)) {
// predicates.add(accession.accessionId.uuid.isNotNull()); // Not null
predicates.add(qAccessionId.uuid.in(uuid));
}
if (aliases != null) {
predicates.add(qAccessionId.aliases.isNotEmpty().and(aliases.buildQuery(qAccessionId.aliases.any().name)));
}
if (CollectionUtils.isNotEmpty(duplSite)) {
predicates.add(qAccessionId.duplSite.any().in(duplSite));
}
if (sgsv != null) {
predicates.add(accession.inSvalbard.eq(sgsv));
}
if (CollectionUtils.isNotEmpty(curationType)) {
predicates.add(accession.curationType.in(curationType));
}
if (CollectionUtils.isNotEmpty(donorCode)) {
predicates.add(accession.donorCode.in(donorCode));
}
if (CollectionUtils.isNotEmpty(breederCode)) {
predicates.add(qAccessionId.breederCode.any().in(breederCode));
}
if (acquisitionDate != null) {
predicates.add(acquisitionDate.buildQuery(accession.acquisitionDate));
}
if (images != null) {
if (images) {
predicates.add(qAccessionId.imageCount.gt(0));
} else {
predicates.add(qAccessionId.imageCount.eq(0));
}
}
if (CollectionUtils.isNotEmpty(networks)) {
Set<Long> networkMembers = getNetworkMemberIds(networks);
predicates.add(accession.institute().id.in(networkMembers));
}
if (CollectionUtils.isNotEmpty(partner)) {
Set<Long> partnerInstitutes = getPartnerInstituteIds(partner);
predicates.add(accession.institute().id.in(partnerInstitutes));
}
if (hasDoi != null) {
if (hasDoi) {
predicates.add(accession.doi.isNotNull());
} else {
predicates.add(accession.doi.isNull());
}
}
if (hasSubset != null) {
if (hasSubset) {
predicates.add(qAccessionId.subsetCount.gt(0));
} else {
predicates.add(qAccessionId.subsetCount.eq(0l));
}
}
if (hasDataset != null) {
if (hasDataset) {
predicates.add(qAccessionId.datasetCount.gt(0));
} else {
predicates.add(qAccessionId.datasetCount.eq(0l));
}
}
if (lastModifiedDate != null) {
predicates.add(lastModifiedDate.buildQuery(accession.lastModifiedDate));
}
return predicates;
}
// private Collection<Long> fetchInstituteIds(InstituteFilter filter) {
// try {
// InstituteService service = CurrentApplicationContext.getContext().getBean(InstituteService.class);
// return service.list(filter, Pageable.unpaged()).map(FaoInstitute::getId).getContent();
// } catch (Throwable e) {
// throw new RuntimeException("Could not fetch institute ids", e);
// }
// }
private Set<Long> getNetworkMemberIds(Set<String> slugs) {
PGRFANetworkService networkService = CurrentApplicationContext.getContext().getBean(PGRFANetworkService.class);
return networkService.getMembers(slugs).stream().map(FaoInstitute::getId).collect(Collectors.toSet());
}
private Set<Long> getPartnerInstituteIds(Set<UUID> uuids) {
PartnerService partnerService = CurrentApplicationContext.getContext().getBean(PartnerService.class);
return partnerService.loadInstitutes(uuids).stream().map(FaoInstitute::getId).collect(Collectors.toSet());
}
private Set<Long> getDivTreeAccessionIds(Set<UUID> treeUuids, String nodeKey) {
DiversityTreeService treeService = CurrentApplicationContext.getContext().getBean(DiversityTreeService.class);
return treeService.loadAccessionRefs(treeUuids, nodeKey).stream().filter(ref -> ref.getAccession() != null).map(ref -> ref.getAccession().getAccessionId().getId()).collect(Collectors.toSet());
}
/**
* Holder.
*
* @return the institute filter
*/
public synchronized InstituteFilter holder() {
if (this.institute == null) {
return this.institute = new InstituteFilter();
} else {
return this.institute;
}
}
/**
* Origin.
*
* @return the country filter
*/
public synchronized CountryFilter origin() {
if (this.countryOfOrigin == null) {
return this.countryOfOrigin = new CountryFilter();
}
return this.countryOfOrigin;
}
/**
* Geo.
*
* @return the accession geo filter
*/
public synchronized AccessionGeoFilter geo() {
return this.geo == null ? this.geo = new AccessionGeoFilter() : this.geo;
}
/**
* Coll.
*
* @return the accession collect filter
*/
public synchronized AccessionCollectFilter coll() {
return this.coll == null ? this.coll = new AccessionCollectFilter() : this.coll;
}
/**
* Taxa.
*
* @return the taxonomy filter
*/
public synchronized TaxonomyFilter taxa() {
return this.taxonomy == null ? this.taxonomy = new TaxonomyFilter() : this.taxonomy;
}
/**
* Accession Number filter.
*
* @return the string filter
*/
public synchronized StringFilter accessionNumber() {
return this.accessionNumber == null ? this.accessionNumber = new StringFilter() : this.accessionNumber;
}
/**
* Accession Number filter.
*
* @return the string filter
*/
public synchronized Set<String> accessionNumbers() {
return this.accessionNumbers == null ? this.accessionNumbers = new HashSet<>() : this.accessionNumbers;
}
public synchronized Set<String> doi() {
if (doi == null) {
doi = new HashSet<>();
}
return doi;
}
public String get_text() {
return _text;
}
/**
* Institute filter.
*
* @return the institute filter
*/
public synchronized InstituteFilter institute() {
return this.institute == null ? this.institute = new InstituteFilter() : this.institute;
}
@Override
public String[] boostedFields() {
return ES_BOOSTED_FIELDS;
}
@Override
public Map<String, String> remappedProperties() {
return REMAPPED_PROPERTIES;
}
private static final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
public static AccessionFilter fromJson(String json) throws JsonProcessingException {
if (StringUtils.isBlank(json)) {
return new AccessionFilter();
}
return mapper.readValue(json, AccessionFilter.class);
}
}