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

Bug#246 sparql plural values #254

Merged
merged 2 commits into from
Jun 24, 2024
Merged
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 @@ -17,6 +17,9 @@
*/
package cz.cvut.kbss.jopa.query.parameter;

import cz.cvut.kbss.jopa.query.sparql.SparqlConstants;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

Expand All @@ -29,12 +32,30 @@ class CollectionParameterValue extends AbstractParameterValue {
}

@Override
public Object getValue() {
public List<Object> getValue() {
return values.stream().map(ParameterValue::getValue).collect(Collectors.toList());
}

@Override
public String getQueryString() {
return values.stream().map(ParameterValue::getQueryString).collect(Collectors.joining(","));
}

@Override
public List<String> toQueryValues(int size) {
assert size >= values.size();
final List<String> result = new ArrayList<>(size);
values.stream().map(ParameterValue::getQueryString).forEach(result::add);
if (values.size() < size) {
for (int i = values.size(); i < size; i++) {
result.add(SparqlConstants.UNDEF);
}
}
return result;
}

@Override
public int valueCount() {
return values.size();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
*/
package cz.cvut.kbss.jopa.query.parameter;

import cz.cvut.kbss.jopa.query.sparql.SparqlConstants;

import java.util.ArrayList;
import java.util.List;

/**
* Query parameter value holder.
*/
Expand All @@ -36,6 +41,31 @@ public interface ParameterValue {
*/
String getQueryString();

/**
* Builds a list of the specified size containing the value(s) represented by this parameter value.
* <p>
* If this instance does not contain enough values to fill in the list of the specified size, its remainder is
* filled with {@link cz.cvut.kbss.jopa.query.sparql.SparqlConstants#UNDEF}s.
* <p>
* The resulting list will be used to build a SPARQL {@literal VALUES} table.
*
* @param size Requested size of value list
* @return List of values
*/
default List<String> toQueryValues(int size) {
assert size > 0;

if (size == 1) {
return List.of(getQueryString());
}
final List<String> result = new ArrayList<>(size);
result.add(getQueryString());
for (int i = 1; i < size; i++) {
result.add(SparqlConstants.UNDEF);
}
return result;
}

/**
* Whether this parameter value is set or it represents just the parameter identification.
*
Expand All @@ -44,4 +74,13 @@ public interface ParameterValue {
default boolean isSet() {
return true;
}

/**
* Returns the number of values held by this instance.
* <p>
* Will return number different from 1 only for collection value parameters.
*
* @return Number of values represented by this instance
*/
default int valueCount() {return 1;}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public class SparqlConstants {
*/
public static final String RDF_TYPE_SHORTCUT = "a";

/**
* The {@literal UNDEF} keyword.
*/
public static final String UNDEF = "UNDEF";

private SparqlConstants() {
throw new AssertionError();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@
import cz.cvut.kbss.jopa.query.QueryHolder;
import cz.cvut.kbss.jopa.query.QueryParameter;

import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

public class SparqlQueryHolder implements QueryHolder {

Expand Down Expand Up @@ -193,15 +201,32 @@ private static Optional<String> assembleValuesClause(Set<QueryParameter<?>> para
return Optional.empty();
}
final StringBuilder variables = new StringBuilder();
final StringBuilder data = new StringBuilder();
final int tableSize = maxValueCount(parameters);
final List<List<String>> valueTable = new ArrayList<>(parameters.size());
for (QueryParameter<?> qp : parameters) {
if (!variables.isEmpty()) {
variables.append(' ');
}
variables.append(qp.getIdentifierAsQueryString());
data.append('(').append(qp.getValue().getQueryString()).append(')');
valueTable.add(qp.getValue().toQueryValues(tableSize));
}
return Optional.of(" VALUES (" + variables + ") {" + data + "}");
return Optional.of(" VALUES (" + variables + ") { " + valueTableToString(valueTable, tableSize) + "}");
}

private static int maxValueCount(Set<QueryParameter<?>> parameters) {
return parameters.stream().map(p -> p.getValue().valueCount()).max(Integer::compareTo).orElse(1);
}

private static String valueTableToString(List<List<String>> valueTable, int rowSize) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < rowSize; i++) {
sb.append("( ");
for (List<String> row : valueTable) {
sb.append(row.get(i)).append(" ");
}
sb.append(") ");
}
return sb.toString();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@
package cz.cvut.kbss.jopa.query.parameter;

import cz.cvut.kbss.jopa.environment.utils.Generators;
import cz.cvut.kbss.jopa.query.sparql.SparqlConstants;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItems;
import static org.junit.jupiter.api.Assertions.assertEquals;

class CollectionParameterValueTest {
Expand All @@ -34,17 +38,35 @@ class CollectionParameterValueTest {

@Test
void getValueReturnsValuesOfIndividualElements() {
final CollectionParameterValue sut = new CollectionParameterValue(Arrays.asList(V_ONE, V_TWO, V_THREE));
final CollectionParameterValue sut = new CollectionParameterValue(List.of(V_ONE, V_TWO, V_THREE));

assertEquals(Arrays.asList(V_ONE.getValue(), V_TWO.getValue(), V_THREE.getValue()), sut.getValue());
assertEquals(List.of(V_ONE.getValue(), V_TWO.getValue(), V_THREE.getValue()), sut.getValue());
}

@Test
void getQueryStringReturnsQueryStringRepresentationsOfIndividualElementsJoinedByComma() {
final CollectionParameterValue sut = new CollectionParameterValue(Arrays.asList(V_ONE, V_TWO, V_THREE));
final CollectionParameterValue sut = new CollectionParameterValue(List.of(V_ONE, V_TWO, V_THREE));

assertEquals(
Stream.of(V_ONE, V_TWO, V_THREE).map(ParameterValue::getQueryString).collect(Collectors.joining(",")),
sut.getQueryString());
}

@Test
void toQueryValuesReturnsListOfQueryStringifiedParameterValues() {
final CollectionParameterValue sut = new CollectionParameterValue(List.of(V_ONE, V_TWO, V_THREE));
final List<String> result = sut.toQueryValues(sut.valueCount());
assertEquals(Stream.of(V_ONE, V_TWO, V_THREE).map(ParameterValue::getQueryString).toList(), result);
}

@Test
void toQueryValuesReturnsListOfQueryStringifiedParameterValuesFilledWithSparqlUndefWhenSizeIsGreaterThanValueCount() {
final int size = 6;
final CollectionParameterValue sut = new CollectionParameterValue(List.of(V_ONE, V_TWO, V_THREE));
final List<String> result = sut.toQueryValues(size);
assertThat(result, hasItems(Stream.of(V_ONE, V_TWO, V_THREE).map(ParameterValue::getQueryString).toArray(String[]::new)));
final String[] remainder = new String[size - sut.valueCount()];
Arrays.fill(remainder, SparqlConstants.UNDEF);
assertThat(result, hasItems(remainder));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cz.cvut.kbss.jopa.query.parameter;

import cz.cvut.kbss.jopa.environment.utils.Generators;
import cz.cvut.kbss.jopa.query.sparql.SparqlConstants;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

class ParameterValueTest {

@Test
void toQueryValuesReturnsListOfSizeOneWithParameterValueStringifiedForQuery() {
final ParameterValue sut = new IntegerParameterValue(Generators.randomPositiveInt(1000));
final List<String> result = sut.toQueryValues(1);
assertEquals(List.of(sut.getQueryString()), result);
}

@Test
void toQueryValuesReturnsListOfSpecifiedSizeFilledWithValueAndUndef() {
final ParameterValue sut = new IntegerParameterValue(Generators.randomPositiveInt(1000));
int size = 5;
final List<String> result = sut.toQueryValues(size);
assertEquals(size, result.size());
assertEquals(sut.getQueryString(), result.get(0));
result.subList(1, size).forEach(v -> assertEquals(SparqlConstants.UNDEF, v));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@

public class NamedParameterSparqlQueryHolderTest {

private static final String QUERY = "PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n" +
"SELECT ?craft\n" +
"{\n" +
"?craft foaf:name \"Apollo 7\" .\n" +
"?craft foaf:homepage ?homepage .\n" +
"}";
private static final String QUERY = """
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?craft
{
?craft foaf:name "Apollo 7" .
?craft foaf:homepage ?homepage .
}""";
private static final List<String> PARTS = Arrays.asList("PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n" +
"SELECT ", "\n{\n", " foaf:name \"Apollo 7\" .\n", " foaf:homepage ", " .\n}");
"SELECT ", "\n{\n ", " foaf:name \"Apollo 7\" .\n ", " foaf:homepage ", " .\n}");
private static final List<String> PARAMS = Arrays.asList("craft", "craft", "craft", "homepage");
private static final Set<String> PARAM_NAMES = new HashSet<>(Arrays.asList("craft", "homepage"));

Expand Down Expand Up @@ -103,7 +104,7 @@ public void getValueOfUnknownParameterThrowsIllegalArgumentException() {

@Test
public void testSetParameter() {
final String value = "http://kbss.felk.cvut.cz";
final String value = "https://kbss.felk.cvut.cz";
holder.setParameter(new QueryParameter<>("homepage", paramValueFactory), value);
assertEquals(value, holder.getParameterValue(holder.getParameter("homepage")));
}
Expand All @@ -128,7 +129,7 @@ public void setUnknownParameterThrowsException() {
@Test
public void clearParameterRemovesParameterValue() {
final QueryParameter<?> qp = new QueryParameter<>("homepage", paramValueFactory);
holder.setParameter(qp, URI.create("http://kbss.felk.cvut.cz"));
holder.setParameter(qp, URI.create("https://kbss.felk.cvut.cz"));
assertNotNull(holder.getParameterValue(qp));
holder.clearParameter(qp);
assertNull(holder.getParameterValue(qp));
Expand All @@ -137,7 +138,7 @@ public void clearParameterRemovesParameterValue() {
@Test
public void clearParametersRemovesAllParameterValues() {
final QueryParameter<?> qp = new QueryParameter<>("homepage", paramValueFactory);
holder.setParameter(qp, URI.create("http://kbss.felk.cvut.cz"));
holder.setParameter(qp, URI.create("https://kbss.felk.cvut.cz"));
final QueryParameter<?> qpTwo = new QueryParameter<>("craft", paramValueFactory);
holder.setParameter(qpTwo, "Programming");
holder.getParameters().forEach(param -> assertNotNull(holder.getParameterValue(param)));
Expand All @@ -148,26 +149,28 @@ public void clearParametersRemovesAllParameterValues() {
@Test
public void assembleQueryWithUri() {
final QueryParameter<?> qp = new QueryParameter<>("homepage", paramValueFactory);
holder.setParameter(qp, URI.create("http://kbss.felk.cvut.cz"));
final String expected = "PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n" +
"SELECT ?craft\n" +
"{\n" +
"?craft foaf:name \"Apollo 7\" .\n" +
"?craft foaf:homepage <http://kbss.felk.cvut.cz> .\n" +
"}";
holder.setParameter(qp, URI.create("https://kbss.felk.cvut.cz"));
final String expected = """
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?craft
{
?craft foaf:name "Apollo 7" .
?craft foaf:homepage <https://kbss.felk.cvut.cz> .
}""";
assertEquals(expected, holder.assembleQuery());
}

@Test
public void assembleQueryWithLiteral() {
final QueryParameter<?> qp = new QueryParameter<>("homepage", paramValueFactory);
holder.setParameter(qp, "http://kbss.felk.cvut.cz", null);
final String expected = "PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n" +
"SELECT ?craft\n" +
"{\n" +
"?craft foaf:name \"Apollo 7\" .\n" +
"?craft foaf:homepage \"http://kbss.felk.cvut.cz\" .\n" +
"}";
holder.setParameter(qp, "https://kbss.felk.cvut.cz", null);
final String expected = """
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?craft
{
?craft foaf:name "Apollo 7" .
?craft foaf:homepage "https://kbss.felk.cvut.cz" .
}""";
assertEquals(expected, holder.assembleQuery());
}

Expand All @@ -192,10 +195,10 @@ public void setParametersAndAssembleQueryWithMultipleParamsNextToEachOther() {
void setParameterAddsValuesClauseWhenSetParameterIsInProjection() {
final QueryParameter<?> qp = new QueryParameter<>("craft", paramValueFactory);
qp.setProjected(true);
holder.setParameter(qp, URI.create("http://kbss.felk.cvut.cz/apollo7"));
holder.setParameter(qp, URI.create("https://kbss.felk.cvut.cz/apollo7"));
final String result = holder.assembleQuery();
assertThat(result, endsWith("VALUES (?craft) {(" + holder.getParameter("craft").getValue()
.getQueryString() + ")}"));
assertThat(result, endsWith("VALUES (?craft) { ( " + holder.getParameter("craft").getValue()
.getQueryString() + " ) }"));
assertThat(result, containsString(QUERY));
}
}
Loading
Loading