Hi Andy!
08.12.2013 23:09, Andy Seaborne kirjoitti:
That there was material under src/test/java at all. Not all patches
have tests :-(
Right. That was a bit premature though, as in that version I only
tweaked existing tests so they wouldn't break.
However, the attached patch also includes real unit tests for the
graph-aware text index. I grouped the new test code into abstract
classes following the same pattern as existing tests (it took a while to
wrap my brain around the layers of abstract classes!):
* AbstractTestDatasetWithGraphTextIndex (subclass of
AbstractTestDatasetWithTextIndex) contains four working (and one
non-working, see below) unit tests that test the graph-specific
functionality.
* AbstractTestDatasetWithLuceneGraphTextIndex (ugh! subclass of above)
contains the in-memory TDB+Lucene setup required to run the tests.
* Finally, TestDatasetWithLuceneGraphTextIndex is a concrete class
subclassing the above, making sure the tests are actually run.
Thanks to the subclassing, all the existing text index tests (from
AbstractTestDatasetWithTextIndex) are also successfully executed on the
graph-aware index.
In this version I also reworked the constructor changes in
EntityDefinition. Now all the old constructors are preserved (3 old + 2
new) so there is no urgent need to adjust old tests, example code or
documentation.
1/ Blank node graphs - how about using the pseudo URI _:label rather
than use g.getBlankNodeLabel()?
So you mean
String graph = (g.isURI() ) ? g.getURI() : "_:" +
g.getBlankNodeLabel() ;
instead of the current
String graph = (g.isURI() ) ? g.getURI() : g.getBlankNodeLabel() ;
or did I misunderstand?
yes - that should do it if it isn't in the code anywhere else as well.
I implemented it this way. However, I couldn't find a way to really test
bnode-labeled graphs. The last unit test I wrote in
AbstractTestDatasetWithGraphTextIndex tries to parse a TriG snippet and
test with that, but executing the test fails with
"com.hp.hpl.jena.sparql.ARQInternalErrorException:
QueryIterGraphInner.buildIterator". I gave up and commented out the test
method. I may well have made some stupid beginner's mistake here, I'm
not very familiar with using the Jena Java API.
(Sidenote: I think the current jena-text index code also won't
gracefully handle resources with a bnode subject. The
getBlankNodeLabel() result gets stored in the index and is then
considered a URI at query time, though it isn't really and probably
won't match the original triples in the store.)
Upload a TriG file with bnode-labeled graphs.
I tried this from the Fuseki web UI (using attached TriG file) and got this:
10:10:22 INFO [2] POST http://localhost:3030/ds/upload
10:10:22 INFO [2] Upload: Filename: blanknodegraphs.trig,
Content-Type=application/octet-stream, Charset=null => TriG
10:10:22 WARN Only triples or default graph data expected : named graph
data ignored
10:10:22 INFO [2] Upload: Graph: default (2 triple(s))
10:10:22 INFO [2] 200 OK (40 ms)
So only the two default graph triples were actually stored.
The java code is behind the curve - Graph/Node level works, the
Dataset/Resource API does not allow the creation of bNode labeled graphs.
As mentioned above I tried to parse a TriG snippet via Java code in the
unit test. The parsing seemed to work (at least there was no error) but
querying failed.
2/ Did I get it right that the default graph is
Quad.defaultGraphNodeGenerated? Maybe
In my tests the default graph seems to have the URI
"urn:x-arq:DefaultGraph", so it's probably this one from Quad.java:
public static final Node defaultGraphIRI =
NodeFactory.createURI("urn:x-arq:DefaultGraph") ;
Since it's just another URI to the index, indexing works fine here as
well. Though it would make sense to add a unit test for indexing the
default graph just in case.
Oops, sorry, you were right. It's actually defaultGraphNodeGenerated and
it is now handled correctly both at indexing and query time. And there's
a unit test to make sure :)
(I also tried enabling TDB unionDefaultGraph mode but that broke some of
the existing jena-text tests...)
How much of the documentation needs to change? Just another section?
Basically it's just another section for the text-query.html page. Also
the the Configuration by Code section currently shows how to use
EntityDefinition, it needs to be updated with the new constructor
argument.
Where is the documentation kept? Do you take documentation patches as
well or what is the preferred way of contributing to the docs?
It's in SVN
http://svn.apache.org/repos/asf/jena/site/trunk/content/documentation/query/text-query.mdtext
OK, I'll look at documenting this next, if the code looks good to you.
-Osma
--
Osma Suominen
D.Sc. (Tech), Information Systems Specialist
National Library of Finland
P.O. Box 26 (Teollisuuskatu 23)
00014 HELSINGIN YLIOPISTO
Tel. +358 50 3199529
[email protected]
http://www.nationallibrary.fi
Index: src/test/java/org/apache/jena/query/text/TS_Text.java
===================================================================
--- src/test/java/org/apache/jena/query/text/TS_Text.java (revision 1547859)
+++ src/test/java/org/apache/jena/query/text/TS_Text.java (working copy)
@@ -30,6 +30,7 @@
@SuiteClasses({
TestBuildTextDataset.class
, TestDatasetWithLuceneTextIndex.class
+ , TestDatasetWithLuceneGraphTextIndex.class
// Embedded solr not supported
//, TestDatasetWithEmbeddedSolrTextIndex.class
, TestSearchBeforeWriteOnDatasetWithLuceneTextIndex.class
Index: src/test/java/org/apache/jena/query/text/TestDatasetWithLuceneGraphTextIndex.java
===================================================================
--- src/test/java/org/apache/jena/query/text/TestDatasetWithLuceneGraphTextIndex.java (revision 0)
+++ src/test/java/org/apache/jena/query/text/TestDatasetWithLuceneGraphTextIndex.java (revision 0)
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.query.text;
+
+import org.junit.AfterClass ;
+import org.junit.BeforeClass ;
+
+public class TestDatasetWithLuceneGraphTextIndex extends AbstractTestDatasetWithLuceneGraphTextIndex {
+
+ @BeforeClass public static void beforeClass() {
+ init();
+ }
+
+}
Index: src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithLuceneGraphTextIndex.java
===================================================================
--- src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithLuceneGraphTextIndex.java (revision 0)
+++ src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithLuceneGraphTextIndex.java (revision 0)
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.query.text;
+
+import org.apache.lucene.store.Directory ;
+import org.apache.lucene.store.RAMDirectory ;
+
+import com.hp.hpl.jena.query.Dataset ;
+import com.hp.hpl.jena.tdb.TDBFactory ;
+import com.hp.hpl.jena.vocabulary.RDFS ;
+
+
+/**
+ * This abstract class defines a setup configuration for a dataset with a graph-enabled Lucene index.
+ */
+public class AbstractTestDatasetWithLuceneGraphTextIndex extends AbstractTestDatasetWithGraphTextIndex {
+
+ public static void init() {
+ Dataset ds1 = TDBFactory.createDataset() ;
+ Directory dir = new RAMDirectory() ;
+ EntityDefinition eDef = new EntityDefinition("iri", "text", "graph", RDFS.label.asNode()) ;
+ eDef.set("comment", RDFS.comment.asNode()) ; // some tests require indexing rdfs:comment
+ TextIndex tidx = new TextIndexLucene(dir, eDef) ;
+ dataset = TextDatasetFactory.create(ds1, tidx) ;
+ }
+
+}
Index: src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithTextIndex.java
===================================================================
--- src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithTextIndex.java (revision 1547859)
+++ src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithTextIndex.java (working copy)
@@ -40,7 +40,7 @@
* the actual tests.
*/
public abstract class AbstractTestDatasetWithTextIndex {
- private static final String RESOURCE_BASE = "http://example.org/data/resource/";
+ protected static final String RESOURCE_BASE = "http://example.org/data/resource/";
protected static Dataset dataset;
protected static final String QUERY_PROLOG =
StrUtils.strjoinNL(
Index: src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithGraphTextIndex.java
===================================================================
--- src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithGraphTextIndex.java (revision 0)
+++ src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithGraphTextIndex.java (revision 0)
@@ -0,0 +1,190 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.query.text;
+
+
+import java.io.Reader ;
+import java.io.StringReader ;
+import java.util.Arrays ;
+import java.util.HashSet ;
+import java.util.Set ;
+
+import org.apache.jena.atlas.lib.StrUtils ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.junit.Test ;
+
+import com.hp.hpl.jena.query.ReadWrite ;
+import com.hp.hpl.jena.rdf.model.Model ;
+
+
+/**
+ * This abstract class defines tests of the graph-specific indexing.
+ */
+public class AbstractTestDatasetWithGraphTextIndex extends AbstractTestDatasetWithTextIndex {
+
+ private void putTurtleInModel(String turtle, String modelName) {
+ Model model = modelName != null ? dataset.getNamedModel(modelName) : dataset.getDefaultModel() ;
+ Reader reader = new StringReader(turtle) ;
+ dataset.begin(ReadWrite.WRITE) ;
+ model.read(reader, "", "TURTLE") ;
+ dataset.commit();
+ }
+
+ @Test
+ public void testOneSimpleResultInGraph() {
+ final String turtle = StrUtils.strjoinNL(
+ TURTLE_PROLOG,
+ "<" + RESOURCE_BASE + "testOneSimpleResult>",
+ " rdfs:label 'bar testOneSimpleResult barfoo foo'",
+ "."
+ );
+ putTurtleInModel(turtle, "http://example.org/modelA") ;
+ String queryString = StrUtils.strjoinNL(
+ QUERY_PROLOG,
+ "SELECT ?s",
+ "WHERE {",
+ " GRAPH ?g { ?s text:query ( rdfs:label 'testOneSimpleResult' 10 ) . }",
+ "}"
+ );
+ Set<String> expectedURIs = new HashSet<String>() ;
+ expectedURIs.addAll( Arrays.asList(RESOURCE_BASE + "testOneSimpleResult")) ;
+ doTestQuery(dataset, "", queryString, expectedURIs, expectedURIs.size()) ;
+ }
+
+ @Test
+ public void testOneResultTwoGraphs() {
+ final String turtleA = StrUtils.strjoinNL(
+ TURTLE_PROLOG,
+ "<" + RESOURCE_BASE + "testResultInModelA>",
+ " rdfs:label 'bar testOneResult barfoo foo'",
+ "."
+ );
+ putTurtleInModel(turtleA, "http://example.org/modelA") ;
+ final String turtleB = StrUtils.strjoinNL(
+ TURTLE_PROLOG,
+ "<" + RESOURCE_BASE + "testResultInModelB>",
+ " rdfs:label 'bar testOneResult barfoo foo'",
+ "."
+ );
+ putTurtleInModel(turtleB, "http://example.org/modelB") ;
+ String queryString = StrUtils.strjoinNL(
+ QUERY_PROLOG,
+ "SELECT ?s",
+ "WHERE {",
+ " GRAPH <http://example.org/modelA> { ?s text:query ( rdfs:label 'testOneResult' 10 ) . }",
+ "}"
+ );
+ Set<String> expectedURIs = new HashSet<String>() ;
+ expectedURIs.addAll( Arrays.asList(RESOURCE_BASE + "testResultInModelA")) ;
+ doTestQuery(dataset, "", queryString, expectedURIs, expectedURIs.size()) ;
+ }
+
+ @Test
+ public void testORFromGraphs() {
+ final String turtleA = StrUtils.strjoinNL(
+ TURTLE_PROLOG,
+ "<" + RESOURCE_BASE + "testResultOneInModelA>",
+ " rdfs:label 'bar testResultOne barfoo foo'",
+ ".",
+ "<" + RESOURCE_BASE + "testResultTwoInModelA>",
+ " rdfs:label 'bar testResultTwo barfoo foo'",
+ ".",
+ "<" + RESOURCE_BASE + "testResultThreeInModelA>",
+ " rdfs:label 'bar testResultThree barfoo foo'",
+ "."
+ );
+ putTurtleInModel(turtleA, "http://example.org/modelA") ;
+ final String turtleB = StrUtils.strjoinNL(
+ TURTLE_PROLOG,
+ "<" + RESOURCE_BASE + "testResultOneInModelB>",
+ " rdfs:label 'bar testResultOne barfoo foo'",
+ "."
+ );
+ putTurtleInModel(turtleB, "http://example.org/modelB") ;
+ String queryString = StrUtils.strjoinNL(
+ QUERY_PROLOG,
+ "SELECT ?s",
+ "WHERE {",
+ " GRAPH <http://example.org/modelA> { ?s text:query ( rdfs:label 'testResultOne OR testResultTwo' 10 ) . }",
+ "}"
+ );
+ Set<String> expectedURIs = new HashSet<String>() ;
+ expectedURIs.addAll( Arrays.asList(RESOURCE_BASE + "testResultOneInModelA", RESOURCE_BASE + "testResultTwoInModelA")) ;
+ doTestQuery(dataset, "", queryString, expectedURIs, expectedURIs.size()) ;
+ }
+
+ @Test
+ public void testQueryFromDefaultGraph() {
+ final String turtleA = StrUtils.strjoinNL(
+ TURTLE_PROLOG,
+ "<" + RESOURCE_BASE + "testResultInModelA>",
+ " rdfs:label 'bar testOneResult barfoo foo'",
+ "."
+ );
+ putTurtleInModel(turtleA, null) ; // put in default graph
+ final String turtleB = StrUtils.strjoinNL(
+ TURTLE_PROLOG,
+ "<" + RESOURCE_BASE + "testResultInModelB>",
+ " rdfs:label 'bar testOneResult barfoo foo'",
+ "."
+ );
+ putTurtleInModel(turtleB, "http://example.org/modelB") ;
+ String queryString = StrUtils.strjoinNL(
+ QUERY_PROLOG,
+ "SELECT ?s",
+ "WHERE {",
+ " ?s text:query ( rdfs:label 'testOneResult' 10 ) .",
+ "}"
+ );
+ Set<String> expectedURIs = new HashSet<String>() ;
+ expectedURIs.addAll( Arrays.asList(RESOURCE_BASE + "testResultInModelA")) ;
+ doTestQuery(dataset, "", queryString, expectedURIs, expectedURIs.size()) ;
+ }
+
+/* disabled for now - this fails with the following error:
+ com.hp.hpl.jena.sparql.ARQInternalErrorException: QueryIterGraphInner.buildIterator
+
+ @Test
+ public void testBnodeIdentifiedGraph() {
+ final String trig = StrUtils.strjoinNL(
+ TURTLE_PROLOG,
+ "_:graphA {",
+ " <" + RESOURCE_BASE + "testResultInGraphA>",
+ " rdfs:label 'bar testResult barfoo foo' .",
+ "}"
+ );
+ StringReader reader = new StringReader(trig);
+ dataset.begin(ReadWrite.WRITE) ;
+ RDFDataMgr.read(dataset.asDatasetGraph(), reader, "", Lang.TRIG);
+ dataset.commit();
+
+ String queryString = StrUtils.strjoinNL(
+ QUERY_PROLOG,
+ "SELECT ?s",
+ "WHERE {",
+ " GRAPH ?g { ?s text:query ( rdfs:label 'testResult' 10 ) . }",
+ "}"
+ );
+ Set<String> expectedURIs = new HashSet<String>() ;
+ expectedURIs.addAll( Arrays.asList(RESOURCE_BASE + "testResultInGraphA")) ;
+ doTestQuery(dataset, "", queryString, expectedURIs, expectedURIs.size()) ;
+ }
+*/
+}
Index: src/main/java/org/apache/jena/query/text/TextQueryPF.java
===================================================================
--- src/main/java/org/apache/jena/query/text/TextQueryPF.java (revision 1547859)
+++ src/main/java/org/apache/jena/query/text/TextQueryPF.java (working copy)
@@ -32,6 +32,8 @@
import com.hp.hpl.jena.graph.Node ;
import com.hp.hpl.jena.query.QueryBuildException ;
import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+import com.hp.hpl.jena.sparql.core.GraphView ;
+import com.hp.hpl.jena.sparql.core.Quad ;
import com.hp.hpl.jena.sparql.core.Substitute ;
import com.hp.hpl.jena.sparql.core.Var ;
import com.hp.hpl.jena.sparql.engine.ExecutionContext ;
@@ -181,6 +183,18 @@
}
private List<Node> query(String queryString, int limit, ExecutionContext execCxt) {
+ // use the graph information in the text index if possible
+ if (server.getDocDef().getGraphField() != null
+ && execCxt.getActiveGraph() instanceof GraphView) {
+ GraphView activeGraph = (GraphView)execCxt.getActiveGraph() ;
+ if (!Quad.isUnionGraph(activeGraph.getGraphName())) {
+ String uri = activeGraph.getGraphName() != null ? activeGraph.getGraphName().getURI() : Quad.defaultGraphNodeGenerated.getURI() ;
+ String escaped = QueryParser.escape(uri) ;
+ String qs2 = server.getDocDef().getGraphField() + ":" + escaped ;
+ queryString = "(" + queryString + ") AND " + qs2 ;
+ }
+ }
+
Explain.explain(execCxt.getContext(), "Text query: "+queryString) ;
if ( log.isDebugEnabled())
log.debug("Text query: {} ({})", queryString,limit) ;
Index: src/main/java/org/apache/jena/query/text/TextIndexLucene.java
===================================================================
--- src/main/java/org/apache/jena/query/text/TextIndexLucene.java (revision 1547859)
+++ src/main/java/org/apache/jena/query/text/TextIndexLucene.java (working copy)
@@ -23,10 +23,13 @@
import java.util.Map.Entry ;
import org.apache.lucene.analysis.Analyzer ;
+import org.apache.lucene.analysis.core.KeywordAnalyzer ;
+import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper ;
import org.apache.lucene.analysis.standard.StandardAnalyzer ;
import org.apache.lucene.document.Document ;
import org.apache.lucene.document.Field ;
import org.apache.lucene.document.FieldType ;
+import org.apache.lucene.document.StringField ;
import org.apache.lucene.document.TextField ;
import org.apache.lucene.index.DirectoryReader ;
import org.apache.lucene.index.IndexReader ;
@@ -61,6 +64,7 @@
ftIRI.setIndexed(true) ;
ftIRI.freeze() ;
}
+ public static final FieldType ftString = StringField.TYPE_NOT_STORED ;
public static final FieldType ftText = TextField.TYPE_NOT_STORED ;
// Bigger index, easier to debug!
// public static final FieldType ftText = TextField.TYPE_STORED ;
@@ -68,13 +72,21 @@
private final EntityDefinition docDef ;
private final Directory directory ;
private IndexWriter indexWriter ;
- private Analyzer analyzer = new StandardAnalyzer(VER);
+ private Analyzer analyzer ;
public TextIndexLucene(Directory directory, EntityDefinition def)
{
this.directory = directory ;
this.docDef = def ;
+ // create the analyzer as a wrapper that uses KeywordAnalyzer for
+ // entity and graph fields and StandardAnalyzer for all other
+ Map<String,Analyzer> analyzerPerField = new HashMap<String,Analyzer>() ;
+ analyzerPerField.put(def.getEntityField(), new KeywordAnalyzer()) ;
+ if (def.getGraphField() != null)
+ analyzerPerField.put(def.getGraphField(), new KeywordAnalyzer()) ;
+ this.analyzer = new PerFieldAnalyzerWrapper(new StandardAnalyzer(VER), analyzerPerField) ;
+
// force creation of the index if it don't exist
// otherwise if we get a search before data is written we get an exception
startIndexing();
@@ -136,6 +148,13 @@
Document doc = new Document() ;
Field entField = new Field(docDef.getEntityField(), entity.getId(), ftIRI) ;
doc.add(entField) ;
+
+ String graphField = docDef.getGraphField() ;
+ if ( graphField != null )
+ {
+ Field gField = new Field(graphField, entity.getGraph(), ftString) ;
+ doc.add(gField) ;
+ }
for ( Entry<String, Object> e : entity.getMap().entrySet() )
{
Index: src/main/java/org/apache/jena/query/text/Entity.java
===================================================================
--- src/main/java/org/apache/jena/query/text/Entity.java (revision 1547859)
+++ src/main/java/org/apache/jena/query/text/Entity.java (working copy)
@@ -24,12 +24,20 @@
public class Entity
{
private final String id ;
+ private final String graph ;
private final Map<String, Object> map = new HashMap<String, Object>() ;
- public Entity(String entityId) { this.id = entityId ; }
+ public Entity(String entityId, String entityGraph) {
+ this.id = entityId ;
+ this.graph = entityGraph;
+ }
+
+ public Entity(String entityId) { this(entityId, null) ; }
public String getId() { return id ; }
+ public String getGraph() { return graph ; }
+
public void put(String key, Object value)
{ map.put(key, value) ; }
Index: src/main/java/org/apache/jena/query/text/TextIndexSolr.java
===================================================================
--- src/main/java/org/apache/jena/query/text/TextIndexSolr.java (revision 1547859)
+++ src/main/java/org/apache/jena/query/text/TextIndexSolr.java (working copy)
@@ -87,6 +87,13 @@
{
SolrInputDocument doc = new SolrInputDocument() ;
doc.addField(docDef.getEntityField(), entity.getId()) ;
+
+ String graphField = docDef.getGraphField() ;
+ if ( graphField != null )
+ {
+ doc.addField(graphField, entity.getGraph()) ;
+ }
+
// the addition needs to be done as a partial update
// otherwise, if we have multiple fields, each successive
// addition will replace the previous one and we are left
Index: src/main/java/org/apache/jena/query/text/TextDocProducerTriples.java
===================================================================
--- src/main/java/org/apache/jena/query/text/TextDocProducerTriples.java (revision 1547859)
+++ src/main/java/org/apache/jena/query/text/TextDocProducerTriples.java (working copy)
@@ -65,7 +65,8 @@
return ;
String x = (s.isURI() ) ? s.getURI() : s.getBlankNodeLabel() ;
- Entity entity = new Entity(x) ;
+ String graph = (g.isURI() ) ? g.getURI() : "_:" + g.getBlankNodeLabel() ;
+ Entity entity = new Entity(x, graph) ;
if ( ! o.isLiteral() )
{
Index: src/main/java/org/apache/jena/query/text/EntityDefinition.java
===================================================================
--- src/main/java/org/apache/jena/query/text/EntityDefinition.java (revision 1547859)
+++ src/main/java/org/apache/jena/query/text/EntityDefinition.java (working copy)
@@ -39,6 +39,7 @@
// Collections.unmodifiableCollection(fieldToPredicate.keySet()) ;
private final String entityField ;
private final String primaryField ;
+ private final String graphField ;
//private final Node primaryPredicate ;
/**
@@ -48,8 +49,21 @@
* The primary/default field to search
*/
public EntityDefinition(String entityField, String primaryField) {
+ this(entityField, primaryField, (String)null) ;
+ }
+
+ /**
+ * @param entityField
+ * The entity being indexed (e.g. it's URI).
+ * @param primaryField
+ * The primary/default field to search
+ * @param graphField
+ * The field that stores graph URI, or null
+ */
+ public EntityDefinition(String entityField, String primaryField, String graphField) {
this.entityField = entityField ;
this.primaryField = primaryField ;
+ this.graphField = graphField ;
}
/**
@@ -61,7 +75,7 @@
* The property associated with the primary/default field
*/
public EntityDefinition(String entityField, String primaryField, Resource primaryPredicate) {
- this(entityField, primaryField, primaryPredicate.asNode()) ;
+ this(entityField, primaryField, null, primaryPredicate.asNode()) ;
}
/**
@@ -73,7 +87,21 @@
* The property associated with the primary/default field
*/
public EntityDefinition(String entityField, String primaryField, Node primaryPredicate) {
- this(entityField, primaryField) ;
+ this(entityField, primaryField, null, primaryPredicate) ;
+ }
+
+ /**
+ * @param entityField
+ * The entity being indexed (e.g. it's URI).
+ * @param primaryField
+ * The primary/default field to search
+ * @param graphField
+ * The field that stores graph URI, or null
+ * @param primaryPredicate
+ * The property associated with the primary/default field
+ */
+ public EntityDefinition(String entityField, String primaryField, String graphField, Node primaryPredicate) {
+ this(entityField, primaryField, graphField) ;
set(primaryField, primaryPredicate) ;
}
@@ -107,6 +135,10 @@
return getOne(c) ;
}
+ public String getGraphField() {
+ return graphField ;
+ }
+
public Collection<String> fields() {
return fields ;
}
Index: src/main/java/org/apache/jena/query/text/assembler/EntityMapAssembler.java
===================================================================
--- src/main/java/org/apache/jena/query/text/assembler/EntityMapAssembler.java (revision 1547859)
+++ src/main/java/org/apache/jena/query/text/assembler/EntityMapAssembler.java (working copy)
@@ -62,7 +62,10 @@
"SELECT * {" ,
" ?eMap :entityField ?entityField ;" ,
" :map ?map ;",
- " :defaultField ?dftField" ,
+ " :defaultField ?dftField ." ,
+ " OPTIONAL {" ,
+ " ?eMap :graphField ?graphField" ,
+ " }",
"}") ;
ParameterizedSparqlString pss = new ParameterizedSparqlString(qs1) ;
pss.setIri("eMap", root.getURI()) ;
@@ -83,7 +86,7 @@
QuerySolution qsol1 = results.get(0) ;
String entityField = qsol1.getLiteral("entityField").getLexicalForm() ;
-
+ String graphField = qsol1.contains("graphField") ? qsol1.getLiteral("graphField").getLexicalForm() : null;
String defaultField = qsol1.contains("dftField") ? qsol1.getLiteral("dftField").getLexicalForm() : null ;
String qs2 = StrUtils.strjoinNL("SELECT * { ?map list:member [ :field ?field ; :predicate ?predicate ] }") ;
@@ -107,7 +110,7 @@
}
- EntityDefinition docDef = new EntityDefinition(entityField, defaultField) ;
+ EntityDefinition docDef = new EntityDefinition(entityField, defaultField, graphField) ;
for ( String f : mapDefs.keys() ) {
for ( Node p : mapDefs.get(f))
docDef.set(f, p) ;
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix dc: <http://purl.org/dc/terms/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
# default graph
{
<http://example.org/bob> rdfs:label "Bob" .
<http://example.org/alice> rdfs:label "Alice" .
}
<http://example.org/bob>
{
_:a rdfs:label "Bob" .
_:a foaf:mbox <mailto:[email protected]> .
_:a foaf:knows _:b .
}
_:alicegraph
{
_:b rdfs:label "Alice" .
_:b foaf:mbox <mailto:[email protected]> .
}