03-02-2015 04:56 PM
Hello,
I am considering a repository structure where there are two document types: let's call one "Base" and the other "Enhancements". I would like to use the Relations functionality of Nuxeo to indicated that a given "Enhancements" requires a given "Base" using the standard Dublin Core relation included with the software.
This is working fine, and I am able to find the Base documents from the Enhancements documents that require them programmatically using the Relations.GetRelations
operation. However, this only traverses one edge of the given relation. Is there already a way in Nuxeo to retrieve both documents "EnhancementsB" and "Base" in the following dependency chain, if I am querying the "EnhancementsA" document?
EnhancementsA ---requires--> EnhancementsB ---requires--> Base
Obviously I could take the output I have and query the document it returns, but my goal is to reduce the number of serial web requests needed to acquire this information, since the ultimate request is coming from a mobile client. I could also define a server-side operation to go through a fixed number of such relation edges, but maybe there is a better way?
Thanks for any help.
03-13-2015 05:10 PM
I ended up implementing the following solution. It appears to work for my use case.
package com.example;
import org.apache.commons.lang.StringUtils;
import org.nuxeo.ecm.automation.core.Constants;
import org.nuxeo.ecm.automation.core.annotations.Context;
import org.nuxeo.ecm.automation.core.annotations.Operation;
import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
import org.nuxeo.ecm.automation.core.annotations.Param;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
import org.nuxeo.ecm.platform.relations.api.Graph;
import org.nuxeo.ecm.platform.relations.api.Node;
import org.nuxeo.ecm.platform.relations.api.QNameResource;
import org.nuxeo.ecm.platform.relations.api.RelationManager;
import org.nuxeo.ecm.platform.relations.api.Resource;
import org.nuxeo.ecm.platform.relations.api.ResourceAdapter;
import org.nuxeo.ecm.platform.relations.api.Statement;
import org.nuxeo.ecm.platform.relations.api.impl.ResourceImpl;
import org.nuxeo.ecm.platform.relations.api.util.RelationConstants;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Operation(id = GetAllLinkedDocuments.ID, category = Constants.CAT_SERVICES, label = "Get All Linked Documents", description = "Get all documents related to the input document. Includes the input document and any linked documents found via recursive relation search.")
public class GetAllLinkedDocuments {
public static final String ID = "Example.Document.GetAllLinkedDocuments";
@Context
protected CoreSession session;
@Context
protected RelationManager relations;
@Param(name = "predicate")
protected String predicate;
@Param(name = "graphName", required = false)
protected String graphName;
@OperationMethod
public DocumentModelList run(DocumentModel doc) throws Exception {
Resource pred = new ResourceImpl(predicate);
Graph graph = relations.getGraphByName(getGraphName());
DocumentModelList result = new DocumentModelListImpl();
depthFirstSearch(graph, pred, doc, result);
return result;
}
private void depthFirstSearch(Graph graph, Resource pred,
DocumentModel doc,
DocumentModelList found) {
found.add(doc);
for (DocumentModel relatedDoc : getRelatedDocs(graph, pred, doc)) {
if (!found.contains(relatedDoc)) {
depthFirstSearch(graph, pred, relatedDoc, found);
}
}
}
private DocumentModelList getRelatedDocs(Graph graph, Resource pred,
DocumentModel doc) {
QNameResource resource = (QNameResource) relations.getResource(
RelationConstants.DOCUMENT_NAMESPACE, doc, null);
List<Statement> statements = graph.getStatements(resource, pred, null);
DocumentModelList docs = new DocumentModelListImpl(statements.size());
for (Statement st : statements) {
DocumentModel dm = getDocumentModel(st.getObject());
if (dm != null) {
docs.add(dm);
}
}
return docs;
}
// copied from org.nuxeo.ecm.automation.core.operations.services.GetRelations
protected DocumentModel getDocumentModel(Node node) throws
ClientException {
if (node.isQNameResource()) {
QNameResource resource = (QNameResource) node;
Map<String, Object> context = Collections.<String, Object> singletonMap(
ResourceAdapter.CORE_SESSION_CONTEXT_KEY, session);
Object o = relations.getResourceRepresentation(
resource.getNamespace(), resource, context);
if (o instanceof DocumentModel) {
return (DocumentModel) o;
}
}
return null;
}
// copied from org.nuxeo.ecm.automation.core.operations.services.GetRelation
public String getGraphName() {
if (StringUtils.isEmpty(graphName)) {
return RelationConstants.GRAPH_NAME;
}
return graphName;
}
}
03-03-2015 09:25 AM
I should clarify that I don't need the file content for all documents in the single response, but only the Document entries as they appear in the response to Relations.GetRelations
.
03-13-2015 05:10 PM
I ended up implementing the following solution. It appears to work for my use case.
package com.example;
import org.apache.commons.lang.StringUtils;
import org.nuxeo.ecm.automation.core.Constants;
import org.nuxeo.ecm.automation.core.annotations.Context;
import org.nuxeo.ecm.automation.core.annotations.Operation;
import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
import org.nuxeo.ecm.automation.core.annotations.Param;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
import org.nuxeo.ecm.platform.relations.api.Graph;
import org.nuxeo.ecm.platform.relations.api.Node;
import org.nuxeo.ecm.platform.relations.api.QNameResource;
import org.nuxeo.ecm.platform.relations.api.RelationManager;
import org.nuxeo.ecm.platform.relations.api.Resource;
import org.nuxeo.ecm.platform.relations.api.ResourceAdapter;
import org.nuxeo.ecm.platform.relations.api.Statement;
import org.nuxeo.ecm.platform.relations.api.impl.ResourceImpl;
import org.nuxeo.ecm.platform.relations.api.util.RelationConstants;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Operation(id = GetAllLinkedDocuments.ID, category = Constants.CAT_SERVICES, label = "Get All Linked Documents", description = "Get all documents related to the input document. Includes the input document and any linked documents found via recursive relation search.")
public class GetAllLinkedDocuments {
public static final String ID = "Example.Document.GetAllLinkedDocuments";
@Context
protected CoreSession session;
@Context
protected RelationManager relations;
@Param(name = "predicate")
protected String predicate;
@Param(name = "graphName", required = false)
protected String graphName;
@OperationMethod
public DocumentModelList run(DocumentModel doc) throws Exception {
Resource pred = new ResourceImpl(predicate);
Graph graph = relations.getGraphByName(getGraphName());
DocumentModelList result = new DocumentModelListImpl();
depthFirstSearch(graph, pred, doc, result);
return result;
}
private void depthFirstSearch(Graph graph, Resource pred,
DocumentModel doc,
DocumentModelList found) {
found.add(doc);
for (DocumentModel relatedDoc : getRelatedDocs(graph, pred, doc)) {
if (!found.contains(relatedDoc)) {
depthFirstSearch(graph, pred, relatedDoc, found);
}
}
}
private DocumentModelList getRelatedDocs(Graph graph, Resource pred,
DocumentModel doc) {
QNameResource resource = (QNameResource) relations.getResource(
RelationConstants.DOCUMENT_NAMESPACE, doc, null);
List<Statement> statements = graph.getStatements(resource, pred, null);
DocumentModelList docs = new DocumentModelListImpl(statements.size());
for (Statement st : statements) {
DocumentModel dm = getDocumentModel(st.getObject());
if (dm != null) {
docs.add(dm);
}
}
return docs;
}
// copied from org.nuxeo.ecm.automation.core.operations.services.GetRelations
protected DocumentModel getDocumentModel(Node node) throws
ClientException {
if (node.isQNameResource()) {
QNameResource resource = (QNameResource) node;
Map<String, Object> context = Collections.<String, Object> singletonMap(
ResourceAdapter.CORE_SESSION_CONTEXT_KEY, session);
Object o = relations.getResourceRepresentation(
resource.getNamespace(), resource, context);
if (o instanceof DocumentModel) {
return (DocumentModel) o;
}
}
return null;
}
// copied from org.nuxeo.ecm.automation.core.operations.services.GetRelation
public String getGraphName() {
if (StringUtils.isEmpty(graphName)) {
return RelationConstants.GRAPH_NAME;
}
return graphName;
}
}
03-16-2015 09:00 AM
Instead of creating getRelatedDocs and copying the methods getDocumentModel and getGraphName, why not use the corresponding operation (Relations.GetRelations) ? Is there any performance issue?
For example, something like this (not tested):
private void depthFirstSearch(Graph graph, Resource pred,
DocumentModel doc,
DocumentModelList found) {
OperationContext ctx = new OperationContext(session);
ctx.setInput(doc);
Map<String, Object> params = new HashMap<String, Object>();
params.put("predicate", "http://purl.org/dc/terms/References");
params.put("outgoing", true);
AutomationService service = Framework.getService(AutomationService.class);
found.add(doc);
for (DocumentModel relatedDoc : (DocumentModelList)service.run(ctx, "Relations.GetRelations", params)) {
if (!found.contains(relatedDoc)) {
depthFirstSearch(graph, pred, relatedDoc, found);
}
}
}
03-16-2015 09:53 AM
That looks like it would work in principle. I did notice that they were renaming the GetRelations/CreateRelation operation in the fasttrack release though.
Find what you came for
We want to make your experience in Hyland Connect as valuable as possible, so we put together some helpful links.