MultiParamTest.java

/*******************************************************************************
 * 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.lucene;

import static org.eclipse.rdf4j.model.util.Values.literal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.TupleQueryResult;
import org.eclipse.rdf4j.repository.sail.SailRepository;
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
import org.eclipse.rdf4j.sail.evaluation.TupleFunctionEvaluationMode;
import org.eclipse.rdf4j.sail.memory.MemoryStore;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

public class MultiParamTest {
	private static final String NAMESPACE = "http://example.org/";
	private static final String PREFIXES = joinLines(
			"PREFIX search: <http://www.openrdf.org/contrib/lucenesail#>",
			"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>",
			"PREFIX ex: <" + NAMESPACE + ">");

	private static IRI iri(String name) {
		return Values.iri(NAMESPACE + name);
	}

	private static String joinLines(String... lines) {
		return String.join(" \n", lines);
	}

	private static final IRI elem1 = iri("elem1");
	private static final IRI elem2 = iri("elem2");
	private static final IRI elem3 = iri("elem3");
	private static final IRI elem4 = iri("elem4");
	private static final IRI elem5 = iri("elem5");
	private static final IRI elem6 = iri("elem6");
	private static final IRI elem7 = iri("elem7");

	private static final IRI p1 = iri("p1");
	private static final IRI p2 = iri("p2");
	private static final IRI p3 = iri("p3");

	@Rule
	public TemporaryFolder tmpFolder = new TemporaryFolder();

	LuceneSail luceneSail;
	SailRepository repository;
	SailRepositoryConnection conn;

	@Before
	public void setup() throws IOException {
		MemoryStore memoryStore = new MemoryStore();
		// sail with the ex:text1 filter
		luceneSail = new LuceneSail();
		luceneSail.setParameter(LuceneSail.INDEX_CLASS_KEY, LuceneSail.DEFAULT_INDEX_CLASS);
		luceneSail.setEvaluationMode(TupleFunctionEvaluationMode.NATIVE);
		luceneSail.setBaseSail(memoryStore);
		luceneSail.setDataDir(tmpFolder.newFolder());
		repository = new SailRepository(luceneSail);
		repository.init();

		// add test elements
		conn = repository.getConnection();
		conn.begin();

		conn.add(elem1, p1, literal("aaa"));
		conn.add(elem1, p2, literal("bbb"));
		conn.add(elem1, p3, literal("ccc"));

		conn.add(elem2, p1, literal("aaa"));
		conn.add(elem2, p2, literal("ddd"));
		conn.add(elem2, p3, literal("ccc"));

		conn.add(elem3, p1, literal("ddd"));
		conn.add(elem3, p2, literal("bbb"));
		conn.add(elem3, p3, literal("fff"));

		conn.add(elem4, p1, literal("ddd"));
		conn.add(elem4, p2, literal("ggg"));
		conn.add(elem4, p3, literal("ccc"));

		conn.add(elem5, p1, literal("hhh"));
		conn.add(elem5, p2, literal("eee"));
		conn.add(elem5, p3, literal("aaa"));

		conn.add(elem6, p1, literal("iii zzz yyy"));
		conn.add(elem6, p2, literal("jjj zzz"));
		conn.add(elem6, p3, literal("kkk"));

		conn.add(elem7, p1, literal("iii zzz"));
		conn.add(elem7, p2, literal("jjj zzz yyy"));
		conn.add(elem7, p3, literal("kkk"));

		conn.commit();
	}

	@After
	public void complete() {
		try {
			conn.close();
		} finally {
			repository.shutDown();
		}
	}

	@Test
	public void testPredicateSimple() {
		try (TupleQueryResult result = conn.prepareTupleQuery(joinLines(
				PREFIXES,
				"SELECT * {",
				"  ?subj search:matches [",
				"    search:query \"aaa\"",
				"  ]",
				"}"
		)).evaluate()) {
			Set<String> values = new HashSet<>(Set.of(
					elem1.toString(),
					elem2.toString(),
					elem5.toString()
			));

			while (result.hasNext()) {
				Value next = result.next().getValue("subj");
				assertTrue("unknown value: " + next, values.remove(next.toString()));
			}
			assertTrue("missing value" + values, values.isEmpty());
		}
	}

	@Test
	public void testPredicateMulti() {
		try (TupleQueryResult result = conn.prepareTupleQuery(joinLines(
				PREFIXES,
				"SELECT * {",
				"  ?subj search:matches [",
				"    search:query [ ",
				"       search:query \"aaa\"",
				"    ]",
				"  ]",
				"}"
		)).evaluate()) {
			Set<String> values = new HashSet<>(Set.of(
					elem1.toString(),
					elem2.toString(),
					elem5.toString()
			));

			while (result.hasNext()) {
				Value next = result.next().getValue("subj");
				assertTrue("unknown value: " + next, values.remove(next.toString()));
			}
			assertTrue("missing value" + values, values.isEmpty());
		}
	}

	@Test
	public void testMultiPredicate() {
		try (TupleQueryResult result = conn.prepareTupleQuery(joinLines(
				PREFIXES,
				"SELECT * {",
				"  ?subj search:matches [",
				"    search:query [ ",
				"       search:query \"aaa\" ;",
				"       search:property ex:p1",
				"    ]",
				"  ]",
				"}"
		)).evaluate()) {
			Set<String> values = new HashSet<>(Set.of(
					elem1.toString(),
					elem2.toString()
			));

			while (result.hasNext()) {
				Value next = result.next().getValue("subj");
				assertTrue("unknown value: " + next, values.remove(next.toString()));
			}
			assertTrue("missing value" + values, values.isEmpty());
		}
	}

	@Test
	public void testMultiQuery() {
		try (TupleQueryResult result = conn.prepareTupleQuery(joinLines(
				PREFIXES,
				"SELECT * {",
				"  ?subj search:matches [",
				"    search:query",
				"    [",
				"       search:query \"aaa\" ;",
				"       search:property ex:p1",
				"    ] , [",
				"       search:query \"bbb\" ;",
				"       search:property ex:p2",
				"    ]",
				"  ]",
				"}"
		)).evaluate()) {
			Set<String> values = new HashSet<>(Set.of(
					elem1.toString(),
					elem2.toString(),
					elem3.toString()
			));

			while (result.hasNext()) {
				BindingSet binding = result.next();
				Value next = binding.getValue("subj");
				assertTrue("unknown value: " + next, values.remove(next.toString()));
			}
			assertTrue("missing value" + values, values.isEmpty());
		}
	}

	@Test
	public void testMultiSnippetQuery() {
		try (TupleQueryResult result = conn.prepareTupleQuery(joinLines(
				PREFIXES,
				"SELECT * {",
				"  ?subj search:matches [",
				"    search:query",
				"    [",
				"       search:query \"aaa\" ;",
				"       search:property ex:p1 ;",
				"       search:snippet ?sp1 ;",
				"    ] , [",
				"       search:query \"bbb\" ;",
				"       search:property ex:p2 ;",
				"       search:snippet ?sp2 ;",
				"    ]",
				"  ]",
				"}"
		)).evaluate()) {
			Set<String> values = new HashSet<>(Set.of(
					elem1 + ":\"<B>aaa</B>\":\"<B>bbb</B>\"",
					elem2 + ":\"<B>aaa</B>\":null",
					elem3 + ":null:\"<B>bbb</B>\""
			));

			while (result.hasNext()) {
				BindingSet bindings = result.next();
				Value next = bindings.getValue("subj");
				Value snippet1 = bindings.getValue("sp1");
				Value snippet2 = bindings.getValue("sp2");
				String obj = next + ":" + snippet1 + ":" + snippet2;
				assertTrue("unknown value: " + obj, values.remove(obj));
			}
			assertTrue("missing value" + values, values.isEmpty());
		}
	}

	@Test
	public void testMultiOrderQuery() {
		try (TupleQueryResult result = conn.prepareTupleQuery(joinLines(
				PREFIXES,
				"SELECT * {",
				"  ?subj search:matches [",
				"    search:query",
				"    [",
				"       search:query \"iii\" ;",
				"       search:property ex:p1 ;",
				"       search:boost 0.2 ;",
				"    ] , [",
				"       search:query \"jjj\" ;",
				"       search:property ex:p2 ;",
				"       search:boost 0.8 ;",
				"    ] ;",
				"    search:score ?score",
				"  ]",
				"}"
		)).evaluate()) {
			String[] values = new String[] {
					elem6.toString(),
					elem7.toString()
			};
			Iterator<String> it = Arrays.stream(values).iterator();

			while (result.hasNext()) {
				if (!it.hasNext()) {
					do {
						System.out.println(result.next());
					} while (result.hasNext());
					fail("too many binding");
				}
				BindingSet bindings = result.next();
				String exceptedValue = it.next();
				Value next = bindings.getValue("subj");
				assertEquals(exceptedValue, next.toString());
			}
			if (it.hasNext()) {
				do {
					System.out.println(it.next());
				} while (it.hasNext());
				fail();
			}
		}
		try (TupleQueryResult result = conn.prepareTupleQuery(joinLines(
				PREFIXES,
				"SELECT * {",
				"  ?subj search:matches [",
				"    search:query",
				"    [",
				"       search:query \"iii\" ;",
				"       search:property ex:p1 ;",
				"       search:boost 0.8 ;",
				"    ] , [",
				"       search:query \"jjj\" ;",
				"       search:property ex:p2 ;",
				"       search:boost 0.2 ;",
				"    ] ;",
				"    search:score ?score",
				"  ]",
				"}"
		)).evaluate()) {
			String[] values = new String[] {
					elem7.toString(),
					elem6.toString()
			};
			Iterator<String> it = Arrays.stream(values).iterator();

			while (result.hasNext()) {
				if (!it.hasNext()) {
					do {
						System.out.println(result.next());
					} while (result.hasNext());
					fail("too many binding");
				}
				BindingSet bindings = result.next();
				String exceptedValue = it.next();
				Value next = bindings.getValue("subj");
				assertEquals(exceptedValue, next.toString());
			}
			if (it.hasNext()) {
				do {
					System.out.println(it.next());
				} while (it.hasNext());
				fail();
			}
		}
	}

	@Test
	public void testMultiOrderSnippetQuery() {
		try (TupleQueryResult result = conn.prepareTupleQuery(joinLines(
				PREFIXES,
				"SELECT * {",
				"  ?subj search:matches [",
				"    search:query",
				"    [",
				"       search:query \"iii\" ;",
				"       search:property ex:p1 ;",
				"       search:boost 0.2 ;",
				"       search:snippet ?sp1 ;",
				"    ] , [",
				"       search:query \"jjj\" ;",
				"       search:property ex:p2 ;",
				"       search:boost 0.8 ;",
				"       search:snippet ?sp2 ;",
				"    ] ;",
				"    search:score ?score",
				"  ]",
				"}"
		)).evaluate()) {
			String[] values = new String[] {
					elem6 + ":<B>iii</B> zzz yyy:<B>jjj</B> zzz",
					elem7 + ":<B>iii</B> zzz:<B>jjj</B> zzz yyy"
			};
			Iterator<String> it = Arrays.stream(values).iterator();

			while (result.hasNext()) {
				if (!it.hasNext()) {
					do {
						System.out.println(result.next());
					} while (result.hasNext());
					fail("too many binding");
				}
				String exceptedValue = it.next();
				BindingSet bindings = result.next();
				Value snippetValue1 = bindings.getValue("sp1");
				String snippet1 = snippetValue1 == null ? "" : snippetValue1.stringValue();
				Value snippetValue2 = bindings.getValue("sp2");
				String snippet2 = snippetValue2 == null ? "" : snippetValue2.stringValue();
				Value next = bindings.getValue("subj");
				String actualValue = next + ":" + snippet1 + ":" + snippet2;
				assertEquals(exceptedValue, actualValue);
			}
			if (it.hasNext()) {
				do {
					System.out.println(it.next());
				} while (it.hasNext());
				fail();
			}
		}
		try (TupleQueryResult result = conn.prepareTupleQuery(joinLines(
				PREFIXES,
				"SELECT * {",
				"  ?subj search:matches [",
				"    search:query",
				"    [",
				"       search:query \"iii\" ;",
				"       search:property ex:p1 ;",
				"       search:boost 0.8 ;",
				"       search:snippet ?sp1 ;",
				"    ] , [",
				"       search:query \"jjj\" ;",
				"       search:property ex:p2 ;",
				"       search:boost 0.2 ;",
				"       search:snippet ?sp2 ;",
				"    ] ;",
				"    search:score ?score",
				"  ]",
				"}"
		)).evaluate()) {
			String[] values = new String[] {
					elem7 + ":<B>iii</B> zzz:<B>jjj</B> zzz yyy",
					elem6 + ":<B>iii</B> zzz yyy:<B>jjj</B> zzz"
			};
			Iterator<String> it = Arrays.stream(values).iterator();

			while (result.hasNext()) {
				if (!it.hasNext()) {
					do {
						System.out.println(result.next());
					} while (result.hasNext());
					fail("too many binding");
				}
				BindingSet bindings = result.next();
				String exceptedValue = it.next();
				Value snippetValue1 = bindings.getValue("sp1");
				String snippet1 = snippetValue1 == null ? "" : snippetValue1.stringValue();
				Value snippetValue2 = bindings.getValue("sp2");
				String snippet2 = snippetValue2 == null ? "" : snippetValue2.stringValue();
				Value next = bindings.getValue("subj");
				String actualValue = next + ":" + snippet1 + ":" + snippet2;
				assertEquals(exceptedValue, actualValue);
			}
			if (it.hasNext()) {
				do {
					System.out.println(it.next());
				} while (it.hasNext());
				fail();
			}
		}
	}
}