All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metreeca.rdf4j.assets.GraphItems Maven / Gradle / Ivy

/*
 * Copyright © 2013-2021 Metreeca srl
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.metreeca.rdf4j.assets;

import com.metreeca.json.Order;
import com.metreeca.json.Shape;
import com.metreeca.json.queries.Items;
import com.metreeca.json.shapes.*;

import org.eclipse.rdf4j.model.*;
import org.eclipse.rdf4j.model.vocabulary.OWL;
import org.eclipse.rdf4j.model.vocabulary.RDFS;
import org.eclipse.rdf4j.query.AbstractTupleQueryResultHandler;
import org.eclipse.rdf4j.query.BindingSet;

import java.util.*;
import java.util.stream.Stream;

import static com.metreeca.json.Values.*;
import static com.metreeca.json.shapes.And.and;
import static com.metreeca.rdf4j.SPARQLScribe.*;
import static com.metreeca.rdf4j.assets.Graph.graph;
import static com.metreeca.rdf4j.assets.GraphEngine.items;
import static com.metreeca.rest.Context.asset;
import static com.metreeca.rest.Scribe.*;

import static org.eclipse.rdf4j.model.util.Values.triple;

import static java.util.stream.Collectors.toList;

final class GraphItems extends GraphFacts {

	private final Options options=options();

	private final Graph graph=asset(graph());


	GraphItems(final Options options) {
		super(options);
	}


	Collection process(final IRI resource, final Items items) {

		final Shape shape=items.shape();
		final List orders=items.orders();
		final int offset=items.offset();
		final int limit=items.limit();

		final Shape filter=shape
				.filter(resource)
				.resolve(resource)
				.label(this::label);

		final Shape convey=shape
				.convey()
				.resolve(resource)
				.label(this::label);

		final Shape follow=and(orders.stream().map(Order::path).map(path -> path(convey, path)));
		final Collection template=convey.map(new TemplateProbe(root)).collect(toList());

		final Collection model=new LinkedHashSet<>();

		evaluate(() -> graph.exec(connection -> {
			connection.prepareTupleQuery(compile(() -> code(list(

					comment("items query"),

					prefix(OWL.NS),
					prefix(RDFS.NS),

					space(select(), where(

							space(block(

									select(true, var(root)), // transfer matches as tuples to preserve order

									block(

											space(tree(filter, true)),
											space(tree(follow, false))

									),

									order(list(Stream.concat(

											orders.stream().map(order ->
													sort(order.inverse(), var(hook(follow, order.path())))
											),

											Stream.of(asc(var(root))).filter(s -> // then root, unless already included
													orders.stream().map(Order::path).noneMatch(List::isEmpty)
											)

									))),

									offset(offset),
									limit(limit, options.get(items()))

							)),

							tree(convey, false)

					))

			)))).evaluate(new AbstractTupleQueryResultHandler() {

				@Override public void handleSolution(final BindingSet bindings) {

					final Value match=bindings.getValue(root);

					if ( match != null ) {

						if ( !match.equals(resource) ) {
							model.add(statement(resource, Shape.Contains, match));
						}

						template.forEach(statement -> {

							final Resource subject=statement.getSubject();
							final Value object=statement.getObject();

							final Value source=subject instanceof BNode
									? bindings.getValue(principal(((BNode)subject).getID(), bindings.getBindingNames()))
									: subject;

							final Value target=object instanceof BNode
									? bindings.getValue(principal(((BNode)object).getID(), bindings.getBindingNames()))
									: object;

							if ( source instanceof Resource && target != null ) {
								model.add(statement((Resource)source, statement.getPredicate(), target));
							}

						});

					}

				}

			});
		}));

		return model;
	}


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	private static final class TemplateProbe extends Shape.Probe> {

		private final String anchor;


		private TemplateProbe(final String anchor) {
			this.anchor=anchor;
		}


		@Override public Stream probe(final Link link) {
			return link.shape().map(this);
		}

		@Override public Stream probe(final Field field) {

			final String label=field.label();
			final Shape shape=field.shape();

			final BNode source=bnode(anchor);
			final BNode target=bnode(label);

			final Triple triple=traverse(field.iri(),
					iri -> triple(source, iri, target),
					iri -> triple(target, iri, source)
			);

			return Stream.concat(Stream.of(triple), shape.map(new TemplateProbe(label)));
		}


		@Override public Stream probe(final When when) {
			return Stream.concat(
					when.pass().map(this),
					when.fail().map(this)
			);
		}

		@Override public Stream probe(final And and) {
			return and.shapes().stream().flatMap(shape -> shape.map(this));
		}

		@Override public Stream probe(final Or or) {
			return or.shapes().stream().flatMap(shape -> shape.map(this));
		}


		@Override public Stream probe(final Shape shape) { return Stream.empty(); }

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy