Skip to content

feat(api): implement POST endpoint for searching entities#63

Draft
evebrnd wants to merge 7 commits into
mainfrom
feat/implement-search-entities-system
Draft

feat(api): implement POST endpoint for searching entities#63
evebrnd wants to merge 7 commits into
mainfrom
feat/implement-search-entities-system

Conversation

@evebrnd
Copy link
Copy Markdown
Collaborator

@evebrnd evebrnd commented May 29, 2026

PR Description

What this PR Provides

Adds POST /api/v1/entities/search an endpoint for advanced paginated entity search/filter

  • Supports free-text search across identifier, name, templateIdentifier and all property values simultaneously
  • Supports a structured filter tree with nested AND/OR groups and 10 operators: EQ, NEQ, CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH, GT, GTE, LT, LTE
  • Filter can target entity fields (template, identifier, name), properties (property.{name}), relations (relation.{name}, relation.{name}.identifier) and reverse-relations (relations_as_target.{name}.{field})
  • Both modes can be combined in a single request (query + filter)
  • Replaces EntityQueryParserService with a cleaner three-way split: EntityFilterDslParser (existing DSL), SearchFilterParser (filter tree parsing), SearchFilterValidationService (validation)
  • Adds EntitySearchSpecification and JpaPredicateBuilder for composable JPA predicate building
  • Adds Flyway migration V3_6 with GIN trigram and functional btree indexes on entity, property, and relation tables for ILIKE/EQ search performance
  • Requires the pg_trgm PostgreSQL extension, initialized via init-extensions.sql

Review

The reviewer must double-check these points:

  • The reviewer has tested the feature
  • The reviewer has reviewed the implementation of the feature
  • The documentation has been updated
  • The feature implementation respects the Technical Doc / ADR previously produced

How to test

Free-text search

 POST /api/v1/entities/search
 { "query": "checkout", "page": 0, "size": 20 }

Should return all entities whose identifier, name, templateIdentifier or any property value contains "checkout" (case-insensitive).

Structured filter on a single template

POST /api/v1/entities/search
{
  "filter": {
    "connector": "AND",
    "criteria": [
      { "field": "template", "operation": "EQ", "value": "microservice" },
      { "field": "property.language", "operation": "EQ", "value": "JAVA" }
    ]
  },
  "page": 0, "size": 20
}

Should return all microservices entities with language = JAVA.

Combined free-text + filter:

 POST /api/v1/entities/search
 { "query": "payment", "filter": { "connector": "AND", "criteria": [{ "field": "template", "operation": "EQ", "value": "microservice" }] } }

Should return microservices entities that also match "payment" in any text field.

Error case — invalid filter:

 POST /api/v1/entities/search
 { "filter": { "connector": "AND", "criteria": [] } }

Should return 400 Bad Request with a validation error message

Breaking changes

N/A

evebrnd and others added 4 commits May 29, 2026 17:16
Method was lost when resolving rebase conflict — the --ours strategy
on EntityDtoOutMapper.java preserved main's Spotless formatting but
dropped the new search-page mapping method added by the branch.

Also applies Spotless formatting to all files touched during rebase
resolution that still had indentation/whitespace violations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented May 29, 2026

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
30005138 Triggered Generic Password 3077707 src/test/java/com/decathlon/idp_core/AbstractIntegrationTest.java View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new cross-template POST /api/v1/entities/search API for free-text and structured entity search, backed by new domain parsing/validation services, JPA specifications, persistence support, migration indexes, and integration/unit coverage.

Changes:

  • Introduces structured search DTOs, parser/validator, domain models, repository port method, controller endpoint, and exception mapping.
  • Adds JPA predicate/specification support for global text search and nested filter criteria.
  • Adds PostgreSQL trigram index migration, local/test extension initialization, and broad test coverage for search scenarios.

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
.github/instructions/java.instructions.md Adds JavaDoc Markdown guidance.
docker-compose.yml Mounts local PostgreSQL extension init script.
scripts/init-extensions.sql Adds local pg_trgm initialization.
src/main/resources/db/migration/V3_6__add_search_performance_indexes.sql Adds search performance indexes.
src/test/resources/db/init/init-extensions.sql Adds Testcontainers pg_trgm initialization.
src/main/java/.../EntityController.java Adds POST /entities/search endpoint.
src/main/java/.../SwaggerDescription.java Adds Swagger constants for search endpoint.
src/main/java/.../EntitySearchRequestDtoIn.java Adds search request DTO.
src/main/java/.../FilterNodeDtoIn.java Adds polymorphic filter node DTO.
src/main/java/.../SearchFilterMapper.java Maps API filter DTOs to raw domain filter nodes.
src/main/java/.../EntityDtoOutMapper.java Adds multi-template search page DTO mapping.
src/main/java/.../ApiExceptionHandler.java Maps new filter/search exceptions to HTTP 400.
src/main/java/.../SearchFilterNode.java Adds typed search filter tree model.
src/main/java/.../RawSearchFilterNode.java Adds raw search filter tree model.
src/main/java/.../LogicalConnector.java Adds AND/OR connector enum.
src/main/java/.../SearchOperator.java Adds search operator enum.
src/main/java/.../SearchConstraints.java Adds search limits and allowed sort fields.
src/main/java/.../FilterConstraints.java Extracts legacy filter DSL limits.
src/main/java/.../ValidationMessages.java Adds search validation messages.
src/main/java/.../InvalidSearchQueryException.java Adds search validation exception.
src/main/java/.../InvalidFilterDslException.java Adds renamed legacy filter DSL exception.
src/main/java/.../InvalidQueryDslException.java Removes old legacy filter DSL exception.
src/main/java/.../EntityFilterDslParser.java Renames/refactors legacy q= parser.
src/main/java/.../SearchFilterParser.java Parses structured search filter trees.
src/main/java/.../SearchFilterValidationService.java Validates structured search filters and query text.
src/main/java/.../EntityService.java Adds search orchestration, validation, pagination, and sorting.
src/main/java/.../EntityRepositoryPort.java Adds search persistence port method.
src/main/java/.../PostgresEntityAdapter.java Implements search using JPA specifications.
src/main/java/.../EntityFilterSpecification.java Renames legacy specification and shares predicate builder.
src/main/java/.../EntitySearchSpecification.java Adds structured search JPA specification builder.
src/main/java/.../JpaPredicateBuilder.java Adds shared predicate and LIKE escaping utilities.
src/test/java/.../AbstractIntegrationTest.java Initializes test PostgreSQL extension.
src/test/java/.../EntityControllerTest.java Adds integration tests for search endpoint.
src/test/java/.../EntityServiceTest.java Adds service tests for search validation/pagination/sort.
src/test/java/.../EntityFilterDslParserTest.java Updates legacy parser tests after rename.
src/test/java/.../SearchFilterParserTest.java Adds structured parser unit tests.
src/test/java/.../SearchFilterValidationServiceTest.java Adds search validation unit tests.
src/test/java/.../EntitySearchSpecificationTest.java Adds search specification construction tests.
src/test/java/.../JpaPredicateBuilderTest.java Updates LIKE escaping tests for shared builder.
src/test/resources/integration_test/json/entity/v1/searchEntities_200_byRelationNameContains.json Adds expected search fixture.
src/test/resources/integration_test/json/entity/v1/searchEntities_200_byRelationNameEq.json Adds expected search fixture.
src/test/resources/integration_test/json/entity/v1/searchEntities_200_byRelationsAsTarget.json Adds expected search fixture.
src/test/resources/integration_test/json/entity/v1/searchEntities_200_byRelationsAsTargetPresence.json Adds expected search fixture.
src/test/resources/integration_test/json/entity/v1/searchEntities_200_byTemplateAndProperty.json Adds expected search fixture.
src/test/resources/integration_test/json/entity/v1/searchEntities_200_neq.json Adds expected search fixture.
src/test/resources/integration_test/json/entity/v1/searchEntities_200_orTemplates.json Adds expected search fixture.
src/test/resources/integration_test/json/entity/v1/searchEntities_200_startsWith.json Adds expected search fixture.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 1, 2026

Comment thread .pre-commit-config.yaml
pass_filenames: false
files: \.java$
stages: [commit]
stages: [pre-commit]
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[WARNING] hook id `spotless-check` uses deprecated stage names (commit) which will be removed in a future version.  run: `pre-commit migrate-config` to automatically fix this.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 49 out of 49 changed files in this pull request and generated 2 comments.

Comment on lines +44 to +46
/// case-insensitive matching, allowing GIN trigram indexes (V3_5) to be leveraged.
/// SQL wildcards (`%` and `_`) in user-supplied values are escaped to prevent unintended
/// pattern matching. EQ and NEQ use `LOWER()` with functional btree indexes (V3_4).
@@ -0,0 +1,39 @@
package com.decathlon.idp_core.domain.model.entity;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

RawSearchFilterNode représente un arbre de filtre brut utilisé pour le parsing et la validation, plutôt qu'une entité métier.
Je me demande si le package domain.model.entity reflète bien sa responsabilité.
Est-ce qu'un package dédié au search/filter (domain.search, domain.filter etc.) ne serait pas plus explicite ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Effectivement j'ai déplacé les objets domaines liés au search dans un package dédié domain.search 👍

return entityRepository.search(filter, query, pageable);
}

private Pageable buildPageable(int page, int size, String sort) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

On a du Spring Data (Page, Pageable, Sort) directement dans le EntityService, ce qui couple un peu le domaine au framework.

Proposition possible pour alléger ça :

Garder le EntityService centré sur l’orchestration + validation métier uniquement

Remplacer Page/Pageable/Sort par des types neutres (ex: page, size, sort ou PaginationCriteria) dans le domaine

Déplacer la construction du PageRequest + parsing du sort dans l’adapter d’infrastructure (impl EntityRepositoryPort) ->
PaginatedResult search(SearchFilterNode filter, String query, PaginationCriteria paginationCriteria);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants