From e47239d01c91769d494d8f7a9bbf5100759fc967 Mon Sep 17 00:00:00 2001 From: rng Date: Wed, 6 May 2026 09:54:02 +1000 Subject: [PATCH] Fix ID parsing --- .../elastic/CQLToElasticFilterFactory.java | 13 +++-- .../server/core/parser/elastic/IdImpl.java | 51 +++++++++++++++++++ .../server/core/service/ElasticSearch.java | 41 ++++++++------- .../ogcapi/server/common/RestApiTest.java | 21 ++++++++ 4 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/IdImpl.java diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/CQLToElasticFilterFactory.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/CQLToElasticFilterFactory.java index d5e6eff1..8c64809d 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/CQLToElasticFilterFactory.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/CQLToElasticFilterFactory.java @@ -146,7 +146,12 @@ public FunctionName functionName(Name name, List> list, Parameter(Arrays.asList(featureIds))); + } + + @Override + public Id id(Set set) { + return new IdImpl<>(set, collectionFieldType); } @Override @@ -227,12 +232,6 @@ public Not not(Filter filter) { } } - @Override - public Id id(Set set) { - logger.debug("id {}", set); - return null; - } - @Override public PropertyIsBetween between(Expression expression, Expression expression1, Expression expression2) { logger.debug("BETWEEN {} {}, {}", expression, expression1, expression2); diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/IdImpl.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/IdImpl.java new file mode 100644 index 00000000..d89699d8 --- /dev/null +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/IdImpl.java @@ -0,0 +1,51 @@ +package au.org.aodn.ogcapi.server.core.parser.elastic; + +import au.org.aodn.ogcapi.server.core.model.enumeration.CQLFieldsInterface; +import au.org.aodn.ogcapi.server.core.model.enumeration.StacBasicField; +import co.elastic.clients.elasticsearch._types.FieldValue; +import co.elastic.clients.elasticsearch._types.query_dsl.TermsQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField; +import org.opengis.filter.FilterVisitor; +import org.opengis.filter.Id; +import org.opengis.filter.identity.Identifier; + +import java.util.HashSet; +import java.util.Set; + +/** + * ECQL parser id different, you need to pass in ID IN (id1, id2, id3), then this class will trigger to create a + * TermsQuery for id. Other fields are fine with k=v + * @param + */ +public class IdImpl & CQLFieldsInterface> extends QueryHandler implements Id { + + protected Set identifiers = new HashSet<>(); + + public IdImpl(Set identifiers, Class enumType) { + this.identifiers.addAll(identifiers); + this.query = TermsQuery.of(t -> t + .field(StacBasicField.UUID.searchField) + .terms(TermsQueryField.of(f -> f.value(identifiers.stream().map(i -> FieldValue.of(i.toString())).toList()))) + )._toQuery(); + } + + @Override + public Set getIDs() { + return Set.of(); + } + + @Override + public Set getIdentifiers() { + return identifiers; + } + + @Override + public boolean evaluate(Object o) { + return false; + } + + @Override + public Object accept(FilterVisitor filterVisitor, Object o) { + return null; + } +} diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearch.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearch.java index fdbfb9d8..6491e877 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearch.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearch.java @@ -308,26 +308,31 @@ public ElasticSearchBase.SearchResult searchByParameters(Li CQLToElasticFilterFactory factory = new CQLToElasticFilterFactory<>(coor, CQLFields.class); if(cql != null) { - Filter filter = CompilerUtil.parseFilter(Language.ECQL, cql, factory); - - if(filter instanceof QueryHandler handler) { - if(handler.getErrors() == null || handler.getErrors().isEmpty()) { - if(handler.getQuery() != null) { - // There is no error during parsing - filters = List.of(handler.getQuery()); + try { + Filter filter = CompilerUtil.parseFilter(Language.ECQL, cql, factory); + if(filter instanceof QueryHandler handler) { + if(handler.getErrors() == null || handler.getErrors().isEmpty()) { + if(handler.getQuery() != null) { + // There is no error during parsing + filters = List.of(handler.getQuery()); + } + } + else { + throw new IllegalArgumentException( + "ECQL Parse Error", + handler.getErrors() + .stream() + .reduce(null, (e1, e2) -> { + if (e1 == null) return e2; + e1.addSuppressed(e2); + return e1; + })); } } - else { - throw new IllegalArgumentException( - "CQL Parse Error", - handler.getErrors() - .stream() - .reduce(null, (e1, e2) -> { - if (e1 == null) return e2; - e1.addSuppressed(e2); - return e1; - })); - } + } + catch(CQLException ce) { + log.error("Error parsing ECQL", ce); + throw ce; } } // Get the page size after parsing diff --git a/server/src/test/java/au/org/aodn/ogcapi/server/common/RestApiTest.java b/server/src/test/java/au/org/aodn/ogcapi/server/common/RestApiTest.java index ade0fa54..a4630e0d 100644 --- a/server/src/test/java/au/org/aodn/ogcapi/server/common/RestApiTest.java +++ b/server/src/test/java/au/org/aodn/ogcapi/server/common/RestApiTest.java @@ -804,4 +804,25 @@ public void verifyIBoundaryFunctionWorks() throws IOException { assertEquals(1, Objects.requireNonNull(collections.getBody()).getCollections().size(), "hit 1"); assertEquals("516811d7-cd1e-207a-e0440003ba8c79dd", Objects.requireNonNull(collections.getBody()).getCollections().get(0).getId(), "id correct"); } + /** + * The correct syntax for an id query is id IN (xxxxx), use id=xxxx will fail parsing + * @throws IOException - Not expected + */ + @Test + public void verifyQueryByIdWorks() throws IOException { + super.insertJsonToElasticRecordIndex( + // Will hit Shark Bay + "516811d7-cd1e-207a-e0440003ba8c79dd.json", + // Will hit Central Eastern of Auz marine park, but not Shark Bay + "ae86e2f5-eaaf-459e-a405-e654d85adb9c.json" + ); + ResponseEntity collections = testRestTemplate.exchange( + getBasePath() + "/collections?properties=id,geometry&filter=id IN('516811d7-cd1e-207a-e0440003ba8c79dd')", + HttpMethod.GET, + null, + new ParameterizedTypeReference<>() {}); + + assertEquals(1, Objects.requireNonNull(collections.getBody()).getCollections().size(), "hit 1"); + assertEquals("516811d7-cd1e-207a-e0440003ba8c79dd", Objects.requireNonNull(collections.getBody()).getCollections().get(0).getId(), "id correct"); + } }