/** * Does a 'breadth first' search of ancestors, caching as it goes * * @param nodeIds initial list of nodes to visit * @return all visited nodes, in no particular order */ private List<Long> cacheAncestors(List<Long> nodeIds) { final LinkedList<Long> toVisit = new LinkedList<Long>(nodeIds); Set<Long> visited = new TreeSet<Long>(); Long nodeId; nodeDAO.cacheNodesById(toVisit); Long lastCached = toVisit.peekLast(); while ((nodeId = toVisit.pollFirst()) != null) { if (visited.add(nodeId) && (nodeDAO.getNodeIdStatus(nodeId) != null) && (false == nodeDAO.getNodeIdStatus(nodeId).isDeleted())) { nodeDAO.getParentAssocs( nodeId, null, null, null, new ChildAssocRefQueryCallback() { @Override public boolean preLoadNodes() { return false; } @Override public boolean orderResults() { return false; } @Override public boolean handle( Pair<Long, ChildAssociationRef> childAssocPair, Pair<Long, NodeRef> parentNodePair, Pair<Long, NodeRef> childNodePair) { toVisit.add(parentNodePair.getFirst()); return true; } @Override public void done() {} }); } final boolean nodeIdEqualsLastCached = (nodeId == null && lastCached == null) || (nodeId != null && nodeId.equals(lastCached)); if (nodeIdEqualsLastCached && !toVisit.isEmpty()) { nodeDAO.cacheNodesById(toVisit); lastCached = toVisit.peekLast(); } } return new ArrayList<Long>(visited); }
/** {@inheritDoc} */ public void getNodesMetadata( NodeMetaDataParameters nodeMetaDataParameters, MetaDataResultsFilter resultFilter, NodeMetaDataQueryCallback callback) { if (false == enabled) { return; } NodeMetaDataQueryRowHandler rowHandler = new NodeMetaDataQueryRowHandler(callback); boolean includeType = (resultFilter == null ? true : resultFilter.getIncludeType()); boolean includeProperties = (resultFilter == null ? true : resultFilter.getIncludeProperties()); boolean includeAspects = (resultFilter == null ? true : resultFilter.getIncludeAspects()); boolean includePaths = (resultFilter == null ? true : resultFilter.getIncludePaths()); boolean includeNodeRef = (resultFilter == null ? true : resultFilter.getIncludeNodeRef()); boolean includeParentAssociations = (resultFilter == null ? true : resultFilter.getIncludeParentAssociations()); boolean includeChildAssociations = (resultFilter == null ? true : resultFilter.getIncludeChildAssociations()); boolean includeOwner = (resultFilter == null ? true : resultFilter.getIncludeOwner()); boolean includeChildIds = (resultFilter == null ? true : resultFilter.getIncludeChildIds()); boolean includeTxnId = (resultFilter == null ? true : resultFilter.getIncludeTxnId()); List<Long> nodeIds = preCacheNodes(nodeMetaDataParameters); for (Long nodeId : nodeIds) { Status status = nodeDAO.getNodeIdStatus(nodeId); if (status == null) { // We've been called with the ID of a purged node, probably due to processing a transaction // with a // cascading delete. Fine to skip and assume it will be processed in a transaction. // See org.alfresco.solr.tracker.CoreTracker.updateDescendantAuxDocs(NodeMetaData, boolean, // SolrIndexSearcher) continue; } NodeRef nodeRef = status.getNodeRef(); NodeMetaData nodeMetaData = new NodeMetaData(); nodeMetaData.setNodeId(nodeId); if (includeNodeRef) { nodeMetaData.setNodeRef(tenantService.getBaseName(nodeRef, true)); } if (includeTxnId) { nodeMetaData.setTxnId(status.getDbTxnId()); } if (status.isDeleted()) { rowHandler.processResult(nodeMetaData); continue; } Map<QName, Serializable> props = null; Set<QName> aspects = null; nodeMetaData.setAclId(nodeDAO.getNodeAclId(nodeId)); if (includeType) { QName nodeType = getNodeType(nodeId); if (nodeType != null) { nodeMetaData.setNodeType(nodeType); } else { throw new AlfrescoRuntimeException("Nodes with no type are ignored by SOLR"); } } if (includeProperties) { if (props == null) { props = getProperties(nodeId); } nodeMetaData.setProperties(props); } else { nodeMetaData.setProperties(Collections.<QName, Serializable>emptyMap()); } if (includeAspects || includePaths || includeParentAssociations) { aspects = getNodeAspects(nodeId); } nodeMetaData.setAspects(aspects); boolean ignoreLargeMetadata = (typeIndexFilter.shouldBeIgnored(getNodeType(nodeId)) || aspectIndexFilter.shouldBeIgnored(getNodeAspects(nodeId))); if (!ignoreLargeMetadata && (typeIndexFilter.isIgnorePathsForSpecificTypes() || aspectIndexFilter.isIgnorePathsForSpecificAspects())) { // check if parent should be ignored final List<Long> parentIds = new LinkedList<Long>(); nodeDAO.getParentAssocs( nodeId, null, null, true, new ChildAssocRefQueryCallback() { @Override public boolean preLoadNodes() { return false; } @Override public boolean orderResults() { return false; } @Override public boolean handle( Pair<Long, ChildAssociationRef> childAssocPair, Pair<Long, NodeRef> parentNodePair, Pair<Long, NodeRef> childNodePair) { parentIds.add(parentNodePair.getFirst()); return false; } @Override public void done() {} }); if (!parentIds.isEmpty()) { Long parentId = parentIds.iterator().next(); if (typeIndexFilter.isIgnorePathsForSpecificTypes()) { QName parentType = getNodeType(parentId); ignoreLargeMetadata = typeIndexFilter.shouldBeIgnored(parentType); } if (!ignoreLargeMetadata && aspectIndexFilter.isIgnorePathsForSpecificAspects()) { ignoreLargeMetadata = aspectIndexFilter.shouldBeIgnored(getNodeAspects(parentId)); } } } CategoryPaths categoryPaths = new CategoryPaths( new ArrayList<Pair<Path, QName>>(), new ArrayList<ChildAssociationRef>()); if (!ignoreLargeMetadata && (includePaths || includeParentAssociations)) { if (props == null) { props = getProperties(nodeId); } categoryPaths = getCategoryPaths(status.getNodeRef(), aspects, props); } if (includePaths && !ignoreLargeMetadata) { if (props == null) { props = getProperties(nodeId); } List<Path> directPaths = nodeDAO.getPaths(new Pair<Long, NodeRef>(nodeId, status.getNodeRef()), false); Collection<Pair<Path, QName>> paths = new ArrayList<Pair<Path, QName>>(directPaths.size() + categoryPaths.getPaths().size()); for (Path path : directPaths) { paths.add(new Pair<Path, QName>(path.getBaseNamePath(tenantService), null)); } for (Pair<Path, QName> catPair : categoryPaths.getPaths()) { paths.add( new Pair<Path, QName>( catPair.getFirst().getBaseNamePath(tenantService), catPair.getSecond())); } nodeMetaData.setPaths(paths); // Calculate name path Collection<Collection<String>> namePaths = new ArrayList<Collection<String>>(2); nodeMetaData.setNamePaths(namePaths); for (Pair<Path, QName> catPair : paths) { Path path = catPair.getFirst(); boolean added = false; List<String> namePath = new ArrayList<String>(path.size()); NEXT_ELEMENT: for (Path.Element pathElement : path) { if (!(pathElement instanceof ChildAssocElement)) { // This is some path element that is terminal to a cm:name path break; } ChildAssocElement pathChildAssocElement = (ChildAssocElement) pathElement; NodeRef childNodeRef = pathChildAssocElement.getRef().getChildRef(); Pair<Long, NodeRef> childNodePair = nodeDAO.getNodePair(childNodeRef); if (childNodePair == null) { // Gone break; } Long childNodeId = childNodePair.getFirst(); String childNodeName = (String) nodeDAO.getNodeProperty(childNodeId, ContentModel.PROP_NAME); if (childNodeName == null) { // We have hit a non-name node, which acts as a root for cm:name // DH: There is no particular constraint here. This is just a decision made. namePath.clear(); // We have to continue down the path as there could be a name path lower down continue NEXT_ELEMENT; } // We can finally add the name to the path namePath.add(childNodeName); // Add the path if this is the first entry in the name path if (!added) { namePaths.add(namePath); added = true; } } } } nodeMetaData.setTenantDomain(tenantService.getDomain(nodeRef.getStoreRef().getIdentifier())); if (includeChildAssociations) { final List<ChildAssociationRef> childAssocs = new ArrayList<ChildAssociationRef>(100); nodeDAO.getChildAssocs( nodeId, null, null, null, null, null, new ChildAssocRefQueryCallback() { @Override public boolean preLoadNodes() { return false; } @Override public boolean orderResults() { return false; } @Override public boolean handle( Pair<Long, ChildAssociationRef> childAssocPair, Pair<Long, NodeRef> parentNodePair, Pair<Long, NodeRef> childNodePair) { boolean addCurrentChildAssoc = true; if (typeIndexFilter.isIgnorePathsForSpecificTypes()) { QName nodeType = nodeDAO.getNodeType(childNodePair.getFirst()); addCurrentChildAssoc = !typeIndexFilter.shouldBeIgnored(nodeType); } if (!addCurrentChildAssoc && aspectIndexFilter.isIgnorePathsForSpecificAspects()) { addCurrentChildAssoc = !aspectIndexFilter.shouldBeIgnored(getNodeAspects(childNodePair.getFirst())); } if (addCurrentChildAssoc) { childAssocs.add(tenantService.getBaseName(childAssocPair.getSecond(), true)); } return true; } @Override public void done() {} }); nodeMetaData.setChildAssocs(childAssocs); } if (includeChildIds) { final List<Long> childIds = new ArrayList<Long>(100); nodeDAO.getChildAssocs( nodeId, null, null, null, null, null, new ChildAssocRefQueryCallback() { @Override public boolean preLoadNodes() { return false; } @Override public boolean orderResults() { return false; } @Override public boolean handle( Pair<Long, ChildAssociationRef> childAssocPair, Pair<Long, NodeRef> parentNodePair, Pair<Long, NodeRef> childNodePair) { boolean addCurrentId = true; if (typeIndexFilter.isIgnorePathsForSpecificTypes()) { QName nodeType = nodeDAO.getNodeType(childNodePair.getFirst()); addCurrentId = !typeIndexFilter.shouldBeIgnored(nodeType); } if (!addCurrentId) { addCurrentId = !aspectIndexFilter.shouldBeIgnored(getNodeAspects(childNodePair.getFirst())); } if (addCurrentId) { childIds.add(childNodePair.getFirst()); } return true; } @Override public void done() {} }); nodeMetaData.setChildIds(childIds); } if (includeParentAssociations && !ignoreLargeMetadata) { final List<ChildAssociationRef> parentAssocs = new ArrayList<ChildAssociationRef>(100); nodeDAO.getParentAssocs( nodeId, null, null, null, new ChildAssocRefQueryCallback() { @Override public boolean preLoadNodes() { return false; } @Override public boolean orderResults() { return false; } @Override public boolean handle( Pair<Long, ChildAssociationRef> childAssocPair, Pair<Long, NodeRef> parentNodePair, Pair<Long, NodeRef> childNodePair) { parentAssocs.add(tenantService.getBaseName(childAssocPair.getSecond(), true)); return true; } @Override public void done() {} }); for (ChildAssociationRef ref : categoryPaths.getCategoryParents()) { parentAssocs.add(tenantService.getBaseName(ref, true)); } CRC32 crc = new CRC32(); for (ChildAssociationRef car : parentAssocs) { try { crc.update(car.toString().getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UTF-8 encoding is not supported"); } } nodeMetaData.setParentAssocs(parentAssocs, crc.getValue()); // TODO non-child associations // Collection<Pair<Long, AssociationRef>> sourceAssocs = // nodeDAO.getSourceNodeAssocs(nodeId); // Collection<Pair<Long, AssociationRef>> targetAssocs = // nodeDAO.getTargetNodeAssocs(nodeId); // // nodeMetaData.setAssocs(); } if (includeOwner) { // cached in OwnableService nodeMetaData.setOwner(ownableService.getOwner(status.getNodeRef())); } rowHandler.processResult(nodeMetaData); } }