[ACS-11115] Improve GetChildrenCannedQuery implementation backport 25.N (#3922)

Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>
This commit is contained in:
Cezary Witkowski
2026-03-23 12:42:03 +01:00
committed by GitHub
parent 66d18daec5
commit ed08e02b8e
8 changed files with 562 additions and 167 deletions
@@ -36,8 +36,8 @@ import org.alfresco.util.ParameterCheck;
*/
public abstract class AbstractCannedQuery<R> implements CannedQuery<R>
{
private final CannedQueryParameters parameters;
private final String queryExecutionId;
protected final CannedQueryParameters parameters;
protected final String queryExecutionId;
private CannedQueryResults<R> results;
/**
@@ -99,9 +99,6 @@ public abstract class AbstractCannedQuery<R> implements CannedQuery<R>
rawResults = applyPostQueryPermissions(rawResults, requestedCount);
}
// Get total count
final Pair<Integer, Integer> totalCount = getTotalResultCount(rawResults);
// Apply paging
CannedQueryPageDetails pagingDetails = parameters.getPageDetails();
List<List<R>> pages = Collections.singletonList(rawResults);
@@ -110,88 +107,7 @@ public abstract class AbstractCannedQuery<R> implements CannedQuery<R>
pages = applyPostQueryPaging(rawResults, pagingDetails);
}
// Construct results object
final List<List<R>> finalPages = pages;
// Has more items beyond requested pages ? ... ie. at least one more page (with at least one result)
final boolean hasMoreItems = (rawResults.size() > pagingDetails.getResultsRequiredForPaging()) || (totalCount.getFirst() > pagingDetails.getResultsRequiredForPaging());
results = new CannedQueryResults<R>() {
@Override
public CannedQuery<R> getOriginatingQuery()
{
return AbstractCannedQuery.this;
}
@Override
public String getQueryExecutionId()
{
return queryExecutionId;
}
@Override
public Pair<Integer, Integer> getTotalResultCount()
{
if (parameters.getTotalResultCountMax() > 0)
{
return totalCount;
}
else
{
throw new IllegalStateException("Total results were not requested in parameters.");
}
}
@Override
public int getPagedResultCount()
{
int finalPagedCount = 0;
for (List<R> page : finalPages)
{
finalPagedCount += page.size();
}
return finalPagedCount;
}
@Override
public int getPageCount()
{
return finalPages.size();
}
@Override
public R getSingleResult()
{
if (finalPages.size() != 1 && finalPages.get(0).size() != 1)
{
throw new IllegalStateException("There must be exactly one page of one result available.");
}
return finalPages.get(0).get(0);
}
@Override
public List<R> getPage()
{
if (finalPages.size() != 1)
{
throw new IllegalStateException("There must be exactly one page of results available.");
}
return finalPages.get(0);
}
@Override
public List<List<R>> getPages()
{
return finalPages;
}
@Override
public boolean hasMoreItems()
{
return hasMoreItems;
}
};
return results;
return createCannedQueryResults(pages, rawResults);
}
/**
@@ -337,4 +253,90 @@ public abstract class AbstractCannedQuery<R> implements CannedQuery<R>
// Done
return pages;
}
protected CannedQueryResults<R> createCannedQueryResults(List<List<R>> finalPages, List<R> rawResults)
{
CannedQueryPageDetails pagingDetails = parameters.getPageDetails();
// Get total count
final Pair<Integer, Integer> totalCount = getTotalResultCount(rawResults);
// Has more items beyond requested pages ? ... ie. at least one more page (with at least one result)
final boolean hasMoreItems = (rawResults.size() > pagingDetails.getResultsRequiredForPaging()) || (totalCount.getFirst() > pagingDetails.getResultsRequiredForPaging());
results = new CannedQueryResults<>() {
@Override
public CannedQuery<R> getOriginatingQuery()
{
return AbstractCannedQuery.this;
}
@Override
public String getQueryExecutionId()
{
return queryExecutionId;
}
@Override
public Pair<Integer, Integer> getTotalResultCount()
{
if (parameters.getTotalResultCountMax() > 0)
{
return totalCount;
}
else
{
throw new IllegalStateException("Total results were not requested in parameters.");
}
}
@Override
public int getPagedResultCount()
{
int finalPagedCount = 0;
for (List<R> page : finalPages)
{
finalPagedCount += page.size();
}
return finalPagedCount;
}
@Override
public int getPageCount()
{
return finalPages.size();
}
@Override
public R getSingleResult()
{
if (finalPages.size() != 1 && finalPages.get(0).size() != 1)
{
throw new IllegalStateException("There must be exactly one page of one result available.");
}
return finalPages.get(0).get(0);
}
@Override
public List<R> getPage()
{
if (finalPages.size() != 1)
{
throw new IllegalStateException("There must be exactly one page of results available.");
}
return finalPages.get(0);
}
@Override
public List<List<R>> getPages()
{
return finalPages;
}
@Override
public boolean hasMoreItems()
{
return hasMoreItems;
}
};
return results;
}
}
@@ -1897,6 +1897,8 @@ public class NodesImpl implements Nodes
* <p>
* Returns the default sort order.
* </p>
*
* Before any changes please keep in mind that there is at least one implementation depending on default sorting. See GetChildrenCannedQueryFactory.isDefaultSorting() method.
*
* @return The list of <code>Pair&lt;QName, Boolean&gt;</code> sort properties.
*/
@@ -0,0 +1,236 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2026 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.model.filefolder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.alfresco.model.ContentModel;
import org.alfresco.query.CannedQuery;
import org.alfresco.query.CannedQueryPageDetails;
import org.alfresco.query.CannedQueryParameters;
import org.alfresco.query.CannedQueryResults;
import org.alfresco.query.CannedQuerySortDetails;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.node.NodePropertyHelper;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.domain.query.CannedQueryDAO;
import org.alfresco.repo.node.getchildren.FilterProp;
import org.alfresco.repo.node.getchildren.FilterSortNodeEntity;
import org.alfresco.repo.node.getchildren.GetChildrenCannedQueryParams;
import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
/**
* GetChildren canned query that performs sorting in the database.
* <p>
* This query handles the specific case of default sorting (folders first descending, name ascending).
*/
public class DbSortingGetChildrenCannedQuery extends GetChildrenCannedQuery
{
private static final Log LOG = LogFactory.getLog(DbSortingGetChildrenCannedQuery.class);
private static final String QUERY_COUNT_GET_CHILDREN_WITH_PROPS_SORTED = "count_GetChildrenCannedQueryWithPropsSorted";
private static final String QUERY_SELECT_GET_CHILDREN_WITH_PROPS_SORTED = "select_GetChildrenCannedQueryWithPropsSorted";
private final DictionaryService dictionaryService;
private int totalCount;
public DbSortingGetChildrenCannedQuery(NodeDAO nodeDAO, QNameDAO qnameDAO, CannedQueryDAO cannedQueryDAO, NodePropertyHelper nodePropertyHelper, TenantService tenantService, NodeService nodeService, MethodSecurityBean<NodeRef> methodSecurity, CannedQueryParameters params, HiddenAspect hiddenAspect, DictionaryService dictionaryService, Set<QName> ignoreAspectQNames)
{
super(nodeDAO, qnameDAO, cannedQueryDAO, nodePropertyHelper, tenantService, nodeService, methodSecurity, params, hiddenAspect, dictionaryService, ignoreAspectQNames);
this.dictionaryService = dictionaryService;
}
@Override
protected List<NodeRef> executeQuery(List<FilterProp> filterProps, List<Pair<QName, CannedQuerySortDetails.SortOrder>> sortPairs, FilterSortNodeEntity params, GetChildrenCannedQueryParams paramBean, int filterSortPropCnt)
{
addFolderTypes(params);
LOG.trace("Executing DB sorting get children canned query for " + params.getParentNodeId() + " with default sorting");
fetchTotalCount(params);
List<FilterSortNode> children = fetchPagedChildren(params);
return toNodeRefs(children);
}
private void addFolderTypes(FilterSortNodeEntity params)
{
Set<QName> folderQNames = new HashSet<>(50);
folderQNames.addAll(dictionaryService.getSubTypes(ContentModel.TYPE_FOLDER, true));
folderQNames.add(ContentModel.TYPE_FOLDER);
Set<Long> folderTypeQNameIds = qnameDAO.convertQNamesToIds(folderQNames, false);
params.setFolderTypeQNameIds(folderTypeQNameIds);
}
private void fetchTotalCount(FilterSortNodeEntity params)
{
totalCount = cannedQueryDAO.executeCountQuery(QUERY_NAMESPACE, QUERY_COUNT_GET_CHILDREN_WITH_PROPS_SORTED, params).intValue();
LOG.trace("Total children count for " + params.getParentNodeId() + ": " + totalCount);
}
private List<FilterSortNode> fetchPagedChildren(FilterSortNodeEntity params)
{
CannedQueryPageDetails pageDetails = parameters.getPageDetails();
final List<FilterSortNode> children = new ArrayList<>(100);
int requestedCount = pageDetails.getPageSize();
final PagedFilterSortChildQueryCallback callback = new PagedFilterSortChildQueryCallback(children, requestedCount);
FilterSortResultHandler resultHandler = new FilterSortResultHandler(callback);
int skipResults = pageDetails.getSkipResults();
cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_CHILDREN_WITH_PROPS_SORTED, params, skipResults, Integer.MAX_VALUE, resultHandler);
resultHandler.done();
LOG.trace(children.size() + " children found for " + params.getParentNodeId() + " total count: " + totalCount);
return children;
}
private List<NodeRef> toNodeRefs(List<FilterSortNode> children)
{
List<NodeRef> result = new ArrayList<>(children.size());
for (FilterSortNode child : children)
{
result.add(tenantService.getBaseName(child.getNodeRef()));
}
return result;
}
@Override
protected boolean isApplyPostQueryPermissions()
{
return true;
}
@Override
protected boolean isApplyPostQueryPaging()
{
return false;
}
@Override
protected CannedQueryResults<NodeRef> createCannedQueryResults(List<List<NodeRef>> finalPages, List<NodeRef> rawResults)
{
return new CannedQueryResults<>() {
@Override
public CannedQuery<NodeRef> getOriginatingQuery()
{
return DbSortingGetChildrenCannedQuery.this;
}
@Override
public String getQueryExecutionId()
{
return queryExecutionId;
}
@Override
public Pair<Integer, Integer> getTotalResultCount()
{
if (parameters.getTotalResultCountMax() > 0)
{
return new Pair<>(totalCount, totalCount);
}
else
{
throw new IllegalStateException("Total results were not requested in parameters.");
}
}
@Override
public int getPagedResultCount()
{
return rawResults.size();
}
@Override
public int getPageCount()
{
return 1;
}
@Override
public NodeRef getSingleResult()
{
if (rawResults.size() != 1)
{
throw new IllegalStateException("There must be exactly one page of one result available.");
}
return rawResults.get(0);
}
@Override
public List<NodeRef> getPage()
{
return rawResults;
}
@Override
public List<List<NodeRef>> getPages()
{
return finalPages;
}
@Override
public boolean hasMoreItems()
{
CannedQueryPageDetails pageDetails = parameters.getPageDetails();
return totalCount > pageDetails.getSkipResults() + pageDetails.getPageSize();
}
};
}
protected class PagedFilterSortChildQueryCallback implements FilterSortChildQueryCallback
{
private final List<FilterSortNode> children;
private final int requiredCount;
public PagedFilterSortChildQueryCallback(List<FilterSortNode> children, int requiredCount)
{
this.children = children;
this.requiredCount = requiredCount;
}
@Override
public void handle(FilterSortNode node)
{
if (needsMore() && includeImpl(true, node.getNodeRef()))
{
children.add(node);
}
}
@Override
public int remainingNeeded()
{
return requiredCount - children.size();
}
}
}
@@ -124,7 +124,7 @@ public class GetChildrenCannedQuery extends org.alfresco.repo.node.getchildren.G
}
@Override
public boolean handle(FilterSortNode node)
public void handle(FilterSortNode node)
{
super.handle(node);
@@ -143,8 +143,6 @@ public class GetChildrenCannedQuery extends org.alfresco.repo.node.getchildren.G
propVals.put(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER, isFolder);
}
return true;
}
}
@@ -25,11 +25,16 @@
*/
package org.alfresco.repo.model.filefolder;
import static org.alfresco.repo.node.getchildren.GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER;
import java.util.List;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.query.CannedQuery;
import org.alfresco.query.CannedQueryParameters;
import org.alfresco.query.CannedQuerySortDetails;
import org.alfresco.query.CannedQuerySortDetails.SortOrder;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.domain.node.NodePropertyHelper;
import org.alfresco.repo.node.getchildren.FilterProp;
@@ -66,6 +71,22 @@ public class GetChildrenCannedQueryFactory extends org.alfresco.repo.node.getchi
{
NodePropertyHelper nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO);
return (CannedQuery<NodeRef>) new GetChildrenCannedQuery(nodeDAO, qnameDAO, cannedQueryDAO, nodePropertyHelper, tenantService, nodeService, methodSecurity, parameters, hiddenAspect, dictionaryService, ignoreAspectQNames);
if (isDefaultSorting(parameters))
{
return new DbSortingGetChildrenCannedQuery(nodeDAO, qnameDAO, cannedQueryDAO, nodePropertyHelper, tenantService, nodeService, methodSecurity, parameters, hiddenAspect, dictionaryService, ignoreAspectQNames);
}
return new GetChildrenCannedQuery(nodeDAO, qnameDAO, cannedQueryDAO, nodePropertyHelper, tenantService, nodeService, methodSecurity, parameters, hiddenAspect, dictionaryService, ignoreAspectQNames);
}
/**
* See: NodesImpl.getListChildrenSortPropsDefault()
*/
private boolean isDefaultSorting(CannedQueryParameters parameters)
{
CannedQuerySortDetails sortDetails = parameters.getSortDetails();
var sortPairs = sortDetails.getSortPairs();
return sortPairs.size() == 2
&& sortPairs.get(0).equals(new Pair<>(SORT_QNAME_NODE_IS_FOLDER, SortOrder.DESCENDING))
&& sortPairs.get(1).equals(new Pair<>(ContentModel.PROP_NAME, SortOrder.ASCENDING));
}
}
@@ -69,6 +69,8 @@ public class FilterSortNodeEntity
private Boolean isPrimary;
private Set<Long> folderTypeQNameIds;
/**
* Default constructor
*/
@@ -305,6 +307,16 @@ public class FilterSortNodeEntity
this.isPrimary = isPrimary;
}
public Set<Long> getFolderTypeQNameIds()
{
return folderTypeQNameIds;
}
public void setFolderTypeQNameIds(Set<Long> folderTypeQNameIds)
{
this.folderTypeQNameIds = folderTypeQNameIds;
}
public NodeRef createNodeRef()
{
return new NodeRef(new StoreRef(storeProtocol, storeIdentifier), nodeUuid);
@@ -85,9 +85,9 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
{
private Log logger = LogFactory.getLog(getClass());
private static final String QUERY_NAMESPACE = "alfresco.node";
private static final String QUERY_SELECT_GET_CHILDREN_WITH_PROPS = "select_GetChildrenCannedQueryWithProps";
private static final String QUERY_SELECT_GET_CHILDREN_WITHOUT_PROPS = "select_GetChildrenCannedQueryWithoutProps";
protected static final String QUERY_NAMESPACE = "alfresco.node";
protected static final String QUERY_SELECT_GET_CHILDREN_WITH_PROPS = "select_GetChildrenCannedQueryWithProps";
protected static final String QUERY_SELECT_GET_CHILDREN_WITHOUT_PROPS = "select_GetChildrenCannedQueryWithoutProps";
public static final int MAX_FILTER_SORT_PROPS = 3;
@@ -99,11 +99,11 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
public static final QName FILTER_QNAME_NODE_IS_PRIMARY = QName.createQName("", "IS_PRIMARY");
private NodeDAO nodeDAO;
private QNameDAO qnameDAO;
private CannedQueryDAO cannedQueryDAO;
private NodePropertyHelper nodePropertyHelper;
private TenantService tenantService;
protected NodeDAO nodeDAO;
protected QNameDAO qnameDAO;
protected CannedQueryDAO cannedQueryDAO;
protected NodePropertyHelper nodePropertyHelper;
protected TenantService tenantService;
protected NodeService nodeService;
private boolean applyPostQueryPermissions = false; // if true, the permissions will be applied post-query (else should be applied as part of the "queryAndFilter")
@@ -244,7 +244,7 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
filterSortPropCnt = setFilterSortParams(sortFilterProps, params);
List<NodeRef> result = new ArrayList<>(0);
List<NodeRef> result = new ArrayList<>();
try
{
@@ -291,49 +291,7 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
params.setPattern(pattern);
}
if (filterSortPropCnt > 0)
{
// filtered and/or sorted - note: permissions will be applied post query
final List<FilterSortNode> children = new ArrayList<FilterSortNode>(100);
final FilterSortChildQueryCallback c = getFilterSortChildQuery(children, filterProps, paramBean);
FilterSortResultHandler resultHandler = new FilterSortResultHandler(c);
cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_CHILDREN_WITH_PROPS, params, 0, Integer.MAX_VALUE, resultHandler);
resultHandler.done();
if (sortPairs.size() > 0)
{
Long startSort = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
// sort
Collections.sort(children, new PropComparatorAsc(sortPairs));
if (startSort != null)
{
logger.debug("Post-query sort: " + children.size() + " in " + (System.currentTimeMillis() - startSort) + " msecs");
}
}
result = new ArrayList<NodeRef>(children.size());
for (FilterSortNode child : children)
{
result.add(tenantService.getBaseName(child.getNodeRef()));
}
}
else
{
// unsorted (apart from any implicit order) - note: permissions are applied during result handling to allow early cutoff
final int requestedCount = parameters.getResultsRequired();
final List<NodeRef> rawResult = new ArrayList<NodeRef>(Math.min(1000, requestedCount));
UnsortedChildQueryCallback callback = getUnsortedChildQueryCallback(rawResult, requestedCount, paramBean);
UnsortedResultHandler resultHandler = new UnsortedResultHandler(callback);
cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_CHILDREN_WITHOUT_PROPS, params, 0, Integer.MAX_VALUE, resultHandler);
resultHandler.done();
// permissions have been applied
result = PermissionCheckedValueMixin.create(rawResult);
}
result = executeQuery(filterProps, sortPairs, params, paramBean, filterSortPropCnt);
}
finally
{
@@ -342,10 +300,57 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
logger.debug("Base query " + (filterSortPropCnt > 0 ? "(sort=y, perms=n)" : "(sort=n, perms=y)") + ": " + result.size() + " in " + (System.currentTimeMillis() - start) + " msecs");
}
}
return result;
}
protected List<NodeRef> executeQuery(List<FilterProp> filterProps, List<Pair<QName, SortOrder>> sortPairs, FilterSortNodeEntity params, GetChildrenCannedQueryParams paramBean, int filterSortPropCnt)
{
if (filterSortPropCnt > 0)
{
// filtered and/or sorted - note: permissions will be applied post query
final List<FilterSortNode> children = new ArrayList<>(100);
final FilterSortChildQueryCallback c = getFilterSortChildQuery(children, filterProps, paramBean);
FilterSortResultHandler resultHandler = new FilterSortResultHandler(c);
cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_CHILDREN_WITH_PROPS, params, 0, Integer.MAX_VALUE, resultHandler);
resultHandler.done();
if (sortPairs.size() > 0)
{
Long startSort = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
// sort
Collections.sort(children, new PropComparatorAsc(sortPairs));
if (startSort != null)
{
logger.debug("Post-query sort: " + children.size() + " in " + (System.currentTimeMillis() - startSort) + " msecs");
}
}
List<NodeRef> result = new ArrayList<>(children.size());
for (FilterSortNode child : children)
{
result.add(tenantService.getBaseName(child.getNodeRef()));
}
return result;
}
else
{
// unsorted (apart from any implicit order) - note: permissions are applied during result handling to allow early cutoff
final int requestedCount = parameters.getResultsRequired();
final List<NodeRef> rawResult = new ArrayList<>(Math.min(1000, requestedCount));
UnsortedChildQueryCallback callback = getUnsortedChildQueryCallback(rawResult, requestedCount, paramBean);
UnsortedResultHandler resultHandler = new UnsortedResultHandler(callback);
cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_CHILDREN_WITHOUT_PROPS, params, 0, Integer.MAX_VALUE, resultHandler);
resultHandler.done();
// permissions have been applied
return PermissionCheckedValueMixin.create(rawResult);
}
}
// Set filter/sort props (between 0 and 3)
private int setFilterSortParams(List<QName> filterSortProps, FilterSortNodeEntity params)
{
@@ -512,7 +517,7 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
}
}
private boolean includeAspects(NodeRef nodeRef, Set<QName> inclusiveAspects, Set<QName> exclusiveAspects)
protected boolean includeAspects(NodeRef nodeRef, Set<QName> inclusiveAspects, Set<QName> exclusiveAspects)
{
if (inclusiveAspects == null && exclusiveAspects == null)
{
@@ -666,7 +671,22 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
protected interface FilterSortChildQueryCallback
{
boolean handle(FilterSortNode node);
void handle(FilterSortNode node);
default int remainingNeeded()
{
return Integer.MAX_VALUE;
}
default boolean needsMore()
{
return remainingNeeded() > 0;
}
default boolean doesntNeedMore()
{
return !needsMore();
}
}
protected class DefaultFilterSortChildQueryCallback implements FilterSortChildQueryCallback
@@ -692,15 +712,12 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
}
@Override
public boolean handle(FilterSortNode node)
public void handle(FilterSortNode node)
{
if (include(node))
{
children.add(node);
}
// More results
return true;
}
protected boolean include(FilterSortNode node)
@@ -751,41 +768,43 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
protected class FilterSortResultHandler implements CannedQueryDAO.ResultHandler<FilterSortNodeEntity>
{
private final FilterSortChildQueryCallback resultsCallback;
private boolean more = true;
private static final int BATCH_SIZE = 256 * 4;
private final List<FilterSortNodeEntity> results;
private final List<FilterSortNodeEntity> currentBatch;
private FilterSortResultHandler(FilterSortChildQueryCallback resultsCallback)
public FilterSortResultHandler(FilterSortChildQueryCallback resultsCallback)
{
this.resultsCallback = resultsCallback;
results = new LinkedList<FilterSortNodeEntity>();
currentBatch = new ArrayList<>(BATCH_SIZE);
}
@Override
public boolean handleResult(FilterSortNodeEntity result)
{
// Do nothing if no further results are required
if (!more)
if (resultsCallback.doesntNeedMore())
{
return false;
}
if (results.size() >= BATCH_SIZE)
currentBatch.add(result);
int remainingNeeded = resultsCallback.remainingNeeded();
int currentBatchSize = currentBatch.size();
if (currentBatchSize >= BATCH_SIZE || currentBatchSize >= remainingNeeded)
{
// batch
preloadNodes();
filterSort();
}
results.add(result);
return more;
return resultsCallback.needsMore();
}
public void done()
{
if (results.size() >= 0)
if (currentBatch.size() >= 0)
{
// finish batch
preloadNodes();
@@ -795,8 +814,8 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
private void preloadNodes()
{
List<NodeRef> nodeRefs = new ArrayList<>(results.size());
for (FilterSortNodeEntity result : results)
List<NodeRef> nodeRefs = new ArrayList<>(currentBatch.size());
for (FilterSortNodeEntity result : currentBatch)
{
nodeRefs.add(result.createNodeRef());
}
@@ -806,7 +825,7 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
private void filterSort()
{
for (FilterSortNodeEntity result : results)
for (FilterSortNodeEntity result : currentBatch)
{
NodeRef nodeRef = result.createNodeRef();
@@ -876,15 +895,14 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions<NodeR
}
// Call back
boolean more = resultsCallback.handle(new FilterSortNode(nodeRef, propVals));
if (!more)
resultsCallback.handle(new FilterSortNode(nodeRef, propVals));
if (resultsCallback.doesntNeedMore())
{
this.more = false;
break;
}
}
results.clear();
currentBatch.clear();
}
}
@@ -989,6 +989,112 @@
and assoc.is_primary = #{isPrimary}
</select>
<!-- GetChildren - count of matching rows with same conditions as select_GetChildrenCannedQueryWithPropsSorted -->
<select id="count_GetChildrenCannedQueryWithPropsSorted" parameterType="FilterSortNode" resultType="long">
select count(distinct childNode.id)
from
alf_child_assoc assoc
join alf_node childNode on (childNode.id = assoc.child_node_id)
<if test="pattern != null">
join alf_node_properties prop4 on (prop4.node_id = childNode.id)
join alf_qname qname on (prop4.qname_id = qname.id and qname.id = #{namePropertyQNameId})
</if>
where
assoc.parent_node_id = #{parentNodeId}
<if test="isPrimary != null">
and assoc.is_primary = #{isPrimary}
</if>
<if test="childNodeTypeQNameIds != null">
and childNode.type_qname_id in
<foreach item="item" index="index" collection="childNodeTypeQNameIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="pattern != null">
and prop4.string_value like #{pattern} <include refid="alfresco.util.escape"/>
</if>
<if test="assocTypeQNameIds != null">
and assoc.type_qname_id in
<foreach item="item" index="index" collection="assocTypeQNameIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</select>
<!-- GetChildren - with explicit prop filtering and DEFAULT sorting -->
<select id="select_GetChildrenCannedQueryWithPropsSorted" parameterType="FilterSortNode" resultMap="result_FilterSortNode">
select distinct
childNode.id as id,
childNode.version as version,
childStore.id as store_id,
childStore.protocol as protocol,
childStore.identifier as identifier,
childNode.uuid as uuid,
childNode.type_qname_id as type_qname_id,
childNode.locale_id as locale_id,
childNode.acl_id as acl_id,
childTxn.id as txn_id,
childTxn.change_txn_id as txn_change_id,
childNode.audit_creator as audit_creator,
childNode.audit_created as audit_created,
childNode.audit_modifier as audit_modifier,
childNode.audit_modified as audit_modified,
childNode.audit_accessed as audit_accessed,
prop1.node_id as prop1_node_id,
prop1.qname_id as prop1_qname_id,
prop1.locale_id as prop1_locale_id,
prop1.list_index as prop1_list_index,
prop1.actual_type_n as prop1_actual_type_n,
prop1.persisted_type_n as prop1_persisted_type_n,
prop1.string_value as prop1_string_value,
CASE
WHEN childNode.type_qname_id IN
<foreach item="id" collection="folderTypeQNameIds" open="(" separator="," close=")">
#{id}
</foreach>
THEN 0
ELSE 1
END as sort_folder_first,
CASE
WHEN prop1.string_value IS NULL THEN 0
ELSE 1
END as sort_null_prop1_first
from
alf_child_assoc assoc
join alf_node childNode on (childNode.id = assoc.child_node_id)
left join alf_store childStore on (childStore.id = childNode.store_id)
join alf_transaction childTxn on (childTxn.id = childNode.transaction_id)
left join alf_node_properties prop1 on (prop1.node_id = childNode.id and prop1.qname_id = #{prop1qnameId})
<if test="pattern != null">
join alf_node_properties prop4 on (prop4.node_id = childNode.id)
join alf_qname qname on (prop4.qname_id = qname.id and qname.id = #{namePropertyQNameId})
</if>
where
assoc.parent_node_id = #{parentNodeId}
<if test="isPrimary != null">
and assoc.is_primary = #{isPrimary}
</if>
<if test="childNodeTypeQNameIds != null">
and childNode.type_qname_id in
<foreach item="item" index="index" collection="childNodeTypeQNameIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="pattern != null">
and prop4.string_value like #{pattern} <include refid="alfresco.util.escape"/>
</if>
<if test="assocTypeQNameIds != null">
and assoc.type_qname_id in
<foreach item="item" index="index" collection="assocTypeQNameIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
ORDER BY
sort_folder_first,
sort_null_prop1_first,
prop1.string_value ASC
</select>
<!-- GetChildren - with explicit prop filtering and/or sorting -->
<select id="select_GetChildrenCannedQueryWithProps" parameterType="FilterSortNode" resultMap="result_FilterSortNode" flushCache="true">
select distinct