Skip to content

Conversation

@Smotanka
Copy link

@Smotanka Smotanka commented Sep 4, 2025

Description

  • 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

Fixes [NAE-2197]

Dependencies

None

Third party dependencies

None

Blocking Pull requests

Merge after #351

How Has Been This Tested?

User hands on test.

Test Configuration

Name Tested on
OS Fedora Workstation 42
Runtime Java 21
Dependency Manager Maven 3.9.9
Framework version Spring Boot 3.2.5
Run parameters
Other configuration

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My changes have been checked, personally or remotely, with @...
  • I have commented my code, particularly in hard-to-understand areas
  • I have resolved all conflicts with the target branch of the PR
  • I have updated and synced my code with the target branch
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing tests pass locally with my changes:
    • Lint test
    • Unit tests
    • Integration tests
  • I have checked my contribution with code analysis tools:
  • I have made corresponding changes to the documentation:
    • Developer documentation
    • User Guides
    • Migration Guides

Summary by CodeRabbit

  • New Features

    • Added support for multi-value string fields; these are now indexed and searchable.
    • Enhanced case indexing: Title and Author support full-text and exact-match (keyword) queries; Visual ID and Case ID are indexed for filtering/sorting.
  • Refactor

    • Improved search sorting: per-field sorts handle unmapped fields and place missing values last for more consistent results.
  • Bug Fixes

    • Boolean fields now correctly initialize their value, ensuring accurate indexing and search behavior.

- 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
@Smotanka Smotanka self-assigned this Sep 4, 2025
@Smotanka Smotanka added bugfix A change that fixes a bug Small labels Sep 4, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 4, 2025

Walkthrough

Adds Elastic support for string collection fields, including a new domain and adapter class, updates case mappings (title, ids, authorName), fixes BooleanField constructor assignment, and revises ElasticCaseService search to build explicit sorts with unmapped type and missing handling while bypassing previous unmapped-sort resolver.

Changes

Cohort / File(s) Summary of changes
Elastic mapping service: StringCollectionField support
application-engine/.../elastic/service/ElasticCaseMappingService.java
Detects StringCollectionField in transform logic; adds transformStringCollectionField to index non-empty string collections as arrays; skips null/empty collections.
Elastic domain: StringCollectionField (core)
nae-object-library/.../objects/elastic/domain/StringCollectionField.java
Adds abstract StringCollectionField extending TextField with String[] collectionValue, constructor, and Lombok annotations.
Spring adapter: StringCollectionField mapping
nae-spring-core-adapter/.../adapter/spring/elastic/domain/StringCollectionField.java
Adds adapter class extending core StringCollectionField; annotates getters for fulltext (Text) and collection (Keyword); constructor delegates to super.
Elastic case mappings
nae-spring-core-adapter/.../adapter/spring/elastic/domain/ElasticCase.java
Updates annotations: id also Keyword; adds title (Text with keyword subfield), visualId (Keyword), caseId alias (Keyword); adjusts creationDate type import; authorName to MultiField (Text + keyword).
Boolean field constructor fix
nae-spring-core-adapter/.../adapter/spring/elastic/domain/BooleanField.java
Initializes booleanValue in constructor after super(value).
Search and sort behavior
application-engine/.../elastic/service/ElasticCaseService.java
Reworks query build: uses NativeQueryBuilder with pageable; iterates pageable sorts to add per-field sort with asc/desc, unmappedType=Keyword, missing="_last"; comments out previous resolveUnmappedSortAttributes call; retains helper unused.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant ElasticCaseService
  participant QueryBuilder as NativeQueryBuilder
  participant ES as Elasticsearch

  Client->>ElasticCaseService: search(query, pageable)
  activate ElasticCaseService
  ElasticCaseService->>ElasticCaseService: buildQuery(query, pageable)
  activate QueryBuilder
  Note over ElasticCaseService,QueryBuilder: Build NativeQuery with query + PageRequest
  ElasticCaseService->>QueryBuilder: For each sort order<br/>- field, asc/desc<br/>- unmappedType=Keyword<br/>- missing="_last"
  deactivate QueryBuilder
  ElasticCaseService->>ES: execute(query)
  ES-->>ElasticCaseService: results
  ElasticCaseService-->>Client: Page<ElasticCase>
  deactivate ElasticCaseService
Loading
sequenceDiagram
  autonumber
  participant Mapper as ElasticCaseMappingService
  participant DF as DataField
  participant NetField as Net StringCollectionField
  participant EDoc as Elastic Document

  DF->>Mapper: transformDataField(DF, NetField)
  alt NetField is StringCollectionField
    Mapper->>Mapper: transformStringCollectionField(DF, NetField)
    alt collection null/empty
      Mapper-->>DF: Optional.empty()
    else collection has values
      Mapper-->>DF: Optional.of(Elastic StringCollectionField with String[])
      Mapper->>EDoc: include field
    end
  else other field types
    Mapper->>Mapper: existing transform paths
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot added improvement A change that improves on an existing feature Medium labels Sep 4, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b8abb83 and 82cf7bd.

📒 Files selected for processing (5)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java (2 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java (1 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java (1 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (1 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-08-19T20:13:40.087Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
🧬 Code graph analysis (2)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java (2)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java (1)
  • Data (8-19)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java (1)
  • NoArgsConstructor (9-30)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java (1)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java (1)
  • Data (11-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (3)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java (1)

15-18: Constructor fix correctly initializes booleanValue

Assigning the field after super(value) prevents null mapping/serialization issues. LGTM.

Please run an index round-trip test to confirm boolean fields now persist and read back correctly.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (1)

31-35: Add @field(type = Keyword) to id — OK

Keyword mapping for id aligns with exact-match querying. LGTM.

application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java (1)

86-88: Type added: StringCollectionField path recognized — OK

Branching into a dedicated transformer keeps the mapper cohesive.

Comment on lines +132 to +138
protected Optional<DataField> 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();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

⚠️ Potential issue

Unsafe cast and null-element handling in transformStringCollectionField

Direct toArray(new String[0]) will throw if the collection contains non-String elements; nulls are not filtered. Make it type-safe and robust.

Apply this diff:

-    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();
+    if (!(dataField.getValue() instanceof Collection<?> c) || c.isEmpty()) {
+        return Optional.empty();
+    }
+    List<String> values = c.stream()
+            .filter(Objects::nonNull)
+            .filter(String.class::isInstance)
+            .map(String.class::cast)
+            .toList();
+    if (values.isEmpty()) {
+        return Optional.empty();
+    }
+    return Optional.of(new com.netgrif.application.engine.adapter.spring.elastic.domain.StringCollectionField(values.toArray(new String[0])));

I can add unit tests covering: null/empty collections, mixed-type collections (ensure filtering), and successful mapping.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected Optional<DataField> 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<DataField> transformStringCollectionField(
com.netgrif.application.engine.objects.workflow.domain.DataField dataField,
com.netgrif.application.engine.objects.petrinet.domain.dataset.StringCollectionField netField) {
// If value is not a non-empty Collection, bail out
if (!(dataField.getValue() instanceof Collection<?> c) || c.isEmpty()) {
return Optional.empty();
}
// Filter out nulls and non-String instances
List<String> values = c.stream()
.filter(Objects::nonNull)
.filter(String.class::isInstance)
.map(String.class::cast)
.toList();
// If no valid String elements remain, bail out
if (values.isEmpty()) {
return Optional.empty();
}
// Build the elastic StringCollectionField from the filtered array
return Optional.of(
new com.netgrif.application.engine.adapter.spring.elastic.domain.StringCollectionField(
values.toArray(new String[0])
)
);
}
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java
around lines 132 to 138, the current transformStringCollectionField does an
unsafe cast using toArray(new String[0]) and doesn't handle nulls or non-String
elements; change it to iterate the collection safely, filter out nulls and
non-String elements, convert remaining elements to String (e.g., via
String.valueOf or cast after instanceof), collect into a String[] and only
return Optional.of(...) when the resulting array is non-empty; if the input is
null/empty or filtering yields no elements, return Optional.empty().

Comment on lines +13 to +18
public String[] collectionValue;

public StringCollectionField(String[] values) {
super(values);
this.collectionValue = values;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Encapsulation of collectionValue

Public mutable array invites accidental external mutation. Prefer at least protected to limit exposure (the adapter uses an accessor anyway).

Apply this minimal change:

-    public String[] collectionValue;
+    protected String[] collectionValue;

If feasible, consider returning an unmodifiable copy in the getter to prevent aliasing.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public String[] collectionValue;
public StringCollectionField(String[] values) {
super(values);
this.collectionValue = values;
}
protected String[] collectionValue;
public StringCollectionField(String[] values) {
super(values);
this.collectionValue = values;
}
🤖 Prompt for AI Agents
In
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java
around lines 13–18, the public mutable array collectionValue exposes internal
state; change its visibility to protected (or private) and update the
constructor to defensively copy the incoming String[] (e.g., Arrays.copyOf) to
avoid aliasing. Also adjust or add the getter to return a defensive copy or an
unmodifiable list/array to prevent external mutation, and update any callers
(the adapter) to use the accessor rather than direct field access.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java (1)

351-357: Javadoc now misleading; update to reflect default-realm behavior

Docs still say “from users in specified realms” but the method no longer accepts realm IDs and, per impl, operates on the default realm. Also, prefer parameter name petriNet for consistency.

Apply this diff to align the contract:

-    /**
-     * Removes roles associated with a deleted Petri net from users in specified realms.
-     *
-     * @param process the deleted Petri net
-     */
-    void removeRoleOfDeletedPetriNet(PetriNet process);
+    /**
+     * Removes all roles of the deleted Petri net from users in the default realm.
+     *
+     * @param petriNet the deleted Petri net
+     */
+    void removeRoleOfDeletedPetriNet(PetriNet petriNet);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 82cf7bd and 6b563c1.

📒 Files selected for processing (3)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1 hunks)
  • nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java (2 hunks)
  • nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: task-list-completed
🔇 Additional comments (1)
nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java (1)

19-19: LGTM: Set import

Import addition is correct for the new overload.

log.info("[" + processId + "]: Initiating deletion of Petri net " + petriNet.getIdentifier() + " version " + petriNet.getVersion().toString());

userService.removeRoleOfDeletedPetriNet(petriNet, null);
userService.removeRoleOfDeletedPetriNet(petriNet);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Realm scope change: confirm intended behavior and tenant impact

Switching to userService.removeRoleOfDeletedPetriNet(petriNet) implies cleanup only in the default realm per the new impl. If multi-realm deployments are supported, this narrows scope versus the previous multi-realm variant and may leave orphaned roles in non-default realms.

If multi-realm cleanup is required, consider a service variant that iterates all realms or accepts an explicit realm set and use that here.

🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
around line 535, the call was changed to
userService.removeRoleOfDeletedPetriNet(petriNet) which only cleans up roles in
the default realm; verify whether multi-realm deployments must also have roles
removed. If multi-realm cleanup is required, replace this call with a
userService method that iterates all realms or accepts an explicit realm set
(e.g., removeRoleOfDeletedPetriNetAcrossRealms(petriNet, realms) or loop through
realm list and call existing removal per realm); if only default-realm cleanup
is intended, add an explicit comment and/or assert to document that behavior so
it’s clear non-default realms are intentionally unchanged.

Comment on lines 525 to 536
public void removeRoleOfDeletedPetriNet(PetriNet petriNet) {
String defaultRealm = collectionNameProvider.getDefaultRealmCollection();
Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
Page<AbstractUser> users;
do {
users = findAllByProcessRoles(petriNet.getRoles().values().stream().map(ProcessRole::get_id).collect(Collectors.toSet()), defaultRealm, pageable);
users.forEach(u -> {
petriNet.getRoles().forEach((k, role) -> removeRole(u, role.get_id()));
});
pageable = pageable.next();
} while (users.hasNext());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Critical: passing collection name where realmId is expected + pagination skip bug

  • defaultRealm is a collection name; findAllByProcessRoles expects a realmId and internally calls getCollectionNameForRealm(realmId). Passing a collection name yields a wrong collection lookup.
  • Incrementing pageable while removing matches can skip users due to page reordering.

Fix both issues and batch-remove roles per user:

-    public void removeRoleOfDeletedPetriNet(PetriNet petriNet) {
-        String defaultRealm = collectionNameProvider.getDefaultRealmCollection();
-        Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
-        Page<AbstractUser> users;
-        do {
-            users = findAllByProcessRoles(petriNet.getRoles().values().stream().map(ProcessRole::get_id).collect(Collectors.toSet()), defaultRealm, pageable);
-            users.forEach(u -> {
-                petriNet.getRoles().forEach((k, role) -> removeRole(u, role.get_id()));
-            });
-            pageable = pageable.next();
-        } while (users.hasNext());
-    }
+    public void removeRoleOfDeletedPetriNet(PetriNet petriNet) {
+        String defaultCollection = collectionNameProvider.getDefaultRealmCollection();
+        String realmId = collectionNameProvider.getRealmIdFromCollectionName(defaultCollection);
+        Set<ProcessResourceId> roleIds = petriNet.getRoles().values()
+                .stream()
+                .map(ProcessRole::get_id)
+                .collect(Collectors.toSet());
+        Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+        Page<AbstractUser> users;
+        do {
+            users = findAllByProcessRoles(roleIds, realmId, pageable);
+            users.forEach(u -> removeRolesById(u, roleIds));
+            // Reset to page 0 to avoid skipping due to deletions changing page boundaries
+            pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+        } while (users.hasContent());
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void removeRoleOfDeletedPetriNet(PetriNet petriNet) {
String defaultRealm = collectionNameProvider.getDefaultRealmCollection();
Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
Page<AbstractUser> users;
do {
users = findAllByProcessRoles(petriNet.getRoles().values().stream().map(ProcessRole::get_id).collect(Collectors.toSet()), defaultRealm, pageable);
users.forEach(u -> {
petriNet.getRoles().forEach((k, role) -> removeRole(u, role.get_id()));
});
pageable = pageable.next();
} while (users.hasNext());
}
public void removeRoleOfDeletedPetriNet(PetriNet petriNet) {
String defaultCollection = collectionNameProvider.getDefaultRealmCollection();
String realmId = collectionNameProvider.getRealmIdFromCollectionName(defaultCollection);
Set<ProcessResourceId> roleIds = petriNet.getRoles().values()
.stream()
.map(ProcessRole::get_id)
.collect(Collectors.toSet());
Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
Page<AbstractUser> users;
do {
users = findAllByProcessRoles(roleIds, realmId, pageable);
users.forEach(u -> removeRolesById(u, roleIds));
// Reset to page 0 to avoid skipping due to deletions changing page boundaries
pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
} while (users.hasContent());
}
🤖 Prompt for AI Agents
In
nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
around lines 525-536, the code currently passes a collection name into
findAllByProcessRoles and advances pages which can skip results; fix by using
the real realmId instead of collectionNameProvider.getDefaultRealmCollection()
(use whatever method returns the default realm id, e.g.
collectionNameProvider.getDefaultRealmId() or resolve the realm from the
collection via collectionNameProvider.getRealmForCollection(...)) so
findAllByProcessRoles receives a realmId, and avoid pageable.next() reordering
issues by not advancing pages while matching/removing roles — either collect all
matching user ids up front in safe-sized batches or repeatedly fetch page 0
until no more matches; for each user, compute the set of role ids to remove
(intersection of petriNet.getRoles() ids and user roles) and call the batch
role-removal API (e.g. removeRoles(user, roleIds)) rather than removing roles
one-by-one.

@machacjozef machacjozef changed the base branch from release/7.0.0-rev7 to release/7.0.0-rev6.1 September 5, 2025 15:58
@machacjozef machacjozef changed the base branch from release/7.0.0-rev6.1 to release/7.0.0-rev7 September 5, 2025 15:59
- 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.
tuplle
tuplle previously approved these changes Sep 5, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java (1)

26-30: Correct: collectionValue as Keyword (not Nested).
This resolves the earlier nested/field-shadowing issue.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (1)

35-38: Consider multi-fields for title/visualId if full-text search is needed.
If only exact filters/aggregations are used, keep as Keyword. Otherwise switch to text+keyword subfield.

Apply if needed:

-    @Field(type = Keyword)
-    public String getTitle() {
+    @MultiField(mainField = @Field(type = Text),
+        otherFields = @InnerField(suffix = "keyword", type = Keyword))
+    public String getTitle() {
         return super.getTitle();
     }

-    @Field(type = Keyword)
-    public String getVisualId() {
+    @MultiField(mainField = @Field(type = Text),
+        otherFields = @InnerField(suffix = "keyword", type = Keyword))
+    public String getVisualId() {
         return super.getVisualId();
     }

Also applies to: 40-44

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6b563c1 and ff3a995.

📒 Files selected for processing (2)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (3 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-19T20:13:40.087Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.

Applied to files:

  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
🧬 Code graph analysis (1)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java (3)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java (1)
  • Data (8-19)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (1)
  • NoArgsConstructor (17-139)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java (1)
  • NoArgsConstructor (9-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (7)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java (3)

11-14: Good: adapter maps via accessors, no field shadowing.
Class setup with Lombok + callSuper is appropriate.


16-18: Constructor correctly delegates without duplicating state.
Avoids reassigning collectionValue locally. LGTM.


20-24: fulltextValue → Text mapping looks consistent with other fields.
Keeps analyzed search behavior. LGTM.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (4)

15-15: Static wildcard import is fine here.
Consistent with other mappings in this class.


30-33: Id as Keyword is appropriate.
No concerns.


45-49: Duplicate of id as caseId — verify necessity.
Confirm consumers require both; dual fields increase storage and mapping surface.


65-66: Date mapping with millis format looks correct.
Matches LocalDateTime usage.

Comment on lines +26 to +30
@Override
@Field(type = Keyword)
public String[] getCollectionValue() {
return super.getCollectionValue();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Plan reindex if changing existing mappings.
If any existing index used nested for this field, switch via alias → build new index → reindex → alias swap.

# Conflicts:
#	application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
- Modified the `findFilter` method to use `title` instead of `title.keyword` in the filter query string.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5a7c88b and 4a3527e.

📒 Files selected for processing (1)
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed

- 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.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (4)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (4)

13-13: Static FieldType import consistency — good

Using import static ...FieldType.*; keeps the annotations concise and consistent with prior style feedback.


33-40: Title multi-field mapping — LGTM

Text + keyword subfield addresses full-text search and exact filters/sorts.


42-45: VisualId: confirm exact-match-only; consider multi-field if search/sort needs

If UI/queries ever require full-text search or case-insensitive sorting on visualId, mirror the title mapping. Otherwise keep as Keyword.

Optional diff:

-    @Field(type = Keyword)
-    public String getVisualId() {
+    @MultiField(
+        mainField = @Field(type = Text),
+        otherFields = { @InnerField(suffix = "keyword", type = Keyword) }
+    )
+    public String getVisualId() {

47-50: caseId duplicates id; plan compatibility and eventual cleanup

Per prior learnings, stringId was removed and id holds the Mongo ObjectId. Re-introducing caseId (mirror of id) is likely for compatibility. Make the migration plan explicit to avoid long-term duplication.

I used retrieved learnings about id/stringId consolidation when assessing this.

Optional diffs:

  • Auto-populate caseId from id on index (keeps writers simple):
-    @Field(type = Keyword)
+    @Field(type = Keyword, copyTo = {"caseId"})
     public String getId() {
         return super.getId();
     }
  • Mark caseId as transitional:
-    @Field(type = Keyword)
+    @Deprecated(since = "7.0.0-rev7", forRemoval = true)
+    @Field(type = Keyword)
     public String getCaseId() {
         return super.getId();
     }

Please confirm whether caseId is needed beyond migration; if not, add a deprecation note and removal ticket.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4a3527e and ebd9c5c.

📒 Files selected for processing (1)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-19T20:13:40.087Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.

Applied to files:

  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (2)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (2)

28-31: Be explicit about dual-mapping id (_id + source field).

Annotating getId() with @Field(type = Keyword) stores the id both as Elasticsearch _id and as a keyword field id. This is fine if you need aggregations/filters on id, but it increases index size and can confuse consumers.

Please confirm:

  • Consumers expect both _id and id fields.
  • Index templates and dashboards use id for terms/sorts; otherwise, consider dropping the @Field to save storage.

1-146: Manual verification required for Elasticsearch field usage

  • The initial grep for caseId, id, and visualId returned excessive noise due to generic matches; please refine your search (e.g., target query/DSL builder code, Spring Data repositories, or Elasticsearch templates) or manually inspect relevant repository and query methods to confirm that the new mappings (Keyword vs Text and .keyword sub-fields) align with existing usage.
  • Ensure no consumers rely on full-text search on visualId or expect inverted-index behavior for caseId/id, and verify that any .keyword references (e.g., in sorting or aggregations) correspond to the newly defined sub-fields.

Comment on lines +28 to 31
@Field(type = Keyword)
public String getId() {
return super.getId();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Mapping changes: plan safe rollout (templates, reindex, aliases)

  • Update index templates to reflect new/changed mappings.
  • Create a new index (vN+1), _reindex from old, then atomically switch the write/read alias.
  • Backfill and verify dashboards/queries for id vs caseId, and title/authorName .keyword usage.

Also applies to: 33-40, 42-45, 47-50, 67-70, 82-89

🤖 Prompt for AI Agents
In
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
around lines 28-31 (and similarly for ranges 33-40, 42-45, 47-50, 67-70, 82-89),
the mapping change (e.g., annotating getters with @Field(type = Keyword))
requires a safe rollout: update your Elasticsearch index templates to include
the new mappings, create a new index version (vN+1) with the updated template,
reindex data from the old index into the new one, atomically switch the
application read/write alias to the new index, and then verify/backfill
dashboards and queries to use the correct field names (`id` vs `caseId`,
`title`/`authorName` and their `.keyword` variants) so consumers are not broken
by the mapping change.

Comment on lines +67 to 70
@Field(type = Date, format = DateFormat.date_hour_minute_second_millis)
public LocalDateTime getCreationDate() {
return super.getCreationDate();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Date format change: ensure backward compatibility

Switching to DateFormat.date_hour_minute_second_millis may break reads/writes if existing indices stored creationDate with date_optional_time. Support both formats if mixed data exists or clients send varying precisions.

Suggested change:

-    @Field(type = Date, format = DateFormat.date_hour_minute_second_millis)
+    @Field(type = Date, format = {
+        DateFormat.date_hour_minute_second_millis,
+        DateFormat.date_optional_time
+    })
     public LocalDateTime getCreationDate() {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Field(type = Date, format = DateFormat.date_hour_minute_second_millis)
public LocalDateTime getCreationDate() {
return super.getCreationDate();
}
@Field(type = Date, format = {
DateFormat.date_hour_minute_second_millis,
DateFormat.date_optional_time
})
public LocalDateTime getCreationDate() {
return super.getCreationDate();
}
🤖 Prompt for AI Agents
In
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
around lines 67-70, the @Field annotation currently forces
DateFormat.date_hour_minute_second_millis which can break compatibility with
existing indices using date_optional_time; change the mapping to accept both
formats by switching to DateFormat.custom and providing a pattern that includes
both formats separated by "||" (i.e., include the millisecond ISO pattern and
the strict_date_optional_time/date_optional_time variants) so reads/writes
tolerate mixed precisions and legacy values.

Comment on lines +82 to 89
@MultiField(
mainField = @Field(type = Text),
otherFields = {
@InnerField(suffix = "keyword", type = Keyword)
})
public String getAuthorName() {
return super.getAuthorName();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

AuthorName multi-field mapping — LGTM

Covers full-text search and exact filters/sorts. If you need case-insensitive sorting, consider a normalizer-backed keyword subfield in the index settings.

🤖 Prompt for AI Agents
In
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
around lines 82 to 89, the current MultiField mapping on getAuthorName()
provides a Text main field and a keyword subfield but does not support
case-insensitive sorting; if you need case-insensitive sorts, add an additional
InnerField (e.g., suffix "keyword_normalized") of type Keyword that uses a
normalizer, and update the Elasticsearch index settings to define that
normalizer (lowercase filter) so the new keyword subfield can be used for
case-insensitive exact matching and sorting.

machacjozef and others added 5 commits September 6, 2025 03:37
- 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.
- 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`.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ebd9c5c and 3195287.

📒 Files selected for processing (1)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-08-19T20:13:40.087Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (3)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java (3)

4-4: Import for FieldType is correct.

Needed for .unmappedType(...) on sort; no issues.


194-197: Pageable sort intentionally dropped; confirm callers don’t rely on Page sort metadata.

You build ES sorts explicitly and pass an unsorted PageRequest to the query. Results are fine, but returned Page metadata (from SearchHitSupport.searchPageFor(hits, query.getPageable())) won’t reflect sort. Verify UI/clients don’t read sort from Pageable.


392-395: Drop .keyword suffix for fulltextValue queries
Term queries must target dataSet.<key>.fulltextValue, as fulltextValue is already mapped as a keyword field.

- dataQuery.must(termQuery("dataSet." + field.getKey() + ".fulltextValue.keyword", field.getValue())._toQuery());
+ dataQuery.must(termQuery("dataSet." + field.getKey() + ".fulltextValue", field.getValue())._toQuery());

Comment on lines +198 to +207
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")
));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Hard-coding unmappedType=Keyword can break date/numeric sorts (e.g., creationDate).

Elastic expects unmapped_type to match the field’s actual mapping. Using Keyword for dates/numbers can yield incorrect ordering or errors. Also handle _score via score sort, not field sort.

Apply this diff to select unmappedType per field and support _score:

-        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")
-            ));
-        }
+        for (org.springframework.data.domain.Sort.Order o : pageable.getSort()) {
+            String prop = o.getProperty();
+            if ("_score".equals(prop)) {
+                builder.withSort(s -> s.score(sc -> sc.order(o.isAscending()
+                        ? co.elastic.clients.elasticsearch._types.SortOrder.Asc
+                        : co.elastic.clients.elasticsearch._types.SortOrder.Desc)));
+                continue;
+            }
+            builder.withSort(s -> s.field(f -> f
+                    .field(prop)
+                    .order(o.isAscending()
+                            ? co.elastic.clients.elasticsearch._types.SortOrder.Asc
+                            : co.elastic.clients.elasticsearch._types.SortOrder.Desc)
+                    .unmappedType(resolveSortUnmappedType(prop))
+                    .missing("_last")));
+        }

Then add this helper in the class:

// Heuristic mapping; extend as needed to cover your mapped fields
private FieldType resolveSortUnmappedType(String property) {
    if (property.endsWith(".keyword")) return FieldType.Keyword;
    if ("creationDate".equals(property) || property.endsWith("Date")) return FieldType.Date;
    if ("priority".equals(property) || property.endsWith(".priority")) return FieldType.Long;
    if (property.equals("_id") || property.endsWith("Id") || property.endsWith("Name")) return FieldType.Keyword;
    return FieldType.Keyword; // safe default for string-ish fields
}
🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java
around lines 198 to 207, the code hard-codes unmappedType=Keyword for all sorts
and doesn't handle score sorts; change the sort builder to pick an unmappedType
based on the field property (use a helper that returns FieldType.Date for
date-like fields, FieldType.Long for numeric/priority, FieldType.Keyword for
ids/names/strings, etc.) and special-case "_score" to emit a score sort rather
than a field sort; also add the suggested resolveSortUnmappedType(String) helper
method in the class and call it when building the sort to supply the correct
unmappedType per field.

@machacjozef machacjozef requested a review from tuplle September 6, 2025 03:34
@machacjozef machacjozef merged commit e1bf989 into release/7.0.0-rev7 Sep 6, 2025
6 of 7 checks passed
@machacjozef machacjozef deleted the NAE-2197 branch September 6, 2025 03:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix A change that fixes a bug improvement A change that improves on an existing feature Medium Small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants