Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8409228
[maven-release-plugin] prepare release legend-engine-4.121.0
finos-admin Feb 16, 2026
3b30826
[maven-release-plugin] prepare for next development iteration
finos-admin Feb 16, 2026
77d5be6
versioning application query storage
gs-gunjan Feb 25, 2026
802450e
Merge remote-tracking branch 'origin/master'
OmGupta-GS2038 Mar 2, 2026
4d4c754
Capturing attribute metadata in Query
OmGupta-GS2038 Mar 5, 2026
3018bf4
Merge branch 'finos-master' into MetadataAndPagination
OmGupta-GS2038 Mar 5, 2026
75bedc7
Added pagination to the search API
OmGupta-GS2038 Mar 20, 2026
5bc74e7
Merge branch 'master' into MetadataAndPagination
OmGupta-GS2038 Apr 20, 2026
3818a97
Merge branch 'finos-master' into MetadataAndPagination
OmGupta-GS2038 Apr 20, 2026
3b5fc5a
Addressed Comments on MR, added separate API for search, added QueryS…
OmGupta-GS2038 Apr 21, 2026
f22d034
Made PaginatedQuerySpecification and inner class, and addresssed othe…
OmGupta-GS2038 Apr 21, 2026
96ae930
Query History
OmGupta-GS2038 Apr 24, 2026
10302db
Merge branch 'finos-master' into MetadataAndPagination
OmGupta-GS2038 Apr 28, 2026
c635f75
Merge remote-tracking branch 'origin/master'
OmGupta-GS2038 Apr 30, 2026
2a1b288
Merge branch 'master' into MetadataAndPagination
OmGupta-GS2038 Apr 30, 2026
52a407f
getQueryHistory Method to be used
OmGupta-GS2038 Apr 30, 2026
b56bc98
Addressed Comments: PaginatedQuerySearchSpecification extends QuerySe…
OmGupta-GS2038 May 6, 2026
635db45
Merge branch 'finos-master' into MetadataAndPagination
OmGupta-GS2038 May 8, 2026
2dd5a3f
Added a default sort, marked old API as deprecated
OmGupta-GS2038 May 13, 2026
b9a6f2b
Merge branch 'review-metadataandpagination' of https://github.com/OmG…
OmGupta-GS2038 May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.swagger.annotations.ApiParam;
import org.eclipse.collections.api.list.MutableList;
import org.finos.legend.engine.application.query.model.DataCubeQuery;
import org.finos.legend.engine.application.query.model.PaginatedQuerySearchSpecification;
import org.finos.legend.engine.application.query.model.Query;
import org.finos.legend.engine.application.query.model.QueryEvent;
import org.finos.legend.engine.application.query.model.QuerySearchSpecification;
Expand Down Expand Up @@ -58,11 +59,32 @@ private static String getCurrentUser(ProfileManager<CommonProfile> profileManage
return profile != null ? profile.getId() : null;
}

@Deprecated
@POST
@Path("search")
@ApiOperation(value = "Search queries")
@Consumes({MediaType.APPLICATION_JSON})
public Response searchQueries(QuerySearchSpecification searchSpecification, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager<CommonProfile> profileManager)
{
try
{
return Response.ok().entity(this.queryStoreManager.searchQueries(searchSpecification, getCurrentUser(profileManager)).queries).build();
}
catch (Exception e)
{
if (e instanceof ApplicationQueryException)
{
return ((ApplicationQueryException) e).toResponse();
}
return ExceptionTool.exceptionManager(e, LoggingEventType.SEARCH_QUERIES_ERROR, null);
}
}

@POST
@Path("search/paginated")
@ApiOperation(value = "Search queries with pagination")
@Consumes({MediaType.APPLICATION_JSON})
public Response searchQueriesPaginated(PaginatedQuerySearchSpecification searchSpecification, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager<CommonProfile> profileManager)
{
try
{
Expand Down Expand Up @@ -98,6 +120,7 @@ public Response getQueries(@QueryParam("queryIds") @ApiParam("The list of query
}
}


@GET
@Path("{queryId}")
@ApiOperation(value = "Get the query with specified ID")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
Expand All @@ -51,6 +53,7 @@ public class QueryStoreManager
private static final int MAX_NUMBER_OF_QUERIES = 100;
private static final int MAX_NUMBER_OF_EVENTS = 1000;
private static final int QUERY_BATCH_SIZE = 1000;
private static final int MAX_PAGE_SIZE = 100;

private final ObjectMapper objectMapper = new ObjectMapper();
private static final Document EMPTY_FILTER = Document.parse("{}");
Expand Down Expand Up @@ -207,7 +210,7 @@ else if (query.executionContext instanceof DataProductLakehouseAccessExecutionCo
// TODO: we can potentially create a pattern check for version
}

public List<Query> searchQueries(QuerySearchSpecification searchSpecification, String currentUser)
public PaginatedResult searchQueries(QuerySearchSpecification searchSpecification, String currentUser)
{
List<Bson> filters = new ArrayList<>();
if (searchSpecification.searchTermSpecification != null)
Expand Down Expand Up @@ -303,17 +306,58 @@ public List<Query> searchQueries(QuerySearchSpecification searchSpecification, S
{
builder.sortDesc(getSortByField(searchSpecification.sortByOption));
}
else
{
builder.sortDesc(getSortByField(QuerySearchSortBy.SORT_BY_VIEW));
}
builder.withExcludeFields(EXCLUDED_PROJECTION_FIELDS);
if (searchSpecification.limit != null && searchSpecification.limit <= 0)

// Check if pagination is requested (spec is a PaginatedQuerySearchSpecification with pageSize specified)
boolean isPaginated = searchSpecification instanceof PaginatedQuerySearchSpecification
&& ((PaginatedQuerySearchSpecification) searchSpecification).pageSize != null;

if (isPaginated)
{
throw new ApplicationQueryException("Limit should be greater than 0", Response.Status.BAD_REQUEST);
PaginatedQuerySearchSpecification paginatedSpec = (PaginatedQuerySearchSpecification) searchSpecification;
int pageSize = Math.min(paginatedSpec.pageSize, MAX_PAGE_SIZE);
int offset = paginatedSpec.cursor != null ? paginatedSpec.cursor : 0;
int pageNumber = (offset / pageSize) + 1;

builder.withSkip(offset);
// Fetch one extra to determine if there's a next page
builder.withLimit(pageSize + 1);

List<Query> queries = getQueryDao().find(filters.isEmpty() ? EMPTY_FILTER : Filters.and(filters), false, builder.build())
.map(this::convertFromStoredQuery)
.sorted(Comparator.comparing(query -> query.owner != null && query.owner.equals(currentUser) ? 0 : 1))
.collect(Collectors.toList());

boolean hasNextPage = queries.size() > pageSize;
if (hasNextPage)
{
queries = queries.subList(0, pageSize);
}

Integer cursor = hasNextPage ? offset + pageSize : null;

return new PaginatedResult(queries, hasNextPage, cursor, pageNumber);
}
builder.withLimit(Math.min(MAX_NUMBER_OF_QUERIES, searchSpecification.limit == null ? Integer.MAX_VALUE : searchSpecification.limit));
else
{
// Non-paginated: use limit parameter (backward compatible behavior)
if (searchSpecification.limit != null && searchSpecification.limit <= 0)
{
throw new ApplicationQueryException("Limit should be greater than 0", Response.Status.BAD_REQUEST);
}
builder.withLimit(Math.min(MAX_NUMBER_OF_QUERIES, searchSpecification.limit == null ? Integer.MAX_VALUE : searchSpecification.limit));

return getQueryDao().find(filters.isEmpty() ? EMPTY_FILTER : Filters.and(filters), false, builder.build())
.map(this::convertFromStoredQuery)
.sorted(Comparator.comparing(query -> query.owner != null && query.owner.equals(currentUser) ? 0 : 1))
.collect(Collectors.toList());
List<Query> queries = getQueryDao().find(filters.isEmpty() ? EMPTY_FILTER : Filters.and(filters), false, builder.build())
.map(this::convertFromStoredQuery)
.sorted(Comparator.comparing(query -> query.owner != null && query.owner.equals(currentUser) ? 0 : 1))
.collect(Collectors.toList());

return new PaginatedResult(queries, false, null, 1);
}
}

public String getSortByField(QuerySearchSortBy sortBy)
Expand Down Expand Up @@ -559,4 +603,5 @@ public QueryStoreStats getQueryStoreStats()
.countDocuments(dataSpaceFilter));
return storeStats;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class ApplicationStoredQuery implements StoredVersionedAssetContent<Strin
public List<StereotypePtr> stereotypes;
public List<QueryParameterValue> defaultParameterValues;
public Map<String, ?> gridConfig;
public List<AttributeMetadata> attributeMetadata;
public Long lastOpenAt;
public StoredAuditInformation audit;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2026 Goldman Sachs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.finos.legend.engine.application.query.model;

public class AttributeMetadata
{
public String path;
public String alias;
public String description;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2026 Goldman Sachs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.finos.legend.engine.application.query.model;

public class PaginatedQuerySearchSpecification extends QuerySearchSpecification
{
public Integer pageSize;
public Integer cursor;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2026 Goldman Sachs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.finos.legend.engine.application.query.model;

import java.util.List;

public class PaginatedResult
{
public List<Query> queries;
public boolean hasNextPage;
public Integer cursor;
public int pageNumber;

public PaginatedResult(List<Query> queries, boolean hasNextPage, Integer cursor, int pageNumber)
{
this.queries = queries;
this.hasNextPage = hasNextPage;
this.cursor = cursor;
this.pageNumber = pageNumber;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@ public class Query
public String owner;

public Map<String, ?> gridConfig;
public List<AttributeMetadata> attributeMetadata;

}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public static ApplicationStoredQuery toStoredQuery(Query query)
stored.stereotypes = query.stereotypes;
stored.defaultParameterValues = query.defaultParameterValues;
stored.gridConfig = query.gridConfig;
stored.attributeMetadata = query.attributeMetadata;
stored.lastOpenAt = Instant.now().toEpochMilli();

// Initialize audit with info from query
Expand Down
Loading