mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2026-09-16 18:13:17 +00:00
[ACS-11115] Improve GetChildrenCannedQuery implementation backport 25.N (#3922)
Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>
This commit is contained in:
@@ -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<QName, Boolean></code> sort properties.
|
||||
*/
|
||||
|
||||
+236
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-1
@@ -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);
|
||||
|
||||
+94
-76
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+106
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user