diff --git a/qendpoint-backend/pom.xml b/qendpoint-backend/pom.xml
index 6b75e3315..2686d2fbe 100644
--- a/qendpoint-backend/pom.xml
+++ b/qendpoint-backend/pom.xml
@@ -46,7 +46,7 @@
5.0.2
3.4.0
1.5.6
-
+ 2.11.0
UTF-8
UTF-8
@@ -112,6 +112,11 @@
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
commons-codec
diff --git a/qendpoint-backend/src/main/java/com/the_qa_company/qendpoint/controller/EndpointController.java b/qendpoint-backend/src/main/java/com/the_qa_company/qendpoint/controller/EndpointController.java
index e36497c18..09a2d6677 100644
--- a/qendpoint-backend/src/main/java/com/the_qa_company/qendpoint/controller/EndpointController.java
+++ b/qendpoint-backend/src/main/java/com/the_qa_company/qendpoint/controller/EndpointController.java
@@ -41,7 +41,7 @@ public record FormatReturn(String query) {}
@Autowired
Sparql sparql;
- @RequestMapping(value = "/sparql")
+ @RequestMapping(value = "/sparql", method = { RequestMethod.GET, RequestMethod.POST })
public void sparqlEndpoint(@RequestParam(value = "query", required = false) final String query,
@RequestParam(value = "update", required = false) final String updateQuery,
@RequestParam(value = "format", defaultValue = "json") final String format,
diff --git a/qendpoint-backend/src/main/java/com/the_qa_company/qendpoint/controller/Sparql.java b/qendpoint-backend/src/main/java/com/the_qa_company/qendpoint/controller/Sparql.java
index 7b25ce0ca..fd572ac06 100644
--- a/qendpoint-backend/src/main/java/com/the_qa_company/qendpoint/controller/Sparql.java
+++ b/qendpoint-backend/src/main/java/com/the_qa_company/qendpoint/controller/Sparql.java
@@ -10,8 +10,12 @@
import com.the_qa_company.qendpoint.store.EndpointFiles;
import com.the_qa_company.qendpoint.store.EndpointStore;
import com.the_qa_company.qendpoint.store.EndpointStoreUtils;
+import com.the_qa_company.qendpoint.store.HDTProps;
import com.the_qa_company.qendpoint.utils.FileUtils;
import com.the_qa_company.qendpoint.utils.RDFStreamUtils;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
+import org.apache.lucene.index.IndexReader;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.repository.RepositoryConnection;
@@ -28,14 +32,14 @@
import com.the_qa_company.qendpoint.core.util.io.CloseSuppressPath;
import com.the_qa_company.qendpoint.core.util.io.IOUtil;
import org.eclipse.rdf4j.sail.lucene.LuceneSail;
+import org.eclipse.rdf4j.sail.lucene.SearchIndex;
+import org.eclipse.rdf4j.sail.lucene.impl.LuceneIndex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebInputException;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -53,6 +57,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -342,6 +347,46 @@ void initializeEndpointStore(boolean finishLoading) throws IOException {
CompiledSailOptions opt = sparqlRepository.getOptions();
port = opt.getPort();
}
+
+ if (endpoint != null) {
+ HDTProps props = endpoint.getHdtProps();
+ long bnCount = props.getEndBlankObjects() - props.getStartBlankObjects() // obj
+ + props.getEndBlankShared() - props.getStartBlankShared() // shared
+ + props.getEndBlankSubjects() - props.getStartBlankSubjects(); // subj
+ long literals = props.getEndLiteral() - props.getStartLiteral();
+ logger.info("Index props: Lit:{} bn:{}", literals, bnCount);
+ }
+ Set lcs = sparqlRepository.getLuceneSails();
+ if (!lcs.isEmpty()) {
+ logger.info("Lucene sails ({})", lcs.size());
+
+ final int maxCount = 5;
+ Iterator it = lcs.iterator();
+ for (int i = 0; i < Math.min(maxCount, lcs.size()); i++) {
+ if (!it.hasNext())
+ break;
+ LuceneSail lc = it.next();
+
+ String id = lc.getParameter(LuceneSail.INDEX_ID);
+ if (id == null || id.isEmpty())
+ id = "";
+ SearchIndex lcIdx = lc.getLuceneIndex();
+ String infoStr = lcIdx.getClass().getSimpleName();
+ if (lcIdx instanceof LuceneIndex li) {
+ IndexReader reader = li.getIndexReader();
+ int numDocs = reader.numDocs();
+ infoStr += " numDocs*Fields:" + numDocs + "*" + li.getIndexWriter().getFieldNames().size();
+ } else {
+ infoStr += " no data"; // add ES/Solr??
+ }
+ logger.info("{} {}", id, infoStr);
+ }
+ if (lcs.size() > maxCount) {
+ logger.info("...");
+ }
+
+ }
+
}
if (finishLoading) {
completeLoading();
diff --git a/qendpoint-core/pom.xml b/qendpoint-core/pom.xml
index d0522ca2a..46dde9507 100644
--- a/qendpoint-core/pom.xml
+++ b/qendpoint-core/pom.xml
@@ -48,7 +48,7 @@
1.5.6
0.9.44
- 4.3.2
+ 4.9.0
1.7.30
UTF-8
@@ -75,7 +75,7 @@
org.apache.commons
commons-compress
- 1.21
+ 1.26.0
org.apache.jena
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/compact/bitmap/AdjacencyList.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/compact/bitmap/AdjacencyList.java
index c60885b40..19bf26e5e 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/compact/bitmap/AdjacencyList.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/compact/bitmap/AdjacencyList.java
@@ -111,7 +111,7 @@ public long countItemsY(long x) {
return last(x) - find(x) + 1;
}
- public long search(long element, long begin, long end) throws NotFoundException {
+ public long search(long element, long begin, long end) {
if (end - begin > 10) {
return binSearch(element, begin, end);
} else {
@@ -134,7 +134,7 @@ public long binSearch(long element, long begin, long end) {
return -1;
}
- public long linSearch(long element, long begin, long end) throws NotFoundException {
+ public long linSearch(long element, long begin, long end) {
while (begin <= end) {
long read = array.get(begin);
if (read == element) {
@@ -142,7 +142,41 @@ public long linSearch(long element, long begin, long end) throws NotFoundExcepti
}
begin++;
}
- throw new NotFoundException();
+ return -1;
+ }
+
+ public long searchLoc(long element, long begin, long end) {
+ if (end - begin > 10) {
+ return binSearchLoc(element, begin, end);
+ } else {
+ return linSearchLoc(element, begin, end);
+ }
+ }
+
+ public long binSearchLoc(long element, long begin, long end) {
+ while (begin <= end) {
+ long mid = (begin + end) / 2;
+ long read = array.get(mid);
+ if (element > read) {
+ begin = mid + 1;
+ } else if (element < read) {
+ end = mid - 1;
+ } else {
+ return mid;
+ }
+ }
+ return -(1 + begin);
+ }
+
+ public long linSearchLoc(long element, long begin, long end) {
+ while (begin <= end) {
+ long read = array.get(begin);
+ if (read == element) {
+ return begin;
+ }
+ begin++;
+ }
+ return -(1 + begin);
}
public final long get(long pos) {
@@ -237,4 +271,11 @@ public void dump() {
System.out.println();
}
+ public Sequence getArray() {
+ return array;
+ }
+
+ public Bitmap getBitmap() {
+ return bitmap;
+ }
}
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/dictionary/Dictionary.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/dictionary/Dictionary.java
index 1d4ce246c..ef183d57f 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/dictionary/Dictionary.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/dictionary/Dictionary.java
@@ -21,6 +21,7 @@
import com.the_qa_company.qendpoint.core.enums.RDFNodeType;
import com.the_qa_company.qendpoint.core.enums.TripleComponentRole;
import com.the_qa_company.qendpoint.core.header.Header;
+import com.the_qa_company.qendpoint.core.quad.QuadString;
import com.the_qa_company.qendpoint.core.triples.TripleID;
import com.the_qa_company.qendpoint.core.triples.TripleString;
@@ -265,4 +266,16 @@ default TripleID toTripleId(TripleString tsstr) {
}
return tid;
}
+
+ default TripleString toTripleString(TripleID tssid) {
+ if (tssid.isQuad()) {
+ return new QuadString(idToString(tssid.getSubject(), TripleComponentRole.SUBJECT),
+ idToString(tssid.getPredicate(), TripleComponentRole.PREDICATE),
+ idToString(tssid.getObject(), TripleComponentRole.OBJECT),
+ idToString(tssid.getGraph(), TripleComponentRole.GRAPH));
+ }
+ return new TripleString(idToString(tssid.getSubject(), TripleComponentRole.SUBJECT),
+ idToString(tssid.getPredicate(), TripleComponentRole.PREDICATE),
+ idToString(tssid.getObject(), TripleComponentRole.OBJECT));
+ }
}
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/hdt/HDTManagerImpl.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/hdt/HDTManagerImpl.java
index 492421d91..cdb3a156a 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/hdt/HDTManagerImpl.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/hdt/HDTManagerImpl.java
@@ -1,5 +1,6 @@
package com.the_qa_company.qendpoint.core.hdt;
+import com.the_qa_company.qendpoint.core.compact.integer.VByte;
import com.the_qa_company.qendpoint.core.enums.CompressionType;
import com.the_qa_company.qendpoint.core.enums.RDFNotation;
import com.the_qa_company.qendpoint.core.exceptions.NotFoundException;
@@ -35,11 +36,17 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
@@ -49,6 +56,7 @@
public class HDTManagerImpl extends HDTManager {
private static final Logger logger = LoggerFactory.getLogger(HDTManagerImpl.class);
+ private static final long HDT_DL_INFO_MAGIC = 0x4f464e4c44544448L;
@Override
public HDTOptions doReadOptions(String file) throws IOException {
@@ -177,6 +185,15 @@ private HDTResult generateChecksumAfter(long checksum, Path checksumPath, HDTOpt
public HDTResult doGenerateHDT(String rdfFileName, String baseURI, RDFNotation rdfNotation, HDTOptions spec,
ProgressListener listener) throws IOException, ParserException {
// choose the importer
+ long waitTimeStart = spec.getInt(HDTOptionsKeys.LOADER_WAIT_START, 0);
+ if (waitTimeStart > 0) {
+ logger.info("Waiting {}ms before start...", waitTimeStart);
+ try {
+ Thread.sleep(waitTimeStart);
+ } catch (InterruptedException ignore) {
+ }
+ logger.info("Done waiting");
+ }
String loaderType = spec.get(HDTOptionsKeys.LOADER_TYPE_KEY);
TempHDTImporter loader;
boolean isQuad = rdfNotation == RDFNotation.NQUAD;
@@ -224,6 +241,9 @@ public HDTResult doGenerateHDT(String rdfFileName, String baseURI, RDFNotation r
} else {
try {
preSize = Files.size(preDownload);
+ if (preSize == trueSize) {
+ break;
+ }
} catch (IOException ignore) {
preSize = 0;
}
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/SequentialSearchIteratorTripleID.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/SequentialSearchIteratorTripleID.java
index feb76d276..46de10431 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/SequentialSearchIteratorTripleID.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/SequentialSearchIteratorTripleID.java
@@ -97,52 +97,6 @@ public TripleID next() {
return returnTriple;
}
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#hasPrevious()
- */
- @Override
- public boolean hasPrevious() {
- return hasPreviousTriples;
- }
-
- private void doFetchPrevious() {
- hasPreviousTriples = false;
-
- while (iterator.hasPrevious()) {
- TripleID previous = iterator.previous();
-
- if (previous.match(pattern)) {
- hasPreviousTriples = true;
- hasMoreTriples = true;
- previousTriple.assign(previous);
- previousPosition = iterator.getLastTriplePositionSupplier();
- break;
- }
- }
- }
-
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#previous()
- */
- @Override
- public TripleID previous() {
- if (goingUp) {
- goingUp = false;
- if (hasMoreTriples) {
- doFetchPrevious();
- }
- doFetchPrevious();
- }
- returnTriple.assign(previousTriple);
- lastPosition = previousPosition;
-
- doFetchPrevious();
-
- return returnTriple;
- }
-
/*
* (non-Javadoc)
* @see hdt.iterator.IteratorTripleID#goToStart()
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/utils/GraphFilteringTripleId.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/utils/GraphFilteringTripleId.java
index 04185bac8..3894787a9 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/utils/GraphFilteringTripleId.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/utils/GraphFilteringTripleId.java
@@ -16,16 +16,6 @@ public GraphFilteringTripleId(IteratorTripleID iterator, long[] graphIds) {
this.graphIds = graphIds;
}
- @Override
- public boolean hasPrevious() {
- throw new NotImplementedException();
- }
-
- @Override
- public TripleID previous() {
- throw new NotImplementedException();
- }
-
@Override
public void goToStart() {
throw new NotImplementedException();
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/utils/ListTripleIDIterator.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/utils/ListTripleIDIterator.java
index b07b9c972..926b223e6 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/utils/ListTripleIDIterator.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/iterator/utils/ListTripleIDIterator.java
@@ -42,26 +42,6 @@ public TripleID next() {
return triplesList.get(pos++);
}
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#hasPrevious()
- */
- @Override
- public boolean hasPrevious() {
- return pos > 0;
- }
-
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#previous()
- */
- @Override
- public TripleID previous() {
- TripleID tripleID = triplesList.get(--pos);
- lastPosition = pos;
- return tripleID;
- }
-
/*
* (non-Javadoc)
* @see hdt.iterator.IteratorTripleID#goToStart()
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/merge/HDTMergeJoinIterator.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/merge/HDTMergeJoinIterator.java
new file mode 100644
index 000000000..e5affa3e4
--- /dev/null
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/merge/HDTMergeJoinIterator.java
@@ -0,0 +1,154 @@
+package com.the_qa_company.qendpoint.core.merge;
+
+import com.the_qa_company.qendpoint.core.enums.TripleComponentOrder;
+import com.the_qa_company.qendpoint.core.enums.TripleComponentRole;
+import com.the_qa_company.qendpoint.core.iterator.utils.FetcherIterator;
+import com.the_qa_company.qendpoint.core.triples.IteratorTripleID;
+import com.the_qa_company.qendpoint.core.triples.TripleID;
+
+import java.util.List;
+
+public class HDTMergeJoinIterator extends FetcherIterator> {
+ public static final class MergeIteratorData {
+ private final IteratorTripleID it;
+ private final TripleComponentRole role;
+ private TripleID last;
+ private boolean loaded;
+
+ public MergeIteratorData(IteratorTripleID it, TripleComponentRole role) {
+ this.it = it;
+ this.role = role;
+ }
+
+ public long getSeekLayer(TripleID id) {
+ return switch (role) {
+ case OBJECT -> id.getObject();
+ case PREDICATE -> id.getPredicate();
+ case SUBJECT -> id.getSubject();
+ case GRAPH -> id.getGraph();
+ };
+ }
+
+ /**
+ * goto a layer
+ *
+ * @param id layer
+ * @return if we reach the end
+ */
+ public boolean gotoLayer(long id) {
+ while (hasNext()) {
+ if (getSeekLayer(last) >= id) {
+ return false; // good layer or after
+ }
+ next(); // force next
+ }
+ return true;
+ }
+
+ public boolean hasNext() {
+ if (loaded) {
+ return true;
+ }
+ if (!it.hasNext()) {
+ return false;
+ }
+
+ last = it.next();
+ loaded = true;
+ return true;
+ }
+
+ public TripleID peek() {
+ if (hasNext()) {
+ return last;
+ }
+ return null;
+ }
+
+ public TripleID next() {
+ if (hasNext()) {
+ loaded = false;
+ return last;
+ }
+ return null;
+ }
+ }
+
+ private final List iterators;
+ private boolean loaded;
+
+ public HDTMergeJoinIterator(List iterators) {
+ this.iterators = iterators;
+ }
+
+ private void moveNext() {
+ if (!loaded) {
+ loaded = true;
+ return; // start
+ }
+
+ int minIdx = 0;
+ if (!iterators.get(minIdx).hasNext()) {
+ return;
+ }
+ TripleID minVal = iterators.get(minIdx).peek();
+ TripleComponentOrder minOrder = iterators.get(minIdx).it.getOrder();
+
+ for (int i = 1; i < iterators.size(); i++) {
+ MergeIteratorData d = iterators.get(i);
+ if (!d.hasNext()) {
+ return;
+ }
+ TripleID peek = d.peek();
+
+ if (peek == null) {
+ return; // end
+ }
+
+ TripleComponentOrder ord = d.it.getOrder();
+ if (peek.compareTo(minVal, ord, minOrder) < 0) {
+ minVal = peek;
+ minOrder = ord;
+ minIdx = i;
+ }
+ }
+
+ // move to next using this iterator
+ iterators.get(minIdx).next();
+ }
+
+ private boolean seekAll() {
+ MergeIteratorData it1 = iterators.get(0);
+ if (!it1.hasNext()) {
+ return false; // no data
+ }
+ long seek = it1.getSeekLayer(it1.peek());
+ for (int i = 1; i < iterators.size(); i++) {
+ MergeIteratorData d = iterators.get(i);
+
+ if (d.gotoLayer(seek)) {
+ return false; // too far
+ }
+
+ long seekNext = d.getSeekLayer(d.peek());
+
+ if (seekNext != seek) {
+ seek = seekNext;
+ i = -1; // to compensate i++
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ protected List getNext() {
+ moveNext();
+ if (!seekAll())
+ return null;
+
+ // all the iterators are peeked with the same layer, we can read
+ return iterators;
+ }
+
+}
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/merge/HDTMergeJoinPreparer.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/merge/HDTMergeJoinPreparer.java
new file mode 100644
index 000000000..7fc6352bd
--- /dev/null
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/merge/HDTMergeJoinPreparer.java
@@ -0,0 +1,177 @@
+package com.the_qa_company.qendpoint.core.merge;
+
+import com.the_qa_company.qendpoint.core.enums.TripleComponentOrder;
+import com.the_qa_company.qendpoint.core.enums.TripleComponentRole;
+import com.the_qa_company.qendpoint.core.exceptions.NotImplementedException;
+import com.the_qa_company.qendpoint.core.exceptions.ParserException;
+import com.the_qa_company.qendpoint.core.hdt.HDT;
+import com.the_qa_company.qendpoint.core.hdt.HDTManager;
+import com.the_qa_company.qendpoint.core.listener.ProgressListener;
+import com.the_qa_company.qendpoint.core.options.HDTOptions;
+import com.the_qa_company.qendpoint.core.quad.QuadString;
+import com.the_qa_company.qendpoint.core.triples.TripleID;
+import com.the_qa_company.qendpoint.core.triples.TripleString;
+import com.the_qa_company.qendpoint.core.util.CommonUtils;
+import com.the_qa_company.qendpoint.core.util.StopWatch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HDTMergeJoinPreparer {
+ private static final Logger logger = LoggerFactory.getLogger(HDTMergeJoinPreparer.class);
+ private final HDT hdt;
+ private final List patterns = new ArrayList<>();
+ private int keyIds;
+
+ public HDTMergeJoinPreparer(HDT hdt) {
+ this.hdt = hdt;
+ }
+
+ public long createVar() {
+ return -(++keyIds);
+ }
+
+ public void addPattern(TripleID tid) {
+ patterns.add(tid);
+ }
+
+ public void addPattern(long s, long p, long o) {
+ addPattern(new TripleID(s, p, o));
+ }
+
+ public List buildIteration() {
+ List lst = new ArrayList<>();
+
+ if (keyIds == 0) {
+ // no var
+ throw new NotImplementedException("No variable");// TODO:
+ }
+ int[] occSH = new int[keyIds];
+ int[] occP = new int[keyIds];
+
+ for (TripleID patt : patterns) {
+ long pp = patt.getPredicate();
+ if (pp < 0) {
+ occP[1 - (int) pp]++;
+ }
+
+ long ss = patt.getSubject();
+ long oo = patt.getObject();
+
+ if (ss < 0) {
+ occSH[1 - (int) ss]++;
+ }
+ if (oo < 0) {
+ if (ss != oo) { // avoid double var
+ occSH[1 - (int) oo]++;
+ }
+ }
+ }
+
+ int maxShIdx = CommonUtils.maxArg(occSH);
+ int maxPrIdx = CommonUtils.maxArg(occP);
+
+ if (maxShIdx == 0 && maxPrIdx == 0) {
+ // no var
+ throw new NotImplementedException("No variable");// TODO:
+ }
+
+ // fixme: we should also check if all the sub graphs are connected
+
+ if (maxShIdx > maxPrIdx) {
+ // load shared var
+ } else {
+ // load
+
+ }
+
+ return lst;
+ }
+
+ public static void main(String[] args) throws IOException, ParserException {
+ if (args.length < 2) {
+ logger.error("Missing param: Usage [hdt] [desc]");
+ return;
+ }
+ logger.info("Test preparer");
+ String hdtPath = args[0];
+
+ record TPData(TripleString ts, TripleComponentRole role, TripleComponentOrder order) {}
+
+ List data = new ArrayList<>();
+
+ try (BufferedReader r = Files.newBufferedReader(Path.of(args[1]))) {
+ String line;
+
+ while ((line = r.readLine()) != null) {
+ if (line.isEmpty() || line.charAt(0) == '#')
+ continue; // comment
+
+ QuadString ts = new QuadString();
+ ts.read(line, true);
+
+ if (ts.getSubject().equals("var"))
+ ts.setSubject(null);
+ if (ts.getPredicate().equals("var"))
+ ts.setPredicate(null);
+ if (ts.getObject().equals("var"))
+ ts.setObject(null);
+
+ logger.info("read {}", ts);
+
+ String orderCfg = ts.getGraph().toString();
+
+ if (orderCfg.isEmpty()) {
+ logger.error("Invalid role cfg: empty");
+ return;
+ }
+ String[] cfg = orderCfg.split(":");
+ TripleComponentRole role = TripleComponentRole.valueOf(cfg[0]);
+ TripleComponentOrder order = TripleComponentOrder.valueOf(cfg[1]);
+ ts.setGraph(null);
+
+ data.add(new TPData(new TripleString(ts), role, order));
+ }
+ }
+
+ logger.info("Config loaded");
+ data.forEach(c -> logger.info("- {}", c));
+ logger.info("Loading HDT for query");
+ HDTOptions spec = HDTOptions.of("bitmaptriples.index.allowOldOthers", true);
+ try (HDT hdt = HDTManager.mapIndexedHDT(hdtPath, spec, ProgressListener.sout())) {
+ List mergeData = new ArrayList<>();
+ for (TPData tpd : data) {
+ TripleID tid = hdt.getDictionary().toTripleId(tpd.ts());
+
+ if (tid.isNoMatch()) {
+ logger.error("Triple {} is invalid", tpd.ts());
+ return;
+ }
+
+ mergeData.add(new HDTMergeJoinIterator.MergeIteratorData(hdt.getTriples().search(tid, tpd.order().mask),
+ tpd.role()));
+ }
+
+ HDTMergeJoinIterator it = new HDTMergeJoinIterator(mergeData);
+
+ logger.info("results:");
+ StopWatch sw = new StopWatch();
+ sw.reset();
+ long ret = 0;
+ while (it.hasNext()) {
+ it.next();
+ ret++;
+ // logger.info("- {}", it.next());
+ }
+ logger.info("Done in {} {}", sw.stopAndShow(), ret);
+ }
+ logger.info("Unmapped");
+ }
+
+}
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/options/HDTOptionsKeys.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/options/HDTOptionsKeys.java
index 6b61e551b..73a2c799b 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/options/HDTOptionsKeys.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/options/HDTOptionsKeys.java
@@ -303,6 +303,12 @@ public class HDTOptionsKeys {
*/
@Key(type = Key.Type.ENUM, desc = "loading type for HDTCat / HDTDiff")
public static final String LOAD_HDT_TYPE_KEY = "loader.hdt.type";
+
+ /**
+ * Add time before starting the indexing, in ms, default 0
+ */
+ @Key(type = Key.Type.NUMBER, desc = "Add time before starting the indexing, in ms")
+ public static final String LOADER_WAIT_START = "loader.waitStart";
/**
* load the HDT file into memory
*/
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/quad/impl/BitmapTriplesIteratorGraph.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/quad/impl/BitmapTriplesIteratorGraph.java
index aa296be65..8fbcd1efb 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/quad/impl/BitmapTriplesIteratorGraph.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/quad/impl/BitmapTriplesIteratorGraph.java
@@ -64,16 +64,6 @@ protected TripleID getNext() {
}
}
- @Override
- public boolean hasPrevious() {
- throw new NotImplementedException();
- }
-
- @Override
- public TripleID previous() {
- throw new NotImplementedException();
- }
-
@Override
public void goToStart() {
tidIt.goToStart();
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/quad/impl/BitmapTriplesIteratorGraphG.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/quad/impl/BitmapTriplesIteratorGraphG.java
index b6f73594c..0ef4a97fd 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/quad/impl/BitmapTriplesIteratorGraphG.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/quad/impl/BitmapTriplesIteratorGraphG.java
@@ -49,16 +49,6 @@ protected TripleID getNext() {
return tripleID;
}
- @Override
- public boolean hasPrevious() {
- throw new NotImplementedException();
- }
-
- @Override
- public TripleID previous() {
- throw new NotImplementedException();
- }
-
@Override
public void goToStart() {
posZ = -1;
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/IteratorTripleID.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/IteratorTripleID.java
index 828f58c78..0fe04b126 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/IteratorTripleID.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/IteratorTripleID.java
@@ -21,6 +21,7 @@
import com.the_qa_company.qendpoint.core.enums.ResultEstimationType;
import com.the_qa_company.qendpoint.core.enums.TripleComponentOrder;
+import com.the_qa_company.qendpoint.core.exceptions.NotImplementedException;
import java.util.Iterator;
@@ -29,21 +30,6 @@
*/
public interface IteratorTripleID extends Iterator {
- /**
- * Whether the iterator has previous elements.
- *
- * @return boolean
- */
- boolean hasPrevious();
-
- /**
- * Get the previous element. Call only if hasPrevious() returns true. It
- * moves the cursor of the Iterator to the previous entry.
- *
- * @return TripleID
- */
- TripleID previous();
-
/**
* Point the cursor to the first element of the data structure.
*/
@@ -103,4 +89,61 @@ public interface IteratorTripleID extends Iterator {
default boolean isLastTriplePositionBoundToOrder() {
return false;
}
+
+ /**
+ * goto the next subject >= id
+ *
+ * @param id id
+ * @return true if the next subject == id
+ * @see #canGoToSubject() if can goto returns false, this function is not
+ * available
+ */
+ default boolean gotoSubject(long id) {
+ return false;
+ }
+
+ /**
+ * goto the next predicate >= id
+ *
+ * @param id id
+ * @return true if the next predicate == id
+ * @see #canGoToPredicate() if can goto returns false, this function is not
+ * available
+ */
+ default boolean gotoPredicate(long id) {
+ return false;
+ }
+
+ /**
+ * goto the next object >= id
+ *
+ * @param id id
+ * @return true if the next object == id
+ * @see #canGoToObject() if can goto returns false, this function is not
+ * available
+ */
+ default boolean gotoObject(long id) {
+ return false;
+ }
+
+ /**
+ * @return true if {@link #gotoSubject(long)} can be used, false otherwise
+ */
+ default boolean canGoToSubject() {
+ return false;
+ }
+
+ /**
+ * @return true if {@link #gotoPredicate(long)} can be used, false otherwise
+ */
+ default boolean canGoToPredicate() {
+ return false;
+ }
+
+ /**
+ * @return true if {@link #gotoObject(long)} can be used, false otherwise
+ */
+ default boolean canGoToObject() {
+ return false;
+ }
}
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/TripleID.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/TripleID.java
index 4e3f2de68..923697f85 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/TripleID.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/TripleID.java
@@ -19,6 +19,8 @@
package com.the_qa_company.qendpoint.core.triples;
+import com.the_qa_company.qendpoint.core.enums.TripleComponentOrder;
+import com.the_qa_company.qendpoint.core.enums.TripleComponentRole;
import com.the_qa_company.qendpoint.core.util.LongCompare;
import java.io.Serial;
@@ -238,6 +240,45 @@ public int compareTo(TripleID other) {
}
}
+ /**
+ * get a component value from its role
+ *
+ * @param role role
+ * @return component value
+ */
+ public long get(TripleComponentRole role) {
+ return switch (role) {
+ case SUBJECT -> getSubject();
+ case PREDICATE -> getPredicate();
+ case OBJECT -> getObject();
+ case GRAPH -> getGraph();
+ };
+ }
+
+ /**
+ * compare this triple id with another triple id using order remap
+ *
+ * @param other other triple id
+ * @param orderThis order of this triple id
+ * @param orderOther order of the other triple id
+ * @return compare result
+ */
+ public int compareTo(TripleID other, TripleComponentOrder orderThis, TripleComponentOrder orderOther) {
+ int result = LongCompare.compare(get(orderThis.getSubjectMapping()), other.get(orderOther.getSubjectMapping()));
+
+ if (result != 0) {
+ return result;
+ }
+
+ result = LongCompare.compare(get(orderThis.getPredicateMapping()), other.get(orderOther.getPredicateMapping()));
+
+ if (result != 0) {
+ return result;
+ }
+
+ return LongCompare.compare(get(orderThis.getObjectMapping()), other.get(orderOther.getObjectMapping()));
+ }
+
/**
* Check whether this triple matches a pattern of TripleID. 0 acts as a
* wildcard
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriples.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriples.java
index 16dd2b141..81bed8188 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriples.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriples.java
@@ -114,7 +114,7 @@ public class BitmapTriples implements TriplesPrivate, BitmapTriplesIndex {
protected boolean isClosed;
public BitmapTriples() throws IOException {
- this(new HDTSpecification());
+ this(HDTOptions.empty());
}
public BitmapTriples(HDTOptions spec) throws IOException {
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIterator.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIterator.java
index 248a09234..d5b465d18 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIterator.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIterator.java
@@ -21,6 +21,8 @@
import com.the_qa_company.qendpoint.core.enums.ResultEstimationType;
import com.the_qa_company.qendpoint.core.enums.TripleComponentOrder;
+import com.the_qa_company.qendpoint.core.enums.TripleComponentRole;
+import com.the_qa_company.qendpoint.core.exceptions.NotImplementedException;
import com.the_qa_company.qendpoint.core.iterator.SuppliableIteratorTripleID;
import com.the_qa_company.qendpoint.core.triples.TripleID;
import com.the_qa_company.qendpoint.core.compact.bitmap.AdjacencyList;
@@ -168,37 +170,6 @@ public TripleID next() {
return returnTriple;
}
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#hasPrevious()
- */
- @Override
- public boolean hasPrevious() {
- return posZ > minZ;
- }
-
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#previous()
- */
- @Override
- public TripleID previous() {
- posZ--;
-
- posY = adjZ.findListIndex(posZ);
-
- z = adjZ.get(posZ);
- y = adjY.get(posY);
- x = adjY.findListIndex(posY) + 1;
-
- nextY = adjY.last(x - 1) + 1;
- nextZ = adjZ.last(posY) + 1;
-
- updateOutput();
-
- return returnTriple;
- }
-
/*
* (non-Javadoc)
* @see hdt.iterator.IteratorTripleID#goToStart()
@@ -298,4 +269,171 @@ public long getLastTriplePosition() {
public boolean isLastTriplePositionBoundToOrder() {
return true;
}
+
+ private boolean gotoOrder(long id, TripleComponentRole role) {
+ switch (role) {
+ case SUBJECT -> {
+ if (patX != 0) {
+ return id == patX; // can't jump or already on the right element
+ }
+
+ if (x >= id) {
+ return id == x;
+ }
+
+ x = id;
+ posY = adjY.find(x - 1);
+ posZ = adjZ.find(posY);
+ y = adjY.get(posY);
+ nextY = adjY.last(x - 1) + 1;
+ nextZ = adjZ.find(posY + 1);
+
+ return true; // we know x exists
+ }
+ case PREDICATE -> {
+ if (patY != 0) {
+ return id == patY; // can't jump or already on the right element
+ }
+
+ if (posY == nextY) {
+ return false; // no next element
+ }
+
+ long curr = this.adjY.get(posY);
+
+ if (curr >= id) {
+ return curr == id;
+ }
+
+ boolean res;
+ if (posY + 1 == nextY) {
+ // no next element, go next X
+ x++;
+ posY = nextY;
+ nextY = adjY.findNext(posY) + 1;
+ y = adjY.get(posY);
+
+ res = false;
+ } else {
+ long last = this.adjY.get(nextY - 1);
+
+ if (last > id) {
+ // binary search between curr <-> last id
+ long loc = this.adjY.searchLoc(id, posY + 1, nextY - 2);
+
+ if (loc > 0) {
+ res = true;
+ posY = loc;
+ y = id;
+ } else {
+ res = false;
+ posY = -loc - 1;
+ y = adjY.get(posY);
+ }
+ } else {
+ if (last != id) {
+ // last < id - GOTO end + 1
+ posY = nextY;
+ res = false;
+ } else {
+ // last == id - GOTO last
+ posY = nextY - 1;
+ y = adjY.get(posY);
+ res = true;
+ }
+ nextY = adjY.findNext(posY) + 1;
+ }
+ }
+
+ // down to z/posZ/nextZ?
+ posZ = adjZ.find(posY); // assert patZ != 0
+ nextZ = adjZ.findNext(posZ) + 1;
+
+ return res;
+ }
+ case OBJECT -> {
+ if (patZ != 0) {
+ return id == patZ; // can't jump or already on the right element
+ }
+
+ if (posZ == nextZ) {
+ return false; // no next element
+ }
+
+ long curr = this.adjZ.get(posZ);
+
+ if (curr >= id) {
+ return curr == id;
+ }
+ if (posZ + 1 == nextZ) {
+ return false; // no next element
+ }
+
+ long last = this.adjZ.get(nextZ - 1);
+
+ boolean res;
+
+ if (last > id) {
+ // binary search between curr <-> last id
+ long loc = this.adjZ.searchLoc(id, posZ + 1, nextZ - 2);
+
+ if (loc >= 0) { // match
+ res = true;
+ posZ = loc;
+ // z = id; // no need to compute the z, it is only used in
+ // next()
+ } else {
+ res = false;
+ posZ = -loc - 1;
+ // z = adjZ.get(posZ);
+ }
+ } else if (last != id) {
+ // last < id - GOTO end
+ posZ = nextZ;
+ res = false;
+ } else {
+ // last == id - GOTO last
+ posZ = nextZ - 1;
+ // z = adjZ.get(posZ);
+ res = true;
+ }
+
+ nextZ = adjZ.findNext(posZ) + 1;
+
+ return res;
+ }
+ default -> throw new NotImplementedException("goto " + role);
+ }
+ }
+
+ @Override
+ public boolean gotoSubject(long id) {
+ return gotoOrder(id, idx.getOrder().getSubjectMapping());
+ }
+
+ @Override
+ public boolean gotoPredicate(long id) {
+ return gotoOrder(id, idx.getOrder().getPredicateMapping());
+ }
+
+ @Override
+ public boolean gotoObject(long id) {
+ return gotoOrder(id, idx.getOrder().getObjectMapping());
+ }
+
+ @Override
+ public boolean canGoToSubject() {
+ return true;
+ }
+
+ @Override
+ public boolean canGoToPredicate() {
+ return true;
+ }
+
+ @Override
+ public boolean canGoToObject() {
+ return true;
+ }
+
}
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorCat.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorCat.java
index 28a84759c..0ae1e60b4 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorCat.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorCat.java
@@ -55,16 +55,6 @@ public BitmapTriplesIteratorCat(Triples hdt1, Triples hdt2, DictionaryCat dictio
}
- @Override
- public boolean hasPrevious() {
- return false;
- }
-
- @Override
- public TripleID previous() {
- return null;
- }
-
@Override
public void goToStart() {
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorMapDiff.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorMapDiff.java
index a85ee0610..b7d602804 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorMapDiff.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorMapDiff.java
@@ -45,16 +45,6 @@ public BitmapTriplesIteratorMapDiff(HDT hdtOriginal, Bitmap deleteBitmap, Dictio
count++;
}
- @Override
- public boolean hasPrevious() {
- return false;
- }
-
- @Override
- public TripleID previous() {
- return null;
- }
-
@Override
public void goToStart() {
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorY.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorY.java
index 71e7fe5ce..c7ce2bf98 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorY.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorY.java
@@ -111,37 +111,6 @@ public TripleID next() {
* (non-Javadoc)
* @see hdt.iterator.IteratorTripleID#hasPrevious()
*/
- @Override
- public boolean hasPrevious() {
- return prevY != -1 || posZ >= prevZ;
- }
-
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#previous()
- */
- @Override
- public TripleID previous() {
- if (posZ <= prevZ) {
- nextY = posY;
- posY = prevY;
- prevY = adjY.findPreviousAppearance(prevY - 1, patY);
-
- posZ = prevZ = adjZ.find(posY);
- nextZ = adjZ.last(posY);
-
- x = adjY.findListIndex(posY) + 1;
- y = adjY.get(posY);
- z = adjZ.get(posZ);
- } else {
- posZ--;
- z = adjZ.get(posZ);
- }
-
- updateOutput();
-
- return returnTriple;
- }
/*
* (non-Javadoc)
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorYFOQ.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorYFOQ.java
index 726389578..dbbfea499 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorYFOQ.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorYFOQ.java
@@ -112,41 +112,6 @@ public TripleID next() {
return returnTriple;
}
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#hasPrevious()
- */
- @Override
- public boolean hasPrevious() {
- return numOccurrence > 1 || posZ >= prevZ;
- }
-
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#previous()
- */
- @Override
- public TripleID previous() {
- if (posZ <= prevZ) {
- numOccurrence--;
- posY = triples.predicateIndex.getOccurrence(predBase, numOccurrence);
-
- prevZ = adjZ.find(posY);
- posZ = nextZ = adjZ.last(posY);
-
- x = adjY.findListIndex(posY) + 1;
- y = adjY.get(posY);
- z = adjZ.get(posZ);
- } else {
- z = adjZ.get(posZ);
- posZ--;
- }
-
- updateOutput();
-
- return returnTriple;
- }
-
/*
* (non-Javadoc)
* @see hdt.iterator.IteratorTripleID#goToStart()
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorZ.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorZ.java
index 91be651aa..670a59210 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorZ.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorZ.java
@@ -94,24 +94,6 @@ public TripleID next() {
return returnTriple;
}
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#hasPrevious()
- */
- @Override
- public boolean hasPrevious() {
- throw new NotImplementedException();
- }
-
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#previous()
- */
- @Override
- public TripleID previous() {
- throw new NotImplementedException();
- }
-
/*
* (non-Javadoc)
* @see hdt.iterator.IteratorTripleID#goToStart()
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorZFOQ.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorZFOQ.java
index 51d74f537..b1ae4ae10 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorZFOQ.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorZFOQ.java
@@ -159,33 +159,6 @@ public TripleID next() {
return returnTriple;
}
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#hasPrevious()
- */
- @Override
- public boolean hasPrevious() {
- return posIndex > minIndex;
- }
-
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#previous()
- */
- @Override
- public TripleID previous() {
- posIndex--;
-
- long posY = adjIndex.get(posIndex);
-
- z = patZ != 0 ? patZ : adjIndex.findListIndex(posIndex) + 1;
- y = patY != 0 ? patY : adjY.get(posY);
- x = adjY.findListIndex(posY) + 1;
-
- updateOutput();
- return returnTriple;
- }
-
/*
* (non-Javadoc)
* @see hdt.iterator.IteratorTripleID#goToStart()
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/EmptyTriplesIterator.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/EmptyTriplesIterator.java
index fc71a1bd5..6eef2b30d 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/EmptyTriplesIterator.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/EmptyTriplesIterator.java
@@ -24,16 +24,6 @@ public TripleID next() {
throw new NoSuchElementException();
}
- @Override
- public boolean hasPrevious() {
- return false;
- }
-
- @Override
- public TripleID previous() {
- throw new NoSuchElementException();
- }
-
@Override
public void goToStart() {
// Do nothing
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/OneReadTempTriples.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/OneReadTempTriples.java
index 96c8f7381..029a65cd5 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/OneReadTempTriples.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/OneReadTempTriples.java
@@ -233,16 +233,6 @@ public SimpleIteratorTripleID(Iterator it, TripleComponentOrder order,
this.tripleCount = tripleCount;
}
- @Override
- public boolean hasPrevious() {
- throw new NotImplementedException();
- }
-
- @Override
- public TripleID previous() {
- throw new NotImplementedException();
- }
-
@Override
public void goToStart() {
throw new NotImplementedException();
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/TriplesList.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/TriplesList.java
index 9f77fb77f..4b6f522e2 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/TriplesList.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/TriplesList.java
@@ -448,25 +448,6 @@ public TripleID next() {
return triplesList.arrayOfTriples.get(pos++).asTripleID();
}
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#hasPrevious()
- */
- @Override
- public boolean hasPrevious() {
- return pos > 0;
- }
-
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#previous()
- */
- @Override
- public TripleID previous() {
- lastPosition = --pos;
- return triplesList.arrayOfTriples.get(pos).asTripleID();
- }
-
/*
* (non-Javadoc)
* @see hdt.iterator.IteratorTripleID#goToStart()
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/TriplesListLong.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/TriplesListLong.java
index 3897a9722..c9157b277 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/TriplesListLong.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/triples/impl/TriplesListLong.java
@@ -440,25 +440,6 @@ public TripleID next() {
return triplesList.arrayOfTriples.get(pos++);
}
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#hasPrevious()
- */
- @Override
- public boolean hasPrevious() {
- return pos > 0;
- }
-
- /*
- * (non-Javadoc)
- * @see hdt.iterator.IteratorTripleID#previous()
- */
- @Override
- public TripleID previous() {
- lastPosition = --pos;
- return triplesList.arrayOfTriples.get(pos);
- }
-
/*
* (non-Javadoc)
* @see hdt.iterator.IteratorTripleID#goToStart()
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/CommonUtils.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/CommonUtils.java
new file mode 100644
index 000000000..5824cbf97
--- /dev/null
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/CommonUtils.java
@@ -0,0 +1,39 @@
+package com.the_qa_company.qendpoint.core.util;
+
+public class CommonUtils {
+ public static int minArg(int[] array) {
+ if (array.length < 2) {
+ return 0;
+ }
+ int minIdx = 0;
+ int minVal = array[0];
+ for (int i = 1; i < array.length; i++) {
+ if (array[i] < minVal) {
+ minVal = array[i];
+ minIdx = i;
+ }
+ }
+
+ return minIdx;
+ }
+
+ public static int maxArg(int[] array) {
+ if (array.length < 2) {
+ return 0;
+ }
+ int maxIdx = 0;
+ int maxVal = array[0];
+ for (int i = 1; i < array.length; i++) {
+ if (array[i] > maxVal) {
+ maxVal = array[i];
+ maxIdx = i;
+ }
+ }
+
+ return maxIdx;
+ }
+
+ private CommonUtils() {
+ throw new RuntimeException();
+ };
+}
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/StopWatch.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/StopWatch.java
index 46fc58a77..41d0843e1 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/StopWatch.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/StopWatch.java
@@ -35,6 +35,10 @@ public long getMeasure() {
return end - ini;
}
+ public long getMeasureMillis() {
+ return (end - ini) / 1_000_000;
+ }
+
public long stopAndGet() {
stop();
return getMeasure();
diff --git a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/io/compress/NoDuplicateTripleIDIterator.java b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/io/compress/NoDuplicateTripleIDIterator.java
index 50c7806f4..b6c75e976 100644
--- a/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/io/compress/NoDuplicateTripleIDIterator.java
+++ b/qendpoint-core/src/main/java/com/the_qa_company/qendpoint/core/util/io/compress/NoDuplicateTripleIDIterator.java
@@ -49,16 +49,6 @@ public TripleID next() {
return next;
}
- @Override
- public boolean hasPrevious() {
- throw new NotImplementedException();
- }
-
- @Override
- public TripleID previous() {
- throw new NotImplementedException();
- }
-
@Override
public void goToStart() {
throw new NotImplementedException();
diff --git a/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/merge/HDTMergeJoinIteratorTest.java b/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/merge/HDTMergeJoinIteratorTest.java
new file mode 100644
index 000000000..e6e0b43da
--- /dev/null
+++ b/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/merge/HDTMergeJoinIteratorTest.java
@@ -0,0 +1,112 @@
+package com.the_qa_company.qendpoint.core.merge;
+
+import com.the_qa_company.qendpoint.core.dictionary.Dictionary;
+import com.the_qa_company.qendpoint.core.enums.RDFNotation;
+import com.the_qa_company.qendpoint.core.enums.TripleComponentOrder;
+import com.the_qa_company.qendpoint.core.enums.TripleComponentRole;
+import com.the_qa_company.qendpoint.core.exceptions.NotFoundException;
+import com.the_qa_company.qendpoint.core.exceptions.ParserException;
+import com.the_qa_company.qendpoint.core.hdt.HDT;
+import com.the_qa_company.qendpoint.core.hdt.HDTManager;
+import com.the_qa_company.qendpoint.core.iterator.SuppliableIteratorTripleID;
+import com.the_qa_company.qendpoint.core.listener.ProgressListener;
+import com.the_qa_company.qendpoint.core.options.HDTOptions;
+import com.the_qa_company.qendpoint.core.options.HDTOptionsKeys;
+import com.the_qa_company.qendpoint.core.triples.TripleID;
+import com.the_qa_company.qendpoint.core.triples.impl.BitmapTriplesIndexFile;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class HDTMergeJoinIteratorTest {
+
+ @Rule
+ public TemporaryFolder tempDir = TemporaryFolder.builder().assureDeletion().build();
+
+ private InputStream getStream(String filename) {
+ InputStream is = getClass().getResourceAsStream(filename);
+ Assert.assertNotNull("can't find file " + filename, is);
+ return is;
+ }
+
+ @Test
+ @Ignore("wip")
+ public void itTest() throws IOException, ParserException, NotFoundException {
+ Path root = tempDir.newFolder().toPath();
+
+ Path hdtPath = root.resolve("test.hdt");
+ HDTOptions spec = HDTOptions.of(HDTOptionsKeys.LOADER_TYPE_KEY, HDTOptionsKeys.LOADER_TYPE_VALUE_DISK,
+ HDTOptionsKeys.LOADER_DISK_FUTURE_HDT_LOCATION_KEY, hdtPath, HDTOptionsKeys.LOADER_DISK_LOCATION_KEY,
+ root.resolve("gd"), HDTOptionsKeys.DICTIONARY_TYPE_KEY,
+ HDTOptionsKeys.DICTIONARY_TYPE_VALUE_MULTI_OBJECTS_LANG, HDTOptionsKeys.BITMAPTRIPLES_INDEX_METHOD_KEY,
+ HDTOptionsKeys.BITMAPTRIPLES_INDEX_METHOD_VALUE_DISK, HDTOptionsKeys.BITMAPTRIPLES_INDEX_NO_FOQ, true,
+ // all indexes
+ HDTOptionsKeys.BITMAPTRIPLES_INDEX_OTHERS, Arrays.stream(TripleComponentOrder.values())
+ .map(TripleComponentOrder::name).collect(Collectors.joining(",")));
+ ProgressListener listener = ProgressListener.ignore();
+ String ns = "http://example.org/#";
+ try (InputStream is = getStream("/merge_ds.ttl");
+ HDT hdt = HDTManager.generateHDT(is, ns, RDFNotation.TURTLE, spec, listener)) {
+ hdt.saveToHDT(hdtPath);
+ }
+
+ try (HDT hdt = HDTManager.mapIndexedHDT(hdtPath, spec, listener)) {
+ // test index creation
+ assertTrue(Files.exists(BitmapTriplesIndexFile.getIndexPath(hdtPath, TripleComponentOrder.OPS)));
+ assertTrue(Files.exists(BitmapTriplesIndexFile.getIndexPath(hdtPath, TripleComponentOrder.POS)));
+ assertTrue(Files.exists(BitmapTriplesIndexFile.getIndexPath(hdtPath, TripleComponentOrder.PSO)));
+
+ /*
+ * The query is ~that: SELECT * { ?s ex:relative ?o ?o rdfs:name ?n
+ * ?o ex:id ?id }
+ */
+
+ Dictionary dict = hdt.getDictionary();
+ long exRelative = dict.stringToId(ns + "relative", TripleComponentRole.PREDICATE);
+ long rdfsName = dict.stringToId("http://www.w3.org/2000/01/rdf-schema#name", TripleComponentRole.PREDICATE);
+ long exId = dict.stringToId(ns + "id", TripleComponentRole.PREDICATE);
+
+ TripleID p1 = new TripleID(0, exRelative, 0);
+ TripleID p2 = new TripleID(0, rdfsName, 0);
+ TripleID p3 = new TripleID(0, exId, 0);
+
+ assertFalse(p1 + " empty", p1.isEmpty());
+ assertFalse(p2 + " empty", p2.isEmpty());
+ assertFalse(p3 + " empty", p3.isEmpty());
+
+ SuppliableIteratorTripleID it1 = hdt.getTriples().search(p1, TripleComponentOrder.POS.mask);
+ SuppliableIteratorTripleID it2 = hdt.getTriples().search(p2, TripleComponentOrder.PSO.mask);
+ SuppliableIteratorTripleID it3 = hdt.getTriples().search(p3, TripleComponentOrder.PSO.mask);
+
+ assertSame("invalid order ", TripleComponentOrder.POS, it1.getOrder());
+ assertSame("invalid order ", TripleComponentOrder.PSO, it2.getOrder());
+ assertSame("invalid order ", TripleComponentOrder.PSO, it3.getOrder());
+
+ HDTMergeJoinIterator it = new HDTMergeJoinIterator(
+ List.of(new HDTMergeJoinIterator.MergeIteratorData(it1, TripleComponentRole.OBJECT),
+ new HDTMergeJoinIterator.MergeIteratorData(it2, TripleComponentRole.SUBJECT),
+ new HDTMergeJoinIterator.MergeIteratorData(it3, TripleComponentRole.SUBJECT)));
+
+ System.out.println(it.hasNext());
+ it.forEachRemaining(lst -> System.out
+ .println(lst.stream().map(d -> dict.toTripleString(Objects.requireNonNull(d.peek())).toString())
+ .collect(Collectors.joining(" - "))));
+ }
+
+ }
+}
diff --git a/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapQuadTriplesTest.java b/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapQuadTriplesTest.java
index aeeb40c51..6d18a20a1 100644
--- a/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapQuadTriplesTest.java
+++ b/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapQuadTriplesTest.java
@@ -34,16 +34,6 @@ private static IteratorTripleID fromList(List lst) {
private int current;
private int lastLoc;
- @Override
- public boolean hasPrevious() {
- return false;
- }
-
- @Override
- public TripleID previous() {
- return null;
- }
-
@Override
public void goToStart() {
current = 0;
diff --git a/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorTest.java b/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorTest.java
index 706376573..2a13014fd 100644
--- a/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorTest.java
+++ b/qendpoint-core/src/test/java/com/the_qa_company/qendpoint/core/triples/impl/BitmapTriplesIteratorTest.java
@@ -1,26 +1,473 @@
package com.the_qa_company.qendpoint.core.triples.impl;
-import java.io.IOException;
-
+import com.the_qa_company.qendpoint.core.enums.RDFNotation;
+import com.the_qa_company.qendpoint.core.enums.TripleComponentOrder;
+import com.the_qa_company.qendpoint.core.exceptions.ParserException;
+import com.the_qa_company.qendpoint.core.hdt.HDT;
+import com.the_qa_company.qendpoint.core.hdt.HDTManager;
+import com.the_qa_company.qendpoint.core.listener.ProgressListener;
+import com.the_qa_company.qendpoint.core.options.HDTOptions;
+import com.the_qa_company.qendpoint.core.options.HDTOptionsKeys;
+import com.the_qa_company.qendpoint.core.triples.IteratorTripleID;
+import com.the_qa_company.qendpoint.core.triples.TripleID;
+import com.the_qa_company.qendpoint.core.util.LargeFakeDataSetStreamSupplier;
+import org.apache.commons.io.file.PathUtils;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class BitmapTriplesIteratorTest {
+ @Rule
+ public TemporaryFolder tempDir = TemporaryFolder.builder().assureDeletion().build();
+
@Before
public void setUp() throws Exception {
}
@Test
- public void test() throws IOException {
-// HDT hdt = HDTManager.mapHDT("/Users/mck/hdt/swdf.hdt", null);
-//
-// int t = (int) hdt.getTriples().getNumberOfElements();
-// BitmapTriplesIterator it = new BitmapTriplesIterator((BitmapTriples) hdt.getTriples(), t-10, t);
-//
-// while(it.hasNext()) {
-// System.out.println(it.next());
-// }
+ public void jumpTest() throws IOException, ParserException {
+ Path root = tempDir.newFolder().toPath();
+
+ try {
+ LargeFakeDataSetStreamSupplier sup = LargeFakeDataSetStreamSupplier.createSupplierWithMaxTriples(1000, 32);
+ Path hdtPath = root.resolve("test.hdt");
+ sup.createAndSaveFakeHDT(HDTOptions.empty(), hdtPath);
+
+ try (HDT hdt = HDTManager.mapIndexedHDT(hdtPath)) {
+ IteratorTripleID it = hdt.getTriples().searchAll();
+
+ assertTrue("bad class: " + it.getClass(), it instanceof BitmapTriplesIterator);
+
+ TripleID start = it.next().clone();
+
+ for (int i = 0; i < 458; i++) {
+ assertTrue(it.hasNext());
+ it.next();
+ }
+
+ TripleID lastTest = it.next().clone();
+
+ assertNotEquals(start, lastTest);
+
+ long posLast = it.getLastTriplePosition();
+
+ it.goToStart();
+ assertEquals(start, it.next());
+
+ assertEquals(0, it.getLastTriplePosition());
+
+ it.goTo(posLast);
+
+ assertEquals(lastTest, it.next());
+
+ assertEquals(posLast, it.getLastTriplePosition());
+ }
+ } finally {
+ PathUtils.deleteDirectory(root);
+ }
+
+ }
+
+ private static final String JUMP_XYZ_DATASET = """
+ @prefix ex: .
+
+ ex:s1 ex:p1 ex:o0000, ex:o0001, ex:o0002, ex:o0003, ex:o0004, ex:o0005 ;
+ ex:p2 ex:o0000, ex:o0002, ex:o0003, ex:o0004, ex:o0005 ;
+ ex:p3 ex:o0000, ex:o0001, ex:o0002, ex:o0003, ex:o0004, ex:o0005 ;
+ ex:p4 ex:o0000, ex:o0001, ex:o0002, ex:o0004, ex:o0005 ;
+ ex:p5 ex:o0000, ex:o0001, ex:o0002, ex:o0003, ex:o0004, ex:o0005 .
+
+
+ ex:s2 ex:p1 ex:o0006, ex:o0007, ex:o0008, ex:o0009, ex:o0010, ex:o0011 ;
+ ex:p2 ex:o0008, ex:o0009, ex:o0010, ex:o0011 ;
+ ex:p3 ex:o0007, ex:o0008, ex:o0009, ex:o0010, ex:o0011 ;
+ ex:p4 ex:o0006, ex:o0007, ex:o0008, ex:o0009, ex:o0010, ex:o0011 .
+
+
+ ex:s3 ex:p1 ex:o0003, ex:o0005, ex:o0007, ex:o0009, ex:o0011, ex:o0015 ;
+ ex:p2 ex:o0003, ex:o0005, ex:o0007, ex:o0009, ex:o0011, ex:o0015 ;
+ ex:p3 ex:o0003, ex:o0005, ex:o0007, ex:o0009, ex:o0011, ex:o0015 ;
+ ex:p4 ex:o0003, ex:o0005, ex:o0007 ;
+ ex:p5 ex:o0003, ex:o0005, ex:o0007, ex:o0009, ex:o0011, ex:o0015 ;
+ ex:p6 ex:o0003, ex:o0007, ex:o0009, ex:o0011, ex:o0015 ;
+ ex:p7 ex:o0003, ex:o0005, ex:o0009, ex:o0011, ex:o0015 .
+
+
+ ex:s4 ex:p1 ex:o0003, ex:o0005, ex:o0007, ex:o0009, ex:o0011, ex:o0015 ;
+ ex:p2 ex:o0005, ex:o0007, ex:o0009, ex:o0011, ex:o0015 ;
+ ex:p3 ex:o0003, ex:o0005, ex:o0009, ex:o0011, ex:o0015 ;
+ ex:p4 ex:o0003, ex:o0005, ex:o0007, ex:o0009, ex:o0011, ex:o0015 ;
+ ex:p5 ex:o0003, ex:o0005, ex:o0007, ex:o0009 .
+
+ """;
+ private static final long JUMP_XYZ_DATASET_X = 4;
+ private static final long JUMP_XYZ_DATASET_Y = 4;
+
+ @Test
+ public void jumpXTest() throws IOException, ParserException {
+ Path root = tempDir.newFolder().toPath();
+
+ try {
+
+ Path hdtPath = root.resolve("test.hdt");
+
+ HDTOptions spec = HDTOptions.of(HDTOptionsKeys.BITMAPTRIPLES_INDEX_OTHERS, "spo,sop,pos,pso,ops,osp",
+ HDTOptionsKeys.BITMAPTRIPLES_INDEX_NO_FOQ, true);
+
+ try (HDT hdt = HDTManager.generateHDT(
+ new ByteArrayInputStream(JUMP_XYZ_DATASET.getBytes(StandardCharsets.UTF_8)),
+ LargeFakeDataSetStreamSupplier.BASE_URI, RDFNotation.TURTLE, spec, ProgressListener.ignore())) {
+ hdt.saveToHDT(hdtPath);
+ }
+
+ try (HDT hdt = HDTManager.mapIndexedHDT(hdtPath, spec, ProgressListener.ignore())) {
+
+ IteratorTripleID ittt = hdt.getTriples().searchAll();
+ assertTrue("bad class: " + ittt.getClass(), ittt instanceof BitmapTriplesIterator);
+
+ for (int sid = 1; sid <= JUMP_XYZ_DATASET_X; sid++) {
+ IteratorTripleID it = hdt.getTriples().searchAll();
+ IteratorTripleID itex = hdt.getTriples().searchAll();
+
+ assertTrue(it.gotoSubject(sid));
+
+ assertEquals(sid, it.next().getSubject());
+
+ long s;
+ do {
+ assertTrue(itex.hasNext());
+ s = itex.next().getSubject();
+ } while (s < sid);
+ assertEquals(sid, s);
+
+ assertTrue(it.hasNext());
+ do {
+ TripleID ac = it.next();
+ assertTrue(itex.hasNext());
+ TripleID ex = itex.next();
+ assertEquals(itex.getLastTriplePosition(), it.getLastTriplePosition());
+
+ assertEquals(ex, ac);
+ } while (it.hasNext());
+
+ assertFalse(itex.hasNext());
+ }
+ }
+ } finally {
+ PathUtils.deleteDirectory(root);
+ }
}
+ @Test
+ public void jumpYTest() throws IOException, ParserException {
+ Path root = tempDir.newFolder().toPath();
+
+ try {
+
+ Path hdtPath = root.resolve("test.hdt");
+
+ HDTOptions spec = HDTOptions.of(HDTOptionsKeys.BITMAPTRIPLES_INDEX_OTHERS, "spo,sop,pos,pso,ops,osp",
+ HDTOptionsKeys.BITMAPTRIPLES_INDEX_NO_FOQ, true);
+
+ try (HDT hdt = HDTManager.generateHDT(
+ new ByteArrayInputStream(JUMP_XYZ_DATASET.getBytes(StandardCharsets.UTF_8)),
+ LargeFakeDataSetStreamSupplier.BASE_URI, RDFNotation.TURTLE, spec, ProgressListener.ignore())) {
+ hdt.saveToHDT(hdtPath);
+ }
+
+ try (HDT hdt = HDTManager.mapIndexedHDT(hdtPath, spec, ProgressListener.ignore())) {
+
+ IteratorTripleID ittt = hdt.getTriples().searchAll();
+ assertTrue("bad class: " + ittt.getClass(), ittt instanceof BitmapTriplesIterator);
+
+ String lastPosData;
+
+ for (int sid = 1; sid <= JUMP_XYZ_DATASET_X; sid++) {
+ for (int pid = 1; pid <= JUMP_XYZ_DATASET_Y; pid++) {
+ IteratorTripleID it = hdt.getTriples().searchAll();
+ IteratorTripleID itex = hdt.getTriples().searchAll();
+
+ assertTrue(it.gotoSubject(sid));
+ assertTrue(it.gotoPredicate(pid));
+
+ TripleID next = it.next();
+ lastPosData = "[sid:" + sid + "/pid:" + pid + "][ac:" + it.getLastTriplePosition() + "/ex"
+ + itex.getLastTriplePosition() + "]" + next;
+ assertEquals("invalid pos: " + lastPosData, sid, next.getSubject());
+ assertEquals("invalid pos: " + lastPosData, pid, next.getPredicate());
+
+ long s;
+ long p;
+ do {
+ assertTrue(itex.hasNext());
+ TripleID next1 = itex.next();
+ s = next1.getSubject();
+ p = next1.getPredicate();
+ lastPosData = "[sid:" + sid + "/pid:" + pid + "][ac:" + it.getLastTriplePosition() + "/ex"
+ + itex.getLastTriplePosition() + "]" + next1;
+ } while (s < sid || p < pid);
+ assertEquals(lastPosData, sid, s);
+ assertEquals(lastPosData, pid, p);
+
+ assertTrue(it.hasNext());
+ do {
+ TripleID ac = it.next();
+ assertTrue(itex.hasNext());
+ TripleID ex = itex.next();
+ lastPosData = "[sid:" + sid + "/pid:" + pid + "][ac:" + it.getLastTriplePosition() + "/ex"
+ + itex.getLastTriplePosition() + "]" + ac + "/" + ex;
+ assertEquals(lastPosData, itex.getLastTriplePosition(), it.getLastTriplePosition());
+
+ assertEquals(lastPosData, ex, ac);
+ } while (it.hasNext());
+
+ assertFalse(itex.hasNext());
+ }
+ }
+ }
+ } finally {
+ PathUtils.deleteDirectory(root);
+ }
+
+ }
+
+ @Test
+ public void jumpXYZTest() throws IOException, ParserException {
+ Path root = tempDir.newFolder().toPath();
+
+ try {
+ Path hdtPath = root.resolve("test.hdt");
+
+ HDTOptions spec = HDTOptions.of(HDTOptionsKeys.BITMAPTRIPLES_INDEX_OTHERS, "spo,sop,pos,pso,ops,osp",
+ HDTOptionsKeys.BITMAPTRIPLES_INDEX_NO_FOQ, true);
+ final int count = 100_000;
+ LargeFakeDataSetStreamSupplier supplier = LargeFakeDataSetStreamSupplier
+ .createSupplierWithMaxTriples(count, 567890987).withMaxElementSplit(50).withMaxLiteralSize(20);
+
+ supplier.createAndSaveFakeHDT(spec, hdtPath);
+
+ try (HDT hdt = HDTManager.mapIndexedHDT(hdtPath, spec, ProgressListener.ignore())) {
+ int elements = (int) hdt.getTriples().getNumberOfElements();
+
+ for (int idx = 0; idx < elements; idx++) {
+
+ IteratorTripleID it = hdt.getTriples().searchAll();
+
+ assertTrue(it.canGoTo());
+
+ it.goTo(idx);
+
+ TripleID current = it.next().clone();
+ assertEquals(idx, it.getLastTriplePosition());
+
+ for (int member = 0; member < 3; member++) {
+ IteratorTripleID itac = hdt.getTriples().searchAll(TripleComponentOrder.SPO.mask);
+ assertSame("invalid order (" + member + "/" + idx + ")", itac.getOrder(),
+ TripleComponentOrder.SPO);
+
+ // test subject
+ assertTrue("Can't jump to subject " + current + " (" + member + "/" + idx + ")",
+ itac.canGoToSubject() && itac.gotoSubject(current.getSubject()));
+
+ if (member >= 1) {
+ // test predicate
+ assertTrue("Can't jump to predicate " + current + " (" + member + "/" + idx + ")",
+ itac.canGoToPredicate() && itac.gotoPredicate(current.getPredicate()));
+
+ if (member >= 2) {
+ // test object
+ assertTrue("Can't jump to object " + current + " (" + member + "/" + idx + ")",
+ itac.canGoToObject() && itac.gotoObject(current.getObject()));
+ }
+ }
+
+ assertTrue("for " + current + " (" + member + "/" + idx + ")", itac.hasNext());
+ TripleID next = itac.next();
+ String err = "invalid next " + next + " != " + current + " (" + member + "/" + idx + ")";
+ switch (member) {
+ case 2: // object
+ assertEquals("object err " + err, current.getObject(), next.getObject());
+ case 1: // predicate
+ assertEquals("predicate err " + err, current.getPredicate(), next.getPredicate());
+ case 0: // subject only
+ assertEquals("subject err " + err, current.getSubject(), next.getSubject());
+ break;
+ default:
+ fail("bad member: " + member);
+ break;
+ }
+ if (member == 2) {
+ assertEquals("idx err " + err, idx, itac.getLastTriplePosition());
+ if (itac.hasNext()) {
+ TripleID newCurrent = itac.next();
+ assertTrue("idx err " + err, idx < itac.getLastTriplePosition());
+
+ if (current.getSubject() == newCurrent.getSubject()) {
+ // no jump on X, we should have the sam
+ assertTrue("Can't jump to subject " + current + " (" + member + "/" + idx + ")",
+ itac.gotoSubject(current.getSubject()));
+
+ if (current.getPredicate() == newCurrent.getPredicate()) {
+ // no jump on Y, we should have the same
+ assertTrue("Can't jump to subject " + current + " (" + member + "/" + idx + ")",
+ itac.gotoPredicate(current.getPredicate()));
+
+ assertFalse(
+ "Can't jump to subject " + current + " (" + member + "/" + idx + ")",
+ itac.gotoObject(current.getObject()));
+ } else {
+ assertFalse(
+ "Can't jump to subject " + current + " (" + member + "/" + idx + ")",
+ itac.gotoPredicate(current.getPredicate()));
+ }
+
+ } else {
+ assertFalse("Can't jump to subject " + current + " (" + member + "/" + idx + ")",
+ itac.gotoSubject(current.getSubject()));
+ }
+ }
+
+ } else {
+ assertTrue("idx err " + err, idx >= itac.getLastTriplePosition());
+ }
+ }
+ }
+ }
+ } finally {
+ PathUtils.deleteDirectory(root);
+ }
+ }
+
+ @Test
+ public void jumpXYZNextTest() throws IOException, ParserException {
+ Path root = tempDir.newFolder().toPath();
+
+ try {
+ Path hdtPath = root.resolve("test.hdt");
+
+ HDTOptions spec = HDTOptions.of(HDTOptionsKeys.BITMAPTRIPLES_INDEX_OTHERS, "spo,sop,pos,pso,ops,osp",
+ HDTOptionsKeys.BITMAPTRIPLES_INDEX_NO_FOQ, true);
+ final int count = 10_000;
+ LargeFakeDataSetStreamSupplier supplier = LargeFakeDataSetStreamSupplier
+ .createSupplierWithMaxTriples(count, 567890987).withMaxElementSplit(50).withMaxLiteralSize(20);
+
+ supplier.createAndSaveFakeHDT(spec, hdtPath);
+
+ try (HDT hdt = HDTManager.mapIndexedHDT(hdtPath, spec, ProgressListener.ignore())) {
+ int elements = (int) hdt.getTriples().getNumberOfElements();
+ for (int idx = 0; idx < elements; idx++) {
+
+ IteratorTripleID it = hdt.getTriples().searchAll();
+
+ assertTrue(it.canGoTo());
+
+ it.goTo(idx);
+
+ TripleID current = it.next().clone();
+ assertEquals(idx, it.getLastTriplePosition());
+
+ nextCountLoop:
+ for (int nextCount = 0; nextCount < 10; nextCount++) {
+ for (int member = 1; member < 3; member++) {
+ String memberInfo = " (" + member + "/" + idx + "/" + nextCount + ")";
+ IteratorTripleID itac = hdt.getTriples().searchAll(TripleComponentOrder.SPO.mask);
+ assertSame("invalid order" + memberInfo, itac.getOrder(), TripleComponentOrder.SPO);
+
+ // test subject
+ assertTrue("Can't jump to subject " + current + memberInfo,
+ itac.canGoToSubject() && itac.gotoSubject(current.getSubject()));
+
+ for (int j = 0; j < nextCount; j++) {
+ assertTrue(itac.hasNext());
+ TripleID pvid = itac.next();
+
+ if (itac.getLastTriplePosition() == idx) {
+ assertEquals(pvid, current);
+ break nextCountLoop; // we consumed the one
+ // we were searching
+ // for, it can't be
+ // used
+ }
+ }
+
+ // test predicate
+ assertTrue("Can't jump to predicate " + current + memberInfo,
+ itac.canGoToPredicate() && itac.gotoPredicate(current.getPredicate()));
+
+ if (member >= 2) {
+ // test object
+ assertTrue("Can't jump to object " + current + memberInfo,
+ itac.canGoToObject() && itac.gotoObject(current.getObject()));
+ }
+
+ assertTrue("for " + current + memberInfo, itac.hasNext());
+ TripleID next = itac.next();
+ String err = "invalid next " + next + " != " + current + memberInfo;
+ switch (member) {
+ case 2: // object
+ assertEquals("object err " + err, current.getObject(), next.getObject());
+ case 1: // predicate
+ assertEquals("predicate err " + err, current.getPredicate(), next.getPredicate());
+ case 0: // subject only
+ assertEquals("subject err " + err, current.getSubject(), next.getSubject());
+ break;
+ default:
+ fail("bad member: " + member);
+ break;
+ }
+ if (member == 2) {
+ assertEquals("idx err " + err, idx, itac.getLastTriplePosition());
+ if (itac.hasNext()) {
+ TripleID newCurrent = itac.next();
+ assertTrue("idx err " + err, idx < itac.getLastTriplePosition());
+
+ if (current.getSubject() == newCurrent.getSubject()) {
+ // no jump on X, we should have the sam
+ assertTrue("Can't jump to subject " + current + memberInfo + newCurrent,
+ itac.gotoSubject(current.getSubject()));
+
+ if (current.getPredicate() == newCurrent.getPredicate()) {
+ // no jump on Y, we should have the
+ // same
+ assertTrue("Can't jump to subject " + current + memberInfo + newCurrent,
+ itac.gotoPredicate(current.getPredicate()));
+
+ assertFalse("Can't jump to subject " + current + memberInfo + newCurrent,
+ itac.gotoObject(current.getObject()));
+ } else {
+ assertFalse("Can't jump to subject " + current + memberInfo + newCurrent,
+ itac.gotoPredicate(current.getPredicate()));
+ }
+ } else {
+ assertFalse("Can't jump to subject " + current + memberInfo + newCurrent,
+ itac.gotoSubject(current.getSubject()));
+ }
+ }
+
+ } else {
+ assertTrue("idx err " + err, idx >= itac.getLastTriplePosition());
+ }
+ }
+ }
+
+ }
+ }
+ } finally {
+ PathUtils.deleteDirectory(root);
+ }
+ }
}
diff --git a/qendpoint-core/src/test/resources/merge_ds.ttl b/qendpoint-core/src/test/resources/merge_ds.ttl
new file mode 100644
index 000000000..3d11f1f92
--- /dev/null
+++ b/qendpoint-core/src/test/resources/merge_ds.ttl
@@ -0,0 +1,27 @@
+
+@prefix ex: .
+@prefix rdfs: .
+
+ex:s1 rdfs:name "test" ;
+ ex:relative ex:s2, ex:s3 .
+
+
+ex:s2 rdfs:name "test 2" .
+ex:s3 rdfs:name "test 3" ;
+ ex:relative ex:s4 ;
+ ex:id "id3".
+
+ex:s4 rdfs:name "test 4" ;
+ ex:relative ex:s3 ;
+ ex:id "id42", "id43" .
+
+
+ex:s5 rdfs:name "test 5" ;
+ ex:relative ex:s5 ;
+ ex:id "id51", "id52", "id53".
+
+ex:s6 rdfs:name "test 6" ;
+ ex:relative ex:s5 ;
+ ex:relative ex:s6 ;
+ ex:id "id51".
+
diff --git a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/compiler/CompiledSail.java b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/compiler/CompiledSail.java
index 215ea43e7..96d889e90 100644
--- a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/compiler/CompiledSail.java
+++ b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/compiler/CompiledSail.java
@@ -6,6 +6,7 @@
import com.the_qa_company.qendpoint.core.hdt.HDTManager;
import com.the_qa_company.qendpoint.core.options.HDTOptions;
import com.the_qa_company.qendpoint.core.triples.TripleString;
+import com.the_qa_company.qendpoint.core.util.StopWatch;
import com.the_qa_company.qendpoint.store.EndpointFiles;
import com.the_qa_company.qendpoint.store.EndpointStore;
import com.the_qa_company.qendpoint.store.exception.EndpointStoreException;
@@ -216,6 +217,26 @@ public NotifyingSail getSource() {
return source;
}
+ private void reindexSail(LuceneSail sail) {
+ // bypass filtering system to use the source
+ NotifyingSail oldSail = sail.getBaseSail();
+ try {
+ sail.setBaseSail(source);
+ String indexId = sail.getParameter(LuceneSail.INDEX_ID);
+ if (indexId == null || indexId.isEmpty()) {
+ indexId = "";
+ }
+ StopWatch sw = new StopWatch();
+ sw.reset();
+ logger.info("Reindexing sail {}", indexId);
+ sail.reindex();
+ sw.stop();
+ logger.info("Sail {} reindexed in {} ({}ms)", indexId, sw, sw.getMeasureMillis());
+ } finally {
+ sail.setBaseSail(oldSail);
+ }
+ }
+
/**
* reindex all the compiled lucene sails
*
@@ -225,19 +246,7 @@ public NotifyingSail getSource() {
public void reindexLuceneSails() throws SailException {
for (LuceneSail sail : luceneSails) {
// bypass filtering system to use the source
- NotifyingSail oldSail = sail.getBaseSail();
- try {
- sail.setBaseSail(source);
- String indexId = sail.getParameter(LuceneSail.INDEX_ID);
- if (indexId == null || indexId.isEmpty()) {
- indexId = "no id";
- }
- logger.info("Reindexing sail: {}", indexId);
- sail.reindex();
- } finally {
- sail.setBaseSail(oldSail);
- }
-
+ reindexSail(sail);
}
}
@@ -254,20 +263,7 @@ public void reindexLuceneSail(String index) throws SailException {
if (!index.equals(sail.getParameter(LuceneSail.INDEX_ID))) {
continue; // ignore
}
- // bypass filtering system to use the source
- NotifyingSail oldSail = sail.getBaseSail();
- try {
- sail.setBaseSail(source);
- String indexId = sail.getParameter(LuceneSail.INDEX_ID);
- if (indexId == null || indexId.isEmpty()) {
- indexId = "no id";
- }
- logger.info("Reindexing sail: {}", indexId);
- sail.reindex();
- } finally {
- sail.setBaseSail(oldSail);
- }
-
+ reindexSail(sail);
}
}
diff --git a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStore.java b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStore.java
index 62a7a488d..14341ee5b 100644
--- a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStore.java
+++ b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStore.java
@@ -77,6 +77,7 @@ public class EndpointStore extends AbstractNotifyingSail {
* disable the optimizer
*/
public static final String QUERY_CONFIG_NO_OPTIMIZER = "no_optimizer";
+ public static final String QUERY_CONFIG_NO_OPTIMIZER_MERGE = "no_optimizer_merge";
/**
* get the query plan
*/
diff --git a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStoreConnection.java b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStoreConnection.java
index 62b8fff4b..c9ac865d5 100644
--- a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStoreConnection.java
+++ b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStoreConnection.java
@@ -825,6 +825,10 @@ public void run() {
}
}
+ public EndpointTripleSource getTripleSource() {
+ return tripleSource;
+ }
+
private class EndpointStoreConnectionListener implements SailConnectionListener {
private boolean shouldHandle() {
return !endpoint.isMerging() || !endpoint.isNotificationsFreeze();
diff --git a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStoreQueryPreparer.java b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStoreQueryPreparer.java
index 1ddf27800..74d1e0e1a 100644
--- a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStoreQueryPreparer.java
+++ b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointStoreQueryPreparer.java
@@ -2,6 +2,7 @@
import com.the_qa_company.qendpoint.federation.SPARQLServiceWikibaseLabelResolver;
import com.the_qa_company.qendpoint.federation.ServiceClauseOptimizer;
+import com.the_qa_company.qendpoint.utils.MergeJoinOptimizer;
import com.the_qa_company.qendpoint.utils.VariableToIdSubstitution;
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.query.BindingSet;
@@ -132,6 +133,7 @@ protected CloseableIteration extends BindingSet> evaluate(TupleExpr tupleExpr,
new IterativeEvaluationOptimizer().optimize(tupleExpr, dataset, bindings);
new FilterOptimizer().optimize(tupleExpr, dataset, bindings);
new OrderLimitOptimizer().optimize(tupleExpr, dataset, bindings);
+ new MergeJoinOptimizer(conn).optimize(tupleExpr, dataset, bindings);
}
new ServiceClauseOptimizer().optimize(tupleExpr, dataset, bindings);
diff --git a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointTripleSource.java b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointTripleSource.java
index fb3f40f78..3a8689f77 100644
--- a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointTripleSource.java
+++ b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/store/EndpointTripleSource.java
@@ -52,6 +52,10 @@ public EndpointTripleSource(EndpointStoreConnection endpointStoreConnection, End
this.enableMergeJoin = endpoint.getHDTSpec().getBoolean(EndpointStore.OPTION_QENDPOINT_MERGE_JOIN, false);
}
+ public boolean hasEnableMergeJoin() {
+ return enableMergeJoin;
+ }
+
private void initHDTIndex() {
this.numberOfCurrentTriples = this.endpoint.getHdt().getTriples().getNumberOfElements();
}
diff --git a/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/utils/MergeJoinOptimizer.java b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/utils/MergeJoinOptimizer.java
new file mode 100644
index 000000000..a6eb27e7c
--- /dev/null
+++ b/qendpoint-store/src/main/java/com/the_qa_company/qendpoint/utils/MergeJoinOptimizer.java
@@ -0,0 +1,93 @@
+package com.the_qa_company.qendpoint.utils;
+
+import com.the_qa_company.qendpoint.store.EndpointStore;
+import com.the_qa_company.qendpoint.store.EndpointStoreConnection;
+import org.eclipse.rdf4j.query.BindingSet;
+import org.eclipse.rdf4j.query.Dataset;
+import org.eclipse.rdf4j.query.algebra.Join;
+import org.eclipse.rdf4j.query.algebra.LeftJoin;
+import org.eclipse.rdf4j.query.algebra.StatementPattern;
+import org.eclipse.rdf4j.query.algebra.TupleExpr;
+import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
+import org.eclipse.rdf4j.query.algebra.helpers.AbstractQueryModelVisitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MergeJoinOptimizer implements QueryOptimizer {
+ private final EndpointStoreConnection conn;
+
+ public MergeJoinOptimizer(EndpointStoreConnection conn) {
+ this.conn = conn;
+ }
+
+ @Override
+ public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindingSet) {
+ if (!conn.getTripleSource().hasEnableMergeJoin()
+ || conn.hasConfig(EndpointStore.QUERY_CONFIG_NO_OPTIMIZER_MERGE)) {
+ return; // merge join disabled, ignore
+ }
+
+ ModelVisitor visitor = new ModelVisitor();
+ tupleExpr.visit(visitor);
+
+ }
+
+ protected static class ModelVisitor extends AbstractQueryModelVisitor {
+
+ private boolean getJoinPatterns(Join node, List patterns) {
+ TupleExpr la = node.getLeftArg();
+ if (la instanceof Join laj && getJoinPatterns(laj, patterns)) {
+ return true;
+ }
+ if (!(la instanceof StatementPattern stmt)) {
+ return true;
+ }
+ patterns.add(stmt);
+
+ TupleExpr ra = node.getRightArg();
+ if (ra instanceof Join raj && getJoinPatterns(raj, patterns)) {
+ return true;
+ }
+ if (!(ra instanceof StatementPattern stmt2)) {
+ return true;
+ }
+ patterns.add(stmt2);
+ return false;
+ }
+
+ private List getJoinPatterns(Join node) {
+ List patterns = new ArrayList<>();
+ if (getJoinPatterns(node, patterns)) {
+ return List.of();
+ }
+ return patterns;
+ }
+
+ @Override
+ public void meet(Join node) {
+ // stack the triple patterns
+ TupleExpr la = node.getLeftArg();
+ TupleExpr ra = node.getRightArg();
+
+ List patterns = getJoinPatterns(node);
+
+ if (patterns.isEmpty()) {
+ super.meet(node);
+ return;
+ }
+
+ for (StatementPattern p : patterns) {
+ // TODO: we can replace the patterns
+ p.getObjectVar().hasValue();
+ }
+
+ }
+
+ @Override
+ public void meet(LeftJoin node) {
+ super.meet(node);
+ }
+
+ }
+}