org.aksw.jenax.graphql.sparql.GraphQlToSparqlConverter Maven / Gradle / Ivy
The newest version!
package org.aksw.jenax.graphql.sparql;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
import org.aksw.commons.collections.IterableUtils;
import org.aksw.commons.util.range.RangeUtils;
import org.aksw.jenax.arq.util.node.PathUtils;
import org.aksw.jenax.arq.util.syntax.QueryUtils;
import org.aksw.jenax.facete.treequery2.api.ConstraintNode;
import org.aksw.jenax.facete.treequery2.api.NodeQuery;
import org.aksw.jenax.facete.treequery2.impl.NodeQueryImpl;
import org.aksw.jenax.io.json.graph.GraphToJsonMapperNode;
import org.aksw.jenax.io.json.graph.GraphToJsonNodeMapperFragmentBody;
import org.aksw.jenax.io.json.graph.GraphToJsonNodeMapperFragmentHead;
import org.aksw.jenax.io.json.graph.GraphToJsonNodeMapperLiteral;
import org.aksw.jenax.io.json.graph.GraphToJsonNodeMapperObject;
import org.aksw.jenax.io.json.graph.GraphToJsonNodeMapperObjectLike;
import org.aksw.jenax.io.json.graph.GraphToJsonPropertyMapper;
import org.aksw.jenax.model.shacl.domain.ShPropertyShape;
import org.aksw.jenax.path.core.FacetPath;
import org.aksw.jenax.path.core.FacetStep;
import org.aksw.jenax.sparql.fragment.api.Fragment;
import org.aksw.jenax.sparql.fragment.api.Fragment1;
import org.aksw.jenax.sparql.fragment.api.MappedFragment;
import org.aksw.jenax.sparql.fragment.impl.ConceptUtils;
import org.aksw.jenax.sparql.fragment.impl.FragmentUtils;
import org.aksw.jenax.stmt.core.SparqlParserConfig;
import org.aksw.jenax.stmt.parser.query.SparqlQueryParser;
import org.aksw.jenax.stmt.parser.query.SparqlQueryParserImpl;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.query.Query;
import org.apache.jena.riot.system.PrefixMap;
import org.apache.jena.riot.system.PrefixMapFactory;
import org.apache.jena.shared.PrefixMapping;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.graph.PrefixMappingAdapter;
import org.apache.jena.sparql.path.P_Link;
import org.apache.jena.sparql.path.P_Path0;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.XSD;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.Range;
import graphql.language.Argument;
import graphql.language.ArrayValue;
import graphql.language.Directive;
import graphql.language.DirectivesContainer;
import graphql.language.Document;
import graphql.language.EnumValue;
import graphql.language.Field;
import graphql.language.FragmentDefinition;
import graphql.language.FragmentSpread;
import graphql.language.InlineFragment;
import graphql.language.ObjectField;
import graphql.language.ObjectValue;
import graphql.language.OperationDefinition;
import graphql.language.Selection;
import graphql.language.SelectionSet;
import graphql.language.StringValue;
import graphql.language.TypeName;
import graphql.language.Value;
/**
* Compiles a graphql query to a {@link GraphQlToSparqlMapping} instance. All
* fields can be qualified with IRIs of properties and classes. Names of
* unqualified fields are passed to a {@link GraphQlResolver} for resolution to
* IRIs. All resolver methods are allowed to throw
* {@link UnsupportedOperationException} to indicate absence of resolution
* support.
*/
public class GraphQlToSparqlConverter {
private static final Logger logger = LoggerFactory.getLogger(GraphQlToSparqlConverter.class);
protected GraphQlResolver resolver;
/** false -> rdf mode, true -> json mode*/
protected boolean jsonMode;
// public GraphQlToSparqlConverter(GraphQlResolver resolver) {
// this(resolver, false);
// }
public GraphQlToSparqlConverter(GraphQlResolver resolver, boolean jsonMode) {
super();
this.resolver = resolver;
this.jsonMode = jsonMode;
}
public GraphQlToSparqlMapping convertDocument(Document document, Map> assignments) {
GraphQlToSparqlMapping result = new GraphQlToSparqlMapping(document);
new GraphQlQueryWorker(document, assignments).convertDocument(result);
return result;
}
// public GraphQlToSparqlMapping convertDocument(Document document) {
// Map fragmentsByName = new LinkedHashMap<>();
//
// for (graphql.language.Node child : document.getChildren()) {
// if (child instanceof FragmentDefinition fragmentDefinition) {
//
// } else if (child instanceof OperationDefinition operationDefinition) {
//
// }
// }
//
// GraphQlToSparqlMapping result = new GraphQlToSparqlMapping(document);
// OperationDefinition op = document.getFirstDefinitionOfType(OperationDefinition.class).orElse(null);
// if (op != null) {
// SelectionSet selectionSet = op.getSelectionSet();
//
// // System.err.println("Field index: " + fieldIndex);
//
// new GraphQlQueryWorker(document).convertTopLevelFields(selectionSet, result);
// }
//
// return result;
// }
// public static TreeDataMap, Field> indexFields(Document document) {
// TreeDataMap, Field> result = new TreeDataMap<>();
// Path path = PathStr.newAbsolutePath();
// OperationDefinition op = document.getFirstDefinitionOfType(OperationDefinition.class).orElse(null);
// if (op != null) {
// SelectionSet selectionSet = op.getSelectionSet();
// indexFields(result, path, selectionSet);
// }
// return result;
// }
public class GraphQlQueryWorker {
protected Document document;
protected Map> assignments;
/** The currently encountered fragments (definitions are processed in order) */
protected Map fragmentsByName;
public GraphQlQueryWorker(Document document, Map> assignments) {
this.document = Objects.requireNonNull(document);
this.fragmentsByName = new LinkedHashMap<>();
this.assignments = assignments != null ? assignments : Collections.emptyMap();
}
public void convertDocument(GraphQlToSparqlMapping result) {
for (graphql.language.Node child : document.getChildren()) {
if (child instanceof FragmentDefinition fragmentDefinition) {
fragmentsByName.put(fragmentDefinition.getName(), fragmentDefinition);
} else if (child instanceof OperationDefinition operationDefinition) {
SelectionSet selectionSet = operationDefinition.getSelectionSet();
convertTopLevelFields(selectionSet, result);
} else {
// logger.warn("Unknown element: );
throw new RuntimeException("Unknown element: " + child);
}
}
}
/**
* The context keeps track of which information (e.g. namespaces) currently
* applies
*/
// protected Context context = new Context(null, null);
protected Stack contextStack = new Stack<>();
// protected TreeDataMap, Field> fieldIndex;
public void convertTopLevelFields(SelectionSet selectionSet, GraphQlToSparqlMapping result) {
contextStack.push(new Context(null, null));
// Index the field tree and create a global map of field names thereby
// considering
// aliases declared on fields using such as { foo (as: "bar") }
// fieldIndex = GraphQlUtils.indexFields(selectionSet);
// context = new Context(null, selectionSet);
// RdfToJsonNodeMapperObject nodeConverter = null;
for (Field field : selectionSet.getSelectionsOfType(Field.class)) {
convertTopLevelField(field, result);
}
}
public void convertTopLevelField(Field field, GraphQlToSparqlMapping result) {
Context context = contextStack.peek().newChildContext(field);
setupContext(context);
contextStack.push(context);
NodeQuery nodeQuery = NodeQueryImpl.newRoot();
context.setNodeQuery(nodeQuery);
// Relation filterRelation = tryParseSparqlQuery(context, field);
// if (filterRelation != null) {
// // TODO Error message if not an unary relation
// nodeQuery.setFilterRelation(filterRelation.toUnaryRelation());
// }
// The map of aggregators
// Map converters = new LinkedHashMap<>();
Multimap> args = GraphQlUtils.indexArguments(field);
String fieldName = field.getName();
// String fieldIri = deriveFieldIri(context, fieldName);
// Map> directives = field.getDirectivesByName();
if (logger.isDebugEnabled()) {
logger.debug("Seen top level field: " + fieldName);
}
// Node classNode = resolveClass(context, fieldName);
// NodeQuery fieldQuery = resolveKeyToClasses(nodeQuery, classNode);
NodeQuery fieldQuery = nodeQuery;
SelectionSet subSelection = field.getSelectionSet();
GraphToJsonMapperNode nodeMapper = convertInnerSelectionSet(subSelection, nodeQuery);
// Handle arguments of the field, such as slice, filter and orderBy
if (fieldQuery != null) {
tryApplySlice(nodeQuery, args, assignments);
tryApplyOrderBy(nodeQuery, args);
if (false) { // The idea was to allow to filter fields - but this codeblock is broken
for (Argument arg : field.getArguments()) {
String argName = arg.getName();
FacetPath facetPath = resolveProperty(context, argName);
NodeQuery argQuery = facetPath == null ? null : fieldQuery.resolve(facetPath);
Value> rawV = arg.getValue();
if (rawV instanceof StringValue) {
StringValue v = (StringValue) rawV;
String str = v.getValue();
org.apache.jena.graph.Node TO_STRING = NodeFactory.createURI("fn:" + XSD.xstring.getURI());
argQuery.constraints().fwd(TO_STRING).enterConstraints().eq(NodeFactory.createLiteral(str))
.activate().leaveConstraints();
}
}
}
}
processSparqlDirectives(context, resolver);
// Materialize prefixes
PrefixMap prefixMap = PrefixMapFactory.create(context.getFinalPrefixMap());
boolean isSingle = Cardinality.ONE.equals(context.getThisCardinality());
result.addEntry(field, prefixMap, nodeQuery, nodeMapper, isSingle);
// context = context.getParent();
contextStack.pop();
}
public GraphToJsonMapperNode convertInnerSelectionSet(SelectionSet selectionSet, NodeQuery nodeQuery) {
GraphToJsonMapperNode result;
if (selectionSet == null) {
result = GraphToJsonNodeMapperLiteral.get();
} else {
GraphToJsonNodeMapperObject nodeMapperObject = new GraphToJsonNodeMapperObject();
convertInnerSelectionSet(nodeMapperObject, selectionSet, nodeQuery);
// for (Selection> selection : selectionSet.getSelections()) {
// if (selection instanceof Field) {
// Field field = (Field)selection;
// String jsonKeyName = ObjectUtils.firstNonNull(field.getAlias(), field.getName());
// GraphToJsonPropertyMapper propertyMapper = convertInnerField(field, nodeQuery);
// if (propertyMapper != null) {
// nodeMapperObject.getPropertyMappers().put(jsonKeyName, propertyMapper);
// }
// } else if (selection instanceof InlineFragment) {
// InlineFragment fragment = (InlineFragment)selection;
//
// // If there is a type name then resolve it
// TypeName typeName = fragment.getTypeCondition();
// String name = typeName.getName();
// // contextStack.peek().ge
//
// // nodeQuery.
// // fragment.getSelectionSet();
// // throw new RuntimeException("Inline Fragement is not supported yet");
// NodeQuery nodeFragment = nodeQuery.addFragment();
// convertInnerSelectionSet(fragment.getSelectionSet(), nodeFragment);
// }
// }
result = nodeMapperObject;
}
return result;
}
public GraphToJsonMapperNode convertInnerSelectionSet(GraphToJsonNodeMapperObjectLike nodeMapperObject, SelectionSet selectionSet, NodeQuery nodeQuery) {
GraphToJsonMapperNode result;
for (Selection> selection : selectionSet.getSelections()) {
if (selection instanceof Field field) {
// String jsonKeyName = ObjectUtils.firstNonNull(field.getAlias(), field.getName());
// GraphToJsonPropertyMapper propertyMapper =
convertInnerField(field, nodeQuery, nodeMapperObject);
// if (propertyMapper != null) {
// nodeMapperObject.getPropertyMappers().put(jsonKeyName, propertyMapper);
// }
} else {
// Resolve fragments and inline fragements
SelectionSet resolvedSelectionSet = null;
List resolvedbDirectives = null;
TypeName resolvedTypeName = null;
if (selection instanceof FragmentSpread fragmentSpread) {
String name = fragmentSpread.getName();
FragmentDefinition def = fragmentsByName.get(name);
if (def == null) {
throw new RuntimeException("Fragment with name " + name + " not found");
}
resolvedSelectionSet = def.getSelectionSet();
resolvedbDirectives = def.getDirectives();
resolvedTypeName = def.getTypeCondition();
} else if (selection instanceof InlineFragment inlineFragment) {
resolvedSelectionSet = inlineFragment.getSelectionSet();
resolvedbDirectives = inlineFragment.getDirectives();
resolvedTypeName = inlineFragment.getTypeCondition();
}
if (resolvedSelectionSet != null) {
// If there is a type name then resolve it
String name = resolvedTypeName.getName();
// contextStack.peek().ge
// nodeQuery.
// fragment.getSelectionSet();
// throw new RuntimeException("Inline Fragement is not supported yet");
Field dummyField = Field.newField()
.name(name)
.directives(resolvedbDirectives)
.build()
;
Context context = contextStack.peek().newChildContext(dummyField);
setupContext(context);
contextStack.push(context);
NodeQuery nodeFragment = nodeQuery.addFragment();
context.setNodeQuery(nodeFragment);
processSparqlDirectives(context, resolver);
// fieldIri = deriveFieldIri(context, name);
// Node node = NodeFactory.createLiteral(nodeQuery.relationQuery().getScopeBaseName());
Node node = NodeFactory.createLiteral(nodeFragment.relationQuery().getScopeBaseName());
GraphToJsonNodeMapperFragmentHead fragmentMapper = GraphToJsonNodeMapperFragmentHead.of(node, true);
GraphToJsonNodeMapperFragmentBody bodyMapper = new GraphToJsonNodeMapperFragmentBody();
fragmentMapper.setTargetNodeMapper(bodyMapper);
// GraphToJsonM fragmentMapper.getTargetNodeMapper();
convertInnerSelectionSet(fragmentMapper.getTargetNodeMapper(), resolvedSelectionSet, nodeFragment);
// "fragment" + new Random().nextInt()
P_Path0 key = jsonMode
? new P_Link(NodeFactory.createLiteralString(node.getLiteralLexicalForm()))
: new P_Link(node);
nodeMapperObject.getPropertyMappers().put(key, fragmentMapper);
contextStack.pop();
}
}
}
result = nodeMapperObject;
return result;
}
/**
*
* @param field
* @param nodeQuery
* @param nodeMapperObject This method will register the mapper for the field to this object
* @return
*/
public GraphToJsonPropertyMapper convertInnerField(Field field, NodeQuery nodeQuery, GraphToJsonNodeMapperObjectLike nodeMapperObject) {
String jsonKeyName = ObjectUtils.firstNonNull(field.getAlias(), field.getName());
// context = setupContext(new Context(context, field), field);
Context context = contextStack.peek().newChildContext(field);
setupContext(context);
contextStack.push(context);
// Relation filterRelation = tryParseSparqlQuery(context, field);
GraphToJsonPropertyMapper propertyMapper = null;
Multimap> args = GraphQlUtils.indexArguments(field);
String fieldName = field.getName();
Map> directives = field.getDirectivesByName();
// Cardinality is set up in setupContext
boolean isSingle = Cardinality.ONE.equals(context.getThisCardinality());
FacetPath keyPath = resolveProperty(context, fieldName);
if (keyPath != null) {
boolean isInverse = directives.containsKey(GraphQlSpecialKeys.inverse);
if (isInverse) {
if (keyPath.getNameCount() == 1) {
keyPath = FacetPath.newRelativePath(keyPath.getName(0).toSegment().toggleDirection());
}
}
NodeQuery fieldQuery = nodeQuery.resolve(keyPath);
context.setNodeQuery(fieldQuery);
// if (filterRelation != null) {
// // TODO Error message if not an unary relation
// fieldQuery.setFilterRelation(filterRelation.toUnaryRelation());
// }
// FIXME We need to handle the xid field to get a resources IRI / blank node
// label
if (keyPath.getNameCount() == 1) { // xid resolves to a zero-segment path
// Set up an accumulator for the facet path
FacetStep step = keyPath.getFileName().toSegment();
P_Path0 basicPath = PathUtils.createStep(step.getNode(), step.getDirection().isForward());
boolean useRelationId = true;
if (useRelationId) {
Node node = NodeFactory.createLiteral(fieldQuery.relationQuery().getScopeBaseName());
propertyMapper = GraphToJsonPropertyMapper.of(node, step.getDirection().isForward());
} else {
propertyMapper = GraphToJsonPropertyMapper.of(basicPath);
}
propertyMapper.setSingle(isSingle);
Collection propertyShapes;
try {
propertyShapes = resolver.getGlobalPropertyShapes(basicPath);
} catch (UnsupportedOperationException e) {
propertyShapes = Collections.emptySet();
}
if (!propertyShapes.isEmpty()) {
boolean allMaxCountsAreOne = propertyShapes.stream().map(ShPropertyShape::getMaxCount)
.allMatch(v -> v != null && v.intValue() == 1);
boolean isUniqueLang = propertyShapes.stream().map(ShPropertyShape::isUniqueLang)
.allMatch(v -> v != null && v == true);
if (allMaxCountsAreOne) {
propertyMapper.setMaxCount(1);
}
if (isUniqueLang) {
propertyMapper.setUniqueLang(true);
}
}
if (directives.containsKey(GraphQlSpecialKeys.hide)) {
propertyMapper.setHidden(true);
}
}
SelectionSet subSelection = field.getSelectionSet();
GraphToJsonMapperNode childConverterContrib = convertInnerSelectionSet(subSelection, fieldQuery);
if (propertyMapper != null) { // can it be null here?
propertyMapper.setTargetNodeMapper(childConverterContrib);
}
// Handle arguments of the field, such as slice, filter and orderBy
tryApplySlice(fieldQuery, args, assignments);
tryApplyOrderBy(fieldQuery, args);
// tryUpdateContext(args);
// GraphQlUtils.tryU
if (false) {
for (Argument arg : field.getArguments()) {
String argName = arg.getName();
FacetPath facetPath = resolveProperty(context, argName);
NodeQuery argQuery = facetPath == null ? null : fieldQuery.resolve(facetPath);
Value> rawV = arg.getValue();
if (rawV instanceof StringValue) {
StringValue v = (StringValue) rawV;
String str = v.getValue();
org.apache.jena.graph.Node TO_STRING = NodeFactory.createURI("fn:" + XSD.xstring.getURI());
argQuery.constraints().fwd(TO_STRING).enterConstraints().eq(NodeFactory.createLiteral(str))
.activate().leaveConstraints();
}
}
}
}
processSparqlDirectives(context, resolver);
// context = context.getParent();
contextStack.pop();
if (nodeMapperObject != null) {
P_Path0 key = jsonMode
? new P_Link(NodeFactory.createLiteralString(jsonKeyName))
: keyPath.getFileName().toSegment().getStep();
// Objects.requireNonNull(propertyMapper);
// TODO Deal with xid
if (propertyMapper != null) {
nodeMapperObject.getPropertyMappers().put(key, propertyMapper);
}
}
return propertyMapper;
}
public Object tryApplyOrderBy(NodeQuery nodeQuery, Multimap> args) {
Value> val = GraphQlUtils.getArgumentValue(args, GraphQlSpecialKeys.orderBy);
if (val instanceof ArrayValue) {
ArrayValue array = (ArrayValue) val;
for (Value> item : array.getValues()) {
tryApplySortCondition(nodeQuery, item);
}
} else {
tryApplyOrderBy(nodeQuery, val);
}
// System.out.println("ORDER: " + val);
return null;
}
public Object tryApplyOrderBy(NodeQuery nodeQuery, Value> v) {
if (v instanceof ObjectValue) {
ObjectValue ov = (ObjectValue) v;
tryApplyOrderBy(nodeQuery, ov);
}
return null;
}
public FacetPath toFacetPath(Value> value) {
FacetPath result = null;
if (value instanceof ArrayValue) {
ArrayValue av = (ArrayValue) value;
result = FacetPath.newRelativePath();
for (Value> v : av.getValues()) {
FacetPath contrib = toFacetPathSingle(v);
if (contrib == null) {
result = null;
break;
} else {
result = result.resolve(contrib);
}
}
} else {
result = toFacetPathSingle(value);
}
return result;
}
public FacetPath toFacetPathSingle(Value> value) {
FacetPath result = null;
String key = null;
if (value instanceof StringValue) {
key = ((StringValue) value).getValue();
} else if (value instanceof EnumValue) {
key = ((EnumValue) value).getName();
}
if (key != null) {
result = resolver.resolveKeyToProperty(key);
}
return result;
}
public Object tryApplyOrderBy(NodeQuery nodeQuery, ObjectValue ov) {
// System.out.println("ORDER: " + ov);
Multimap> mm = GraphQlUtils.indexValues(ov);
// System.err.println(mm);
// for (String fieldName : mm.keySet()) {
for (Entry>> e : mm.asMap().entrySet()) {
String fieldName = e.getKey();
Value> val = Iterables.getOnlyElement(e.getValue());
String orderStr = GraphQlUtils.toString(val);
int order = "ASC".equalsIgnoreCase(orderStr) ? Query.ORDER_ASCENDING
: "DESC".equalsIgnoreCase(orderStr) ? Query.ORDER_DESCENDING : Query.ORDER_DEFAULT;
Context match = contextStack.peek().findOnlyField(fieldName);
// Set matches = context.findField(fieldName);
// List> matchingPaths = matches.stream().map(Context::getPath).collect(Collectors.toList());
//
// Context match;
// if (matches.isEmpty()) {
// throw new NoSuchElementException("Could not resolve field name " + fieldName + " at path " + context.getPath());
// } else if (matches.size() > 1) {
// throw new IllegalArgumentException("Ambiguous resolution. Field name + " + fieldName + " expected to resolve to 1 field. Got " + matchingPaths.size() + " fields: " + matchingPaths);
// } else {
// match = Iterables.getOnlyElement(matches);
// }
NodeQuery nq = match.getNodeQuery();
FacetPath facetPath = nq.getFacetPath();
FacetPath base = nodeQuery.getFacetPath();
FacetPath rel = base.relativize(facetPath);
// FacetPath facetPath = match.getFacetPath();
// NodeQuery nodeQuery = match.getNodeQuery();
ConstraintNode sortNode = nodeQuery.constraints().resolve(rel);
sortNode.sort(order);
}
// Value> pathValue = Iterables.getOnlyElement(mm.get("path"), null);
// FacetPath path = toFacetPath(pathValue);
// // System.out.println("FacetPath: " + path);
// Value> dirValue = Iterables.getOnlyElement(mm.get("dir"), null);
//
// if (path != null) {
// // We do not want to project the sort conditions so we need to
// // resolve against this node's constraints
// ConstraintNode sortNode = nodeQuery.constraints().resolve(path);
// sortNode.sort(Query.ORDER_ASCENDING);
// // nodeQuery.relationQuery().getSortConditions().add(new SortCondition(sortNode.getRoot().asJenaNode(), Query.ORDER_DESCENDING));
// // nodeQuery.resolve(path);
// // sortNode.getRoot().sortDesc();
// // nodeQuery.resolve(path).sortDesc();
// }
//
// // ov.getObjectFields()
// // Value> val = getArgumentValue(args, "order");
return null;
}
public Object tryApplySortCondition(NodeQuery nodeQuery, Value> value) {
if (value instanceof ObjectValue) {
ObjectValue ov = (ObjectValue) value;
tryApplyOrderBy(nodeQuery, ov);
}
return null;
}
}
/** Parses a GraphQL node's rdf annotations. */
public static Context setupContext(Context cxt) {
Field field = cxt.getField();
List rdfDirectives = field.getDirectives("rdf");
for (Directive rdf : rdfDirectives) {
String baseContrib = GraphQlUtils.toString(GraphQlUtils.getValue(rdf.getArgument("base")));
String iriContrib = GraphQlUtils.toString(GraphQlUtils.getValue(rdf.getArgument("iri")));
String nsContrib = GraphQlUtils.toString(GraphQlUtils.getValue(rdf.getArgument("ns")));
// String prefixContrib =
// GraphQlUtils.toString(GraphQlUtils.getValue(rdf.getArgument("prefix")));
PrefixMap prefixMapContrib = tryGetPrefixMap(GraphQlUtils.getValue(rdf.getArgument("prefixes")));
if (baseContrib != null) {
cxt.setBase(baseContrib);
}
if (iriContrib != null) {
cxt.setIri(iriContrib);
}
if (nsContrib != null) {
cxt.setNs(nsContrib);
}
// if (prefixContrib != null) { cxt.setPrefix(prefixContrib); }
if (prefixMapContrib != null) {
cxt.setLocalPrefixMap(prefixMapContrib);
}
}
ScopedCardinality scopedCardinality = getCardinality(field);
if (scopedCardinality != null) {
Cardinality cardinality = scopedCardinality.getCardinality();
if (scopedCardinality.isSelf()) {
cxt.setThisCardinality(cardinality);
}
if (scopedCardinality.isCascade()) {
cxt.setInheritedCardinality(cardinality);
}
}
// Invoke update to compute the effective prefix information
cxt.update();
return cxt;
}
public static String deriveFieldIri(Context context, String fieldName) {
// If base, iri or ns is specified then resolve the field to that IRI
String result = null;
String base = context.getFinalBase();
String ns = context.getFinalNs();
String iri = context.getFinalIri();
if (base != null && !base.isBlank()) {
result = base + fieldName;
}
String namespace = null;
// if (prefix != null) {
// namespace = context.getPrefix(prefix);
// }
if (ns != null) {
namespace = ns;
}
if (namespace != null) {
result = namespace + fieldName;
}
if (iri != null) {
result = iri;
}
return result;
}
// public static Relation tryParseSparqlQuery(Context cxt, Field field) {
// List sparqlDirectives = field.getDirectives("sparql");
// Directive dir = ListUtils.lastOrNull(sparqlDirectives);
//
// return tryParseSparqlQuery(cxt, dir);
// }
public static void processSparqlDirectives(Context cxt, GraphQlResolver resolver) {
Field field = cxt.getField();
String fieldName = field.getName();
// String fieldIri = deriveFieldIri(cxt, fieldName);
NodeQuery nodeQuery = cxt.getNodeQuery();
FacetPath facetPath = nodeQuery.getFacetPath();
// Process sparql directives
List filterRelations = new ArrayList<>();
for (Directive dir : field.getDirectives()) {
Fragment contrib = null;
boolean isInject = false;
if (GraphQlSpecialKeys.sparql.equals(dir.getName())) {
contrib = tryParseSparqlQuery(cxt, dir, GraphQlSpecialKeys.fragment);
// FIXME handle the case where fragment and inject are given
if (contrib == null) {
contrib = tryParseSparqlQuery(cxt, dir, GraphQlSpecialKeys.inject);
if (contrib != null) {
isInject = true;
}
}
} else if ("class".equals(dir.getName())) {
// If the field does not have an IRI we need to resort to resolution
Node classNode = resolveClass(resolver, cxt, fieldName);
String fieldIri = classNode.getURI();
// NodeQuery fieldQuery = resolveKeyToClasses(nodeQuery, classNode);
// Produce an IRI from the field name
contrib = ConceptUtils.createForRdfType(fieldIri);
}
if (contrib != null) {
if (isInject) {
// Resolve visible variables against known fields
Map varMap = new HashMap<>();
for (Var v : contrib.getVars()) {
String name = v.getName();
Context match;
try {
match = cxt.findOnlyField(name);
} catch (NoSuchElementException e) { // Expensive - get rid of exception handling
// Ignore
continue;
}
NodeQuery tgtNodeQuery = match.getNodeQuery();
FacetPath tgtFacetPath = tgtNodeQuery.getFacetPath();
FacetPath delta = facetPath.relativize(tgtFacetPath);
ConstraintNode cn = nodeQuery.constraints().resolve(delta);
Node jenaNode = cn.asJenaNode();
Node substVar = jenaNode; // cn.var();
varMap.put(v, substVar);
}
// FIXME Only substitute in-scope variables - so apply scope rename first
// Rename.reverseVarRename(null)
// Relation resolvedContrib = contrib.applyNodeTransform(new
// NodeTransformSubst(varMap));
MappedFragment mr = MappedFragment.of(contrib, varMap);
// System.err.println("Resolved contrib: " + resolvedContrib);
nodeQuery.addInjectFragment(mr);
// TODO Register the resolved relation
} else {
filterRelations.add(contrib.toFragment1());
}
}
}
Fragment1 filterRelation = filterRelations.stream().reduce((a, b) -> {
return a.joinOn(a.getVar()).with(b).toFragment1();
}).orElse(null);
nodeQuery.setFilterFragment(filterRelation);
}
public static Fragment tryParseSparqlQuery(Context cxt, Directive dir, String argName) {
String queryStr = Optional.ofNullable(dir).map(d -> d.getArgument(argName)).map(Argument::getValue)
.map(GraphQlUtils::toString).orElse(null);
Fragment result = null;
if (queryStr != null) {
String base = cxt.getFinalBase();
PrefixMapping pm = new PrefixMappingAdapter(cxt.getFinalPrefixMap());
SparqlQueryParser parser = SparqlQueryParserImpl
.create(SparqlParserConfig.newInstance().setBaseURI(base).setPrefixMapping(pm));
Query query = parser.apply(queryStr);
result = FragmentUtils.fromQuery(query);
}
return result;
}
/** Returns null if neither offset nor limit is found */
public static Range tryParseSlice(Multimap> args, Map> assignments) {
// System.out.println("Children: " + field.getNamedChildren().getChildren());
Long offset = GraphQlUtils.toLong(GraphQlUtils.getArgumentValue(args, "offset", assignments));
Long limit = GraphQlUtils.toLong(GraphQlUtils.getArgumentValue(args, "limit", assignments));
Range result = offset == null && limit == null ? null : RangeUtils.createRange(limit, offset);
return result;
}
/** This method uses the context to resolve a field name to a relative facet path */
public FacetPath resolveProperty(Context cxt, String fieldName) {
FacetPath result;
// For now it seems easier to handle the xid field here rather then in the
// property resolver
if ("xid".equals(fieldName)) {
result = FacetPath.newRelativePath();
} else {
String fieldIri = deriveFieldIri(cxt, fieldName);
result = fieldIri != null ? FacetPath.newRelativePath(FacetStep.fwd(NodeFactory.createURI(fieldIri)))
: resolver.resolveKeyToProperty(fieldName);
}
return result;
}
public static org.apache.jena.graph.Node resolveClass(GraphQlResolver resolver, Context cxt, String fieldName) {
String fieldIri = deriveFieldIri(cxt, fieldName);
Set classes = fieldIri != null ? Set.of(NodeFactory.createURI(fieldIri))
: resolver.resolveKeyToClasses(fieldName);
org.apache.jena.graph.Node result = IterableUtils.expectZeroOrOneItems(classes);
return result;
}
public NodeQuery resolveKeyToClasses(NodeQuery nq, org.apache.jena.graph.Node cls) {
NodeQuery result = null;
if (cls != null) {
// FacetStep step = FacetStep.of(RDF.type.asNode(), Direction.FORWARD, "",
// FacetStep.TARGET);
result = nq.constraints().fwd(RDF.type.asNode()).enterConstraints().eq(cls).activate().leaveConstraints()
.getRoot();
}
return result;
}
// public NodeQuery resolveKeyToProperty(NodeQuery nq, String key) {
// FacetPath keyPath = resolver.resolveKeyToProperty(key);
// NodeQuery result = keyPath == null ? null : nq.resolve(keyPath);
// return result;
// }
// public NodeQuery resolveKeyToProperty(NodeQuery nq, FacetPath facetPath) {
// // FacetPath keyPath = resolver.resolveKeyToProperty(key);
// NodeQuery result = keyPath == null ? null : nq.resolve(keyPath);
// return result;
// }
public static void tryApplySlice(NodeQuery nodeQuery, Multimap> args, Map> assignments) {
Range slice = tryParseSlice(args, assignments);
if (slice != null) {
long offset = QueryUtils.rangeToOffset(slice);
if (offset != 0) {
nodeQuery.offset(offset);
}
long limit = QueryUtils.rangeToLimit(slice);
nodeQuery.limit(limit);
}
}
// public static void tryUpdateContext(Multimap> args) {
// String baseIri = GraphQlUtils.tryGetArgumentValue(args, "base")
// .map(GraphQlUtils::toString)
// .orElse(null);
//
// Value> val = GraphQlUtils.tryGetArgumentValue(args, "namespaces").orElse(null);
// PrefixMap pm = tryGetPrefixMap(val);
//
// }
public static PrefixMap tryGetPrefixMap(Value> value) {
PrefixMap result = null;
if (value instanceof ObjectValue) {
ObjectValue obj = (ObjectValue) value;
result = PrefixMapFactory.create();
for (ObjectField field : obj.getObjectFields()) {
String prefix = field.getName();
String namespace = GraphQlUtils.toString(field.getValue());
result.add(prefix, namespace);
}
}
// System.out.println(result);
return result;
}
/**
* Returns the last {@literal @one} or {@literal @many} directive - null if
* there is none.
*/
public static ScopedCardinality getCardinality(DirectivesContainer> container) {
List directives = container.getDirectives();
ScopedCardinality result = directives.stream().map(GraphQlToSparqlConverter::getCardinality)
.filter(Objects::nonNull).reduce((a, b) -> b).orElse(null);
return result;
}
/**
* Return a cardinality object if the directive is {@literal @one} or
* {@literal @many} - null if neither.
*/
public static ScopedCardinality getCardinality(Directive d) {
ScopedCardinality result = null;
String name = d.getName();
Cardinality cardinality = GraphQlSpecialKeys.one.equalsIgnoreCase(name) ? Cardinality.ONE
: GraphQlSpecialKeys.many.equalsIgnoreCase(name) ? Cardinality.MANY : null;
if (cardinality != null) {
boolean cascade = Optional.ofNullable(GraphQlUtils.getArgAsBoolean(d, GraphQlSpecialKeys.cascade, null))
.orElse(false);
boolean self = Optional.ofNullable(GraphQlUtils.getArgAsBoolean(d, GraphQlSpecialKeys.self, null))
.orElse(true);
result = new ScopedCardinality(cardinality, cascade, self);
}
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy