diff --git a/backend/core/src/main/java/org/opencdmp/query/DescriptionQuery.java b/backend/core/src/main/java/org/opencdmp/query/DescriptionQuery.java index 731fb3018..f1751f2e9 100644 --- a/backend/core/src/main/java/org/opencdmp/query/DescriptionQuery.java +++ b/backend/core/src/main/java/org/opencdmp/query/DescriptionQuery.java @@ -13,10 +13,9 @@ import org.opencdmp.authorization.Permission; import org.opencdmp.commons.enums.DescriptionStatus; import org.opencdmp.commons.enums.IsActive; import org.opencdmp.commons.scope.user.UserScope; -import org.opencdmp.data.DescriptionEntity; -import org.opencdmp.data.DmpDescriptionTemplateEntity; -import org.opencdmp.data.DmpEntity; -import org.opencdmp.data.TenantEntityManager; +import org.opencdmp.data.*; +import org.opencdmp.elastic.data.nested.NestedTagElasticEntity; +import org.opencdmp.elastic.elasticbuilder.nested.NestedTagElasticBuilder; import org.opencdmp.model.PublicDescription; import org.opencdmp.model.description.Description; import org.opencdmp.query.utils.QueryUtilsService; @@ -26,6 +25,7 @@ import org.springframework.stereotype.Component; import java.time.Instant; import java.util.*; +import java.util.stream.Collectors; @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @@ -41,6 +41,8 @@ public class DescriptionQuery extends QueryBase { private DmpQuery dmpQuery; + private DescriptionReferenceQuery descriptionReferenceQuery; + private Instant createdAfter; private Instant createdBefore; @@ -155,6 +157,11 @@ public class DescriptionQuery extends QueryBase { return this; } + public DescriptionQuery descriptionReferenceSubQuery(DescriptionReferenceQuery subQuery) { + this.descriptionReferenceQuery = subQuery; + return this; + } + public DescriptionQuery excludedIds(Collection values) { this.excludedIds = values; return this; @@ -334,6 +341,10 @@ public class DescriptionQuery extends QueryBase { QueryContext subQuery = this.applySubQuery(this.dmpQuery, queryContext, UUID.class, dmpEntityRoot -> dmpEntityRoot.get(DmpEntity._id)); predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpId)).value(subQuery.Query)); } + if (this.descriptionReferenceQuery != null) { + QueryContext subQuery = this.applySubQuery(this.descriptionReferenceQuery, queryContext, UUID.class, descriptionReferenceEntityRoot -> descriptionReferenceEntityRoot.get(DescriptionReferenceEntity._descriptionId)); + predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._id)).value(subQuery.Query)); + } if (this.dmpDescriptionTemplateIds != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpDescriptionTemplateId)); for (UUID item : this.dmpDescriptionTemplateIds) diff --git a/backend/core/src/main/java/org/opencdmp/query/lookup/DescriptionLookup.java b/backend/core/src/main/java/org/opencdmp/query/lookup/DescriptionLookup.java index 626db67ba..de6efa589 100644 --- a/backend/core/src/main/java/org/opencdmp/query/lookup/DescriptionLookup.java +++ b/backend/core/src/main/java/org/opencdmp/query/lookup/DescriptionLookup.java @@ -6,6 +6,7 @@ import org.apache.commons.lang3.StringUtils; import org.opencdmp.commons.enums.DescriptionStatus; import org.opencdmp.commons.enums.IsActive; import org.opencdmp.elastic.query.DescriptionElasticQuery; +import org.opencdmp.model.reference.Reference; import org.opencdmp.query.DescriptionQuery; import java.time.Instant; @@ -25,7 +26,8 @@ public class DescriptionLookup extends Lookup { private Instant finalizedBefore; private DmpLookup dmpSubQuery; - + private DescriptionReferenceLookup descriptionReferenceSubQuery; + private List isActive; private List statuses; @@ -78,6 +80,10 @@ public class DescriptionLookup extends Lookup { this.dmpSubQuery = dmpSubQuery; } + public DescriptionReferenceLookup getDescriptionReferenceSubQuery() { return this.descriptionReferenceSubQuery; } + + public void setDescriptionReferenceSubQuery(DescriptionReferenceLookup descriptionReferenceSubQuery) { this.descriptionReferenceSubQuery = descriptionReferenceSubQuery; } + public Instant getCreatedAfter() { return this.createdAfter; } @@ -116,6 +122,7 @@ public class DescriptionLookup extends Lookup { if (this.ids != null) query.ids(this.ids); if (this.excludedIds != null) query.excludedIds(this.excludedIds); if (this.dmpSubQuery != null) query.dmpSubQuery(this.dmpSubQuery.enrich(queryFactory)); + if (this.descriptionReferenceSubQuery != null) query.descriptionReferenceSubQuery(this.descriptionReferenceSubQuery.enrich(queryFactory)); if (this.isActive != null) query.isActive(this.isActive); if (this.statuses != null) query.statuses(this.statuses); if (this.createdAfter != null) query.createdAfter(this.createdAfter); @@ -139,7 +146,7 @@ public class DescriptionLookup extends Lookup { if (this.finalizedAfter != null) query.finalizedAfter(this.finalizedAfter); if (this.finalizedBefore != null) query.finalizedBefore(this.finalizedBefore); if (this.dmpSubQuery != null) query.dmpSubQuery(this.dmpSubQuery.enrichElasticInner(queryFactory)); - + // TODO: add reference ? this.enrichCommon(query); return query; diff --git a/dmp-frontend/src/app/core/query/description.lookup.ts b/dmp-frontend/src/app/core/query/description.lookup.ts index bb5889232..305c5fc94 100644 --- a/dmp-frontend/src/app/core/query/description.lookup.ts +++ b/dmp-frontend/src/app/core/query/description.lookup.ts @@ -8,6 +8,8 @@ import { DescriptionTag } from '../model/description/description'; import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { ReferenceType } from '../model/reference-type/reference-type'; import { Reference } from '../model/reference/reference'; +import { ReferenceTypeLookup } from './reference-type.lookup'; +import { DescriptionReferenceLookup, ReferenceLookup } from './reference.lookup'; export class DescriptionLookup extends Lookup implements DescriptionFilter { ids: Guid[]; @@ -26,7 +28,7 @@ export class DescriptionLookup extends Lookup implements DescriptionFilter { associatedDmpIds: Guid[]; //TODO roleInDmp: DmpUserRole[]; //TODO tags: DescriptionTag[]; //TODO - references: ReferenceAndTypeLookup[]; //TODO + descriptionReferenceSubQuery: DescriptionReferenceLookup; constructor() { super(); @@ -49,10 +51,13 @@ export interface DescriptionFilter { associatedDmpIds: Guid[]; roleInDmp: DmpUserRole[]; tags: DescriptionTag[]; - references: ReferenceAndTypeLookup[]; + descriptionReferenceSubQuery: DescriptionReferenceLookup; } -export interface ReferenceAndTypeLookup { +export class ReferencesWithType { referenceType: ReferenceType; references: Reference[]; + + constructor() { + } } diff --git a/dmp-frontend/src/app/core/query/reference.lookup.ts b/dmp-frontend/src/app/core/query/reference.lookup.ts index f78da6047..75701dc3d 100644 --- a/dmp-frontend/src/app/core/query/reference.lookup.ts +++ b/dmp-frontend/src/app/core/query/reference.lookup.ts @@ -15,6 +15,16 @@ export class ReferenceLookup extends Lookup implements ReferenceFilter { super(); } } +export class DescriptionReferenceLookup extends Lookup { + referenceIds: Guid[]; + excludedIds: Guid[]; + like: string; + isActive: IsActive[]; + + constructor() { + super(); + } +} export interface ReferenceFilter { ids: Guid[]; diff --git a/dmp-frontend/src/app/core/services/reference/reference.service.ts b/dmp-frontend/src/app/core/services/reference/reference.service.ts index 9468fc5be..6b5fb3c91 100644 --- a/dmp-frontend/src/app/core/services/reference/reference.service.ts +++ b/dmp-frontend/src/app/core/services/reference/reference.service.ts @@ -11,7 +11,7 @@ import { QueryResult } from '@common/model/query-result'; import { FilterService } from '@common/modules/text-filter/filter-service'; import { Guid } from '@common/types/guid'; import { Observable, throwError } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; +import { catchError, filter, map } from 'rxjs/operators'; import { nameof } from 'ts-simple-nameof'; import { ConfigurationService } from '../configuration/configuration.service'; import { BaseHttpV2Service } from '../http/base-http-v2.service'; @@ -140,8 +140,20 @@ export class ReferenceService { public getMultipleAutoCompleteSearchConfiguration(typeId: Guid, dependencyReferences: Reference[]): MultipleAutoCompleteConfiguration { return { - initialItems: (excludedItems: any[], data?: any) => this.searchWithDefinition(this.buildAutocompleteSearchLookup(typeId, dependencyReferences, null)).pipe(map(x => x)), - filterFn: (searchQuery: string, excludedItems: any[]) => this.searchWithDefinition(this.buildAutocompleteSearchLookup(typeId, dependencyReferences, searchQuery)).pipe(map(x => x)), + initialItems: (excludedItems: any[], data?: any) => this.searchWithDefinition(this.buildAutocompleteSearchLookup(typeId, dependencyReferences, null)).pipe(map(x => x.filter(r => r.id))).pipe(map(x => x)), + filterFn: (searchQuery: string, excludedItems: any[]) => this.searchWithDefinition(this.buildAutocompleteSearchLookup(typeId, dependencyReferences, searchQuery)).pipe(map(x => x.filter(r => r.id))).pipe(map(x => x)), + displayFn: (item: Reference) => item.label, + titleFn: (item: Reference) => item.label, + subtitleFn: (item: Reference) => item?.sourceType === ReferenceSourceType.External ? this.language.instant('REFERENCE-FIELD-COMPONENT.EXTERNAL-SOURCE') + ': ' + item.source : this.language.instant('REFERENCE-FIELD-COMPONENT.INTERNAL-SOURCE'), + valueAssign: (item: Reference) => item, + uniqueAssign: (item: Reference) => item.source + '_' + item.reference, + }; + } + + public getInternalMultipleAutoCompleteConfiguration(typeId: Guid, dependencyReferences: Reference[]): MultipleAutoCompleteConfiguration { + return { + initialItems: (excludedItems: any[], data?: any) => this.searchWithDefinition(this.buildAutocompleteSearchLookup(typeId, dependencyReferences, null)).pipe(map(x => x.filter(r => r.sourceType == ReferenceSourceType.Internal))).pipe(map(x => x)), + filterFn: (searchQuery: string, excludedItems: any[]) => this.searchWithDefinition(this.buildAutocompleteSearchLookup(typeId, dependencyReferences, searchQuery)).pipe(map(x => x.filter(r => r.sourceType == ReferenceSourceType.Internal))).pipe(map(x => x)), displayFn: (item: Reference) => item.label, titleFn: (item: Reference) => item.label, subtitleFn: (item: Reference) => item?.sourceType === ReferenceSourceType.External ? this.language.instant('REFERENCE-FIELD-COMPONENT.EXTERNAL-SOURCE') + ': ' + item.source : this.language.instant('REFERENCE-FIELD-COMPONENT.INTERNAL-SOURCE'), diff --git a/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts b/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts index da939c27f..2669e543c 100644 --- a/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts +++ b/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts @@ -8,13 +8,13 @@ import { IsActive } from '@app/core/common/enum/is-active.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum'; import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { DescriptionTemplate } from '@app/core/model/description-template/description-template'; -import { BaseDescription, Description } from '@app/core/model/description/description'; +import { BaseDescription, Description, DescriptionReference } from '@app/core/model/description/description'; import { DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; import { Dmp, DmpDescriptionTemplate, DmpUser } from '@app/core/model/dmp/dmp'; import { DmpReference } from '@app/core/model/dmp/dmp-reference'; import { ReferenceType } from '@app/core/model/reference-type/reference-type'; import { Reference } from '@app/core/model/reference/reference'; -import { DescriptionLookup } from '@app/core/query/description.lookup'; +import { DescriptionLookup, ReferencesWithType } from '@app/core/query/description.lookup'; import { AuthService } from '@app/core/services/auth/auth.service'; import { DescriptionService } from '@app/core/services/description/description.service'; import { AnalyticsService } from '@app/core/services/matomo/analytics-service'; @@ -35,6 +35,8 @@ import { QueryResult } from '@common/model/query-result'; import { Observable } from 'rxjs'; import { QueryParamsService } from '@app/core/services/utilities/query-params.service'; import { UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { Guid } from '@common/types/guid'; +import { DescriptionReferenceLookup } from '@app/core/query/reference.lookup'; @Component({ selector: 'app-description-listing-component', @@ -62,6 +64,7 @@ export class DescriptionListingComponent extends BaseListingComponent(x => x.reference), nameof(x => x.id)].join('.'), + ] + }; + + return lookup; + } protected setupColumns() { } @@ -236,7 +253,6 @@ export class DescriptionListingComponent extends BaseListingComponent reference.referenceType != null && reference.references?.length > 0) ?? null; + lookup.descriptionReferenceSubQuery = this.initializeReferenceLookup(); + lookup.descriptionReferenceSubQuery.referenceIds = formGroup.get("references")?.value + ?.filter((reference: ReferencesWithType) => reference.referenceType != null && reference.references?.length > 0) + ?.flatMap((referencesWithType: ReferencesWithType) => referencesWithType.references?.map(reference => reference.id)) as Guid[]; return lookup; } + + _patchReferenceFiltersFromForm(formGroup: UntypedFormGroup): ReferencesWithType[] { + return formGroup?.get("references")?.value?.filter(reference => reference.referenceType != null && reference.references?.length > 0) ?? null; + } - _buildFormFromLookup(lookup: DescriptionLookup): UntypedFormGroup { + _buildFormFromLookup(lookup: DescriptionLookup, referenceFilters: ReferencesWithType[]): UntypedFormGroup { const formArray = this.formBuilder.array([]) as UntypedFormArray; - lookup.references?.forEach(reference => { + referenceFilters?.forEach(reference => { let referenceForm = this.formBuilder.group({ referenceType: reference.referenceType, diff --git a/dmp-frontend/src/app/ui/description/listing/filtering/description-filter.component.ts b/dmp-frontend/src/app/ui/description/listing/filtering/description-filter.component.ts index 553eca352..10d688811 100644 --- a/dmp-frontend/src/app/ui/description/listing/filtering/description-filter.component.ts +++ b/dmp-frontend/src/app/ui/description/listing/filtering/description-filter.component.ts @@ -167,7 +167,7 @@ export class DescriptionFilterComponent extends BaseCriteriaComponent implements }; getReferenceAutocompleteConfiguration(referenceTypeId: Guid, excludedIds: Reference[]): MultipleAutoCompleteConfiguration { - let autocomplete = this.referenceService.getMultipleAutoCompleteSearchConfiguration(referenceTypeId, excludedIds); + let autocomplete = this.referenceService.getInternalMultipleAutoCompleteConfiguration(referenceTypeId, excludedIds); return autocomplete; };