From 82cf7bda4d8112388779a359db5538961b9c4432 Mon Sep 17 00:00:00 2001 From: smetanka Date: Thu, 4 Sep 2025 16:16:47 +0200 Subject: [PATCH 1/6] [NAE-2197] Elastic Mapping Fixes - Add `StringCollectionField` and integrate it into ElasticCase mapping logic - Annotate additional fields in `ElasticCase` with proper Elasticsearch mappings - Fix initialization of `booleanValue` in `BooleanField` constructor --- .../service/ElasticCaseMappingService.java | 10 +++++++ .../elastic/domain/StringCollectionField.java | 19 ++++++++++++ .../spring/elastic/domain/BooleanField.java | 1 + .../spring/elastic/domain/ElasticCase.java | 14 +++++++++ .../elastic/domain/StringCollectionField.java | 30 +++++++++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java index 51ace92131b..6e836b94ced 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java @@ -83,6 +83,8 @@ protected Optional transformDataField(String fieldId, Case useCase) { return this.transformCaseFieldField(caseField, (com.netgrif.application.engine.objects.petrinet.domain.dataset.CaseField) netField); } else if (netField instanceof com.netgrif.application.engine.objects.petrinet.domain.dataset.FilterField) { return this.transformFilterFieldField(caseField, (com.netgrif.application.engine.objects.petrinet.domain.dataset.FilterField) netField); + } else if (netField instanceof com.netgrif.application.engine.objects.petrinet.domain.dataset.StringCollectionField) { + return this.transformStringCollectionField(caseField, (com.netgrif.application.engine.objects.petrinet.domain.dataset.StringCollectionField) netField); } else { String string = caseField.getValue().toString(); if (string == null) @@ -127,6 +129,14 @@ protected Optional transformFilterFieldField(com.netgrif.application. return Optional.of(new com.netgrif.application.engine.adapter.spring.elastic.domain.FilterField(dataField.getValue().toString(),allowedNets, filterMetadata)); } + protected Optional transformStringCollectionField(com.netgrif.application.engine.objects.workflow.domain.DataField dataField, com.netgrif.application.engine.objects.petrinet.domain.dataset.StringCollectionField netField) { + if (dataField.getValue() != null && dataField.getValue() instanceof Collection && !((Collection) dataField.getValue()).isEmpty()) { + String[] values = ((Collection) dataField.getValue()).toArray(new String[0]); + return Optional.of(new com.netgrif.application.engine.adapter.spring.elastic.domain.StringCollectionField(values)); + } + return Optional.empty(); + } + protected Optional transformEnumerationMapField (com.netgrif.application.engine.objects.workflow.domain.DataField enumMap, EnumerationMapField netField) { Map options = this.getFieldOptions(enumMap, netField); diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java new file mode 100644 index 00000000000..c4b4cb972c2 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java @@ -0,0 +1,19 @@ +package com.netgrif.application.engine.objects.elastic.domain; + + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public abstract class StringCollectionField extends TextField { + + public String[] collectionValue; + + public StringCollectionField(String[] values) { + super(values); + this.collectionValue = values; + } +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java index a1bf70e00a3..32392622807 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java @@ -14,6 +14,7 @@ public class BooleanField extends com.netgrif.application.engine.objects.elastic public BooleanField(Boolean value) { super(value); + this.booleanValue = value; } @Override diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java index 7ee9749d94d..1ff719c52ef 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java @@ -29,10 +29,24 @@ public void update(ElasticCase useCase) { } @Id + @Field(type = Keyword) public String getId() { return super.getId(); } + @Field(type = Keyword) + public String getTitle() { return super.getTitle(); } + + @Field(type = Keyword) + public String getVisualId() { + return super.getVisualId(); + } + + @Field(type = FieldType.Keyword) + public String getCaseId() { + return super.getId(); + } + @Version public Long getVersion() { return super.getVersion(); diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java new file mode 100644 index 00000000000..c329f9524cb --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java @@ -0,0 +1,30 @@ +package com.netgrif.application.engine.adapter.spring.elastic.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.springframework.data.elasticsearch.annotations.Field; + + +import static org.springframework.data.elasticsearch.annotations.FieldType.*; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class StringCollectionField extends com.netgrif.application.engine.objects.elastic.domain.StringCollectionField { + + @Field(type = Nested) + public String[] collectionValue; + + public StringCollectionField(String[] values) { + super(values); + this.collectionValue = values; + } + + @Override + @Field(type = Nested) + public String[] getFulltextValue() { + return super.getFulltextValue(); + } + +} From ff3a99599661a591756b807b238b27027a25b4f3 Mon Sep 17 00:00:00 2001 From: Milan Mladoniczky <6153201+tuplle@users.noreply.github.com> Date: Fri, 5 Sep 2025 18:09:54 +0200 Subject: [PATCH 2/6] [NAE-2197] Elastic Mapping Fixes - Updated `@Field` annotations in `StringCollectionField` and `ElasticCase` to improve clarity and consistency. - Removed redundant imports and fixed indentation for better code readability. - Ensured alignment with updated Elastic `FieldType` usage. --- .../spring/elastic/domain/ElasticCase.java | 12 ++++++------ .../elastic/domain/StringCollectionField.java | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java index 1ff719c52ef..aa011a825f2 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java @@ -7,14 +7,12 @@ import org.springframework.data.elasticsearch.annotations.DateFormat; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; -import org.springframework.data.elasticsearch.annotations.FieldType; import java.time.LocalDateTime; import java.util.Map; import java.util.Set; -import static org.springframework.data.elasticsearch.annotations.FieldType.Flattened; -import static org.springframework.data.elasticsearch.annotations.FieldType.Keyword; +import static org.springframework.data.elasticsearch.annotations.FieldType.*; @NoArgsConstructor @Document(indexName = "#{@elasticCaseIndex}") @@ -35,14 +33,16 @@ public String getId() { } @Field(type = Keyword) - public String getTitle() { return super.getTitle(); } + public String getTitle() { + return super.getTitle(); + } @Field(type = Keyword) public String getVisualId() { return super.getVisualId(); } - @Field(type = FieldType.Keyword) + @Field(type = Keyword) public String getCaseId() { return super.getId(); } @@ -62,7 +62,7 @@ public String getProcessId() { return super.getProcessId(); } - @Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second_millis) + @Field(type = Date, format = DateFormat.date_hour_minute_second_millis) public LocalDateTime getCreationDate() { return super.getCreationDate(); } diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java index c329f9524cb..4786a4d2d4a 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java @@ -5,26 +5,28 @@ import lombok.NoArgsConstructor; import org.springframework.data.elasticsearch.annotations.Field; - -import static org.springframework.data.elasticsearch.annotations.FieldType.*; +import static org.springframework.data.elasticsearch.annotations.FieldType.Keyword; +import static org.springframework.data.elasticsearch.annotations.FieldType.Text; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) -public class StringCollectionField extends com.netgrif.application.engine.objects.elastic.domain.StringCollectionField { - - @Field(type = Nested) - public String[] collectionValue; +public class StringCollectionField extends com.netgrif.application.engine.objects.elastic.domain.StringCollectionField { public StringCollectionField(String[] values) { super(values); - this.collectionValue = values; } @Override - @Field(type = Nested) + @Field(type = Text) public String[] getFulltextValue() { return super.getFulltextValue(); } + @Override + @Field(type = Keyword) + public String[] getCollectionValue() { + return super.getCollectionValue(); + } + } From 4a3527e9d433d24ed9f56a731ee4abf2ccc7112f Mon Sep 17 00:00:00 2001 From: Machac Date: Sat, 6 Sep 2025 03:04:31 +0200 Subject: [PATCH 3/6] [NAE-2197] Elastic Mapping Fixes - Modified the `findFilter` method to use `title` instead of `title.keyword` in the filter query string. --- .../petrinet/domain/dataset/logic/action/ActionDelegate.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index f0275b57fbf..da4e1b47572 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2271,7 +2271,7 @@ class ActionDelegate { * @return found filter instance. Can be null */ Case findFilter(String name) { - return findCaseElastic("processIdentifier:$FilterRunner.FILTER_PETRI_NET_IDENTIFIER AND title.keyword:\"$name\"" as String) + return findCaseElastic("processIdentifier:$FilterRunner.FILTER_PETRI_NET_IDENTIFIER AND title:\"$name\"" as String) } /** From ebd9c5cfb8da40e7c4e492a5a33d993d52a048d1 Mon Sep 17 00:00:00 2001 From: Machac Date: Sat, 6 Sep 2025 03:29:25 +0200 Subject: [PATCH 4/6] [NAE-2197] Elastic Mapping Fixes - Updated `ElasticCase` class to enhance field mapping using `@MultiField` for `title` and `authorName`. - Enabled support for both `Text` and `Keyword` types by defining main and inner fields. --- .../spring/elastic/domain/ElasticCase.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java index aa011a825f2..661cd3868ca 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java @@ -4,9 +4,7 @@ import lombok.NoArgsConstructor; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Version; -import org.springframework.data.elasticsearch.annotations.DateFormat; -import org.springframework.data.elasticsearch.annotations.Document; -import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.*; import java.time.LocalDateTime; import java.util.Map; @@ -32,7 +30,11 @@ public String getId() { return super.getId(); } - @Field(type = Keyword) + @MultiField( + mainField = @Field(type = Text), + otherFields = { + @InnerField(suffix = "keyword", type = Keyword) + }) public String getTitle() { return super.getTitle(); } @@ -77,7 +79,11 @@ public String getAuthorRealm() { return super.getAuthorRealm(); } - @Field(type = Keyword) + @MultiField( + mainField = @Field(type = Text), + otherFields = { + @InnerField(suffix = "keyword", type = Keyword) + }) public String getAuthorName() { return super.getAuthorName(); } From c44dde6bcbce35e9bb651ef6f0bce27e3d9c84e4 Mon Sep 17 00:00:00 2001 From: Machac Date: Sat, 6 Sep 2025 03:37:54 +0200 Subject: [PATCH 5/6] [NAE-2197] Elastic Mapping Fixes - Updated the `findFilter` method to use `title.keyword` instead of `title` in the filter query string to align with changes in ElasticSearch mapping for enhanced precision. --- .../petrinet/domain/dataset/logic/action/ActionDelegate.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index da4e1b47572..f0275b57fbf 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2271,7 +2271,7 @@ class ActionDelegate { * @return found filter instance. Can be null */ Case findFilter(String name) { - return findCaseElastic("processIdentifier:$FilterRunner.FILTER_PETRI_NET_IDENTIFIER AND title:\"$name\"" as String) + return findCaseElastic("processIdentifier:$FilterRunner.FILTER_PETRI_NET_IDENTIFIER AND title.keyword:\"$name\"" as String) } /** From 319528797c656336350c5a4bb45fa7f9728572e6 Mon Sep 17 00:00:00 2001 From: Machac Date: Sat, 6 Sep 2025 05:17:42 +0200 Subject: [PATCH 6/6] [NAE-2197] Elastic Mapping Fixes - Updated the `buildQuery` method to enhance query creation: - Added explicit handling for unmapped fields by setting `FieldType.Keyword`. - Included fallback handling for missing values using `_last`. --- .../elastic/service/ElasticCaseService.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java index d4ae214c268..07d69011897 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.elastic.service; import co.elastic.clients.elasticsearch._types.FieldValue; +import co.elastic.clients.elasticsearch._types.mapping.FieldType; import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery; import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField; @@ -138,7 +139,7 @@ public Page search(List requests, LoggedUser user, Page // TODO: impersonation // LoggedUser loggedOrImpersonated = user.getSelfOrImpersonated(); LoggedUser loggedOrImpersonated = user; - pageable = resolveUnmappedSortAttributes(pageable); +// pageable = resolveUnmappedSortAttributes(pageable); NativeQuery query = buildQuery(requests, loggedOrImpersonated, pageable, locale, isIntersection); List casePage; long total; @@ -190,11 +191,22 @@ protected NativeQuery buildQuery(List requests, LoggedUser us BinaryOperator reductionOperation = isIntersection ? (a, b) -> a.must(b.build()._toQuery()) : (a, b) -> a.should(b.build()._toQuery()); BoolQuery.Builder query = singleQueries.stream().reduce(new BoolQuery.Builder(), reductionOperation); - NativeQueryBuilder builder = new NativeQueryBuilder(); - return builder + NativeQueryBuilder builder = new NativeQueryBuilder() .withQuery(query.build()._toQuery()) - .withPageable(pageable) - .build(); + .withPageable(PageRequest.of(pageable.getPageNumber(), pageable.getPageSize())); + + for (org.springframework.data.domain.Sort.Order o : pageable.getSort()) { + builder.withSort(s -> s.field(f -> f + .field(o.getProperty()) + .order(o.isAscending() + ? co.elastic.clients.elasticsearch._types.SortOrder.Asc + : co.elastic.clients.elasticsearch._types.SortOrder.Desc) + .unmappedType(FieldType.Keyword) + .missing("_last") + )); + } + + return builder.build(); } protected BoolQuery.Builder buildSingleQuery(CaseSearchRequest request, LoggedUser user, Locale locale) {