Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-4232 performance issue in VALUES clause query #4330

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,5 @@ protected void meetNode(QueryModelNode node) throws RuntimeException {
super.meetNode(node);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.eclipse.rdf4j.common.annotation.InternalUseOnly;
import org.eclipse.rdf4j.query.algebra.And;
import org.eclipse.rdf4j.query.algebra.BindingSetAssignment;
import org.eclipse.rdf4j.query.algebra.Filter;
import org.eclipse.rdf4j.query.algebra.Join;
import org.eclipse.rdf4j.query.algebra.LeftJoin;
Expand Down Expand Up @@ -64,6 +65,8 @@ public class GraphPattern {
*/
private List<ValueExpr> constraints = new ArrayList<>();

private BindingSetAssignment inlineData;

/**
* Creates a new graph pattern.
*/
Expand Down Expand Up @@ -156,6 +159,7 @@ public void clear() {
requiredTEs.clear();
optionalTEs.clear();
constraints.clear();
inlineData = null;
}

/**
Expand Down Expand Up @@ -195,7 +199,19 @@ public TupleExpr buildTupleExpr() {
result = new Filter(result, constraint);
}

if (getInlineData() != null) {
result = new Join(getInlineData(), result);
}

return result;
}

public BindingSetAssignment getInlineData() {
return inlineData;
}

public void setInlineData(BindingSetAssignment bsa) {
this.inlineData = bsa;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2077,7 +2077,22 @@ public BindingSetAssignment visit(ASTInlineData node, Object data) throws Visito

bsa.setBindingSets(bindingSets);

graphPattern.addRequiredTE(bsa);
if (graphPattern.getInlineData() != null) {
GraphPattern parentGP = graphPattern;
graphPattern = new GraphPattern(parentGP);

graphPattern.setInlineData(bsa);

TupleExpr te = graphPattern.buildTupleExpr();
if (node.isScopeChange()) {
((VariableScopeChange) te).setVariableScopeChange(true);
}
parentGP.addRequiredTE(te);

graphPattern = parentGP;
} else {
graphPattern.setInlineData(bsa);
}
return bsa;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import java.util.List;

import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.query.algebra.BindingSetAssignment;
import org.eclipse.rdf4j.query.algebra.Extension;
import org.eclipse.rdf4j.query.algebra.Join;
import org.eclipse.rdf4j.query.algebra.Order;
import org.eclipse.rdf4j.query.algebra.Projection;
import org.eclipse.rdf4j.query.algebra.Service;
Expand Down Expand Up @@ -82,6 +84,29 @@ public void testAskQuerySolutionModifiers() {

}

@Test
public void testValuesOptionalQuery() throws Exception {
String query = "select ?parent ?child\n"
+ "where {\n"
+ " values ?type {<owl:Class>}\n"
+ " ?parent <rdf:type> ?type .\n"
+ " optional {\n"
+ " ?child <rdfs:subClassOf> ?parent ;\n"
+ " <rdf:type> ?type .\n"
+ " }\n"
+ "}";

TupleExprBuilder builder = new TupleExprBuilder(SimpleValueFactory.getInstance());
ASTQueryContainer qc = SyntaxTreeBuilder.parseQuery(query);
TupleExpr result = builder.visit(qc, null);

assertThat(result).isInstanceOf(Projection.class);
Join topJoin = (Join) ((Projection) result).getArg();

assertThat(topJoin.getLeftArg()).isInstanceOf(BindingSetAssignment.class);

}

@Test
public void testNegatedPathWithFixedObject() {
String query = "ASK WHERE { ?s !<http://example.org/p> <http://example.org/o> . }";
Expand Down
13 changes: 13 additions & 0 deletions core/sail/memory/src/test/resources/benchmarkFiles/query-values.qr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
prefix owl: <http://www.w3.org/2002/07/owl#>

select ?parent ?child
where {
values ?type {owl:Class}
?parent rdf:type ?type .
optional {
?child rdfs:subClassOf ?parent ;
rdf:type ?type .
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
prefix owl: <http://www.w3.org/2002/07/owl#>

select ?parent ?child
where {
?parent rdf:type owl:Class .
optional {
?child rdfs:subClassOf ?parent ;
rdf:type owl:Class .
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*******************************************************************************
* Copyright (c) 2022 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/

package org.eclipse.rdf4j.sail.nativerdf;

import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.rdf4j.model.util.Values.iri;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import org.apache.commons.io.IOUtils;
import org.eclipse.rdf4j.common.transaction.IsolationLevels;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.util.ModelBuilder;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.model.vocabulary.OWL;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.model.vocabulary.RDFS;
import org.eclipse.rdf4j.query.explanation.Explanation;
import org.eclipse.rdf4j.repository.sail.SailRepository;
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

/**
* @author Jeen Broekstra
*/
public class ValuesClauseTest {

private static SailRepository repository;

public static TemporaryFolder tempDir = new TemporaryFolder();

private static final String query1;
private static final String query2;

static {
try {
query1 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query-values.qr"), StandardCharsets.UTF_8);
query2 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query-without-values.qr"),
StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@BeforeClass
public static void beforeClass() throws IOException {
tempDir.create();
File file = tempDir.newFolder();

repository = new SailRepository(new NativeStore(file, "spoc,posc,cspo,opsc"));

int numberOfItems = 2;
int numberOfChildren = 2;
int numberOfTypeOwlClassStatements = 500;
int numberOfSubClassOfStatements = 10_000;

try (var conn = repository.getConnection()) {
conn.begin(IsolationLevels.NONE);
for (int i = 0; i < numberOfItems; i++) {

var parent = iri("http://example.org/parent_" + i);

Model m = new ModelBuilder().setNamespace(OWL.NS)
.setNamespace("ex", "http://example.org/")
.subject(parent)
.add(RDF.TYPE, OWL.CLASS)
.add(RDF.TYPE, RDFS.CLASS)
.add(RDFS.LABEL, "parent " + i)
.build();
conn.add(m);
if (i % 2 == 0) {
for (int j = 0; j < numberOfChildren; j++) {
m = new ModelBuilder().setNamespace(OWL.NS)
.setNamespace("ex", "http://example.org/")
.subject("ex:child_" + i + "_" + j)
.add(RDF.TYPE, OWL.CLASS)
.add(RDF.TYPE, RDFS.CLASS)
.add(RDFS.SUBCLASSOF, parent)
.add(RDFS.LABEL, "child of " + i)
.build();
conn.add(m);
}
}

}
for (int i = 0; i < numberOfTypeOwlClassStatements; i++) {
conn.add(Values.bnode(), RDF.TYPE, OWL.CLASS);
}

for (int i = 0; i < numberOfSubClassOfStatements; i++) {
conn.add(Values.bnode(), RDFS.SUBCLASSOF, Values.bnode());
}
conn.commit();
}
}

@AfterClass
public static void afterClass() throws IOException {
tempDir.delete();
repository.shutDown();
tempDir = null;
repository = null;
}

@Test
public void valuesOptionalQuery() {
try (SailRepositoryConnection connection = repository.getConnection()) {
System.out.println(connection.prepareTupleQuery(query1).explain(Explanation.Level.Executed));
assertThat(connection.prepareTupleQuery(query1).evaluate().stream().count()).isEqualTo(505);
}
}

@Test
public void simpleEquivalentQuery() {
try (SailRepositoryConnection connection = repository.getConnection()) {
System.out.println(connection.prepareTupleQuery(query2).explain(Explanation.Level.Executed));
assertThat(connection.prepareTupleQuery(query2).evaluate().stream().count()).isEqualTo(505);
}
}

private static InputStream getResourceAsStream(String name) {
return ValuesClauseTest.class.getClassLoader().getResourceAsStream(name);
}
}
Loading