You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@jena.apache.org by "Stephen Allen (JIRA)" <ji...@apache.org> on 2015/07/29 17:21:09 UTC
[jira] [Comment Edited] (JENA-999) Poor jena-text query performance
when a bound subject is used
[ https://issues.apache.org/jira/browse/JENA-999?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14646252#comment-14646252 ]
Stephen Allen edited comment on JENA-999 at 7/29/15 3:20 PM:
-------------------------------------------------------------
!jena-text benchmarks.png|width=800,height=718!
I've attached a chart (note log-log scale) of some performance benchmarks. This is an in-memory TDB database with an in-memory lucene index. There are 10,000 entities ("test0" to "test9999") in the system. There are two statements per entity:
{code}
<http://example.org/test1234> rdf:type <http://example.org/Entity> .
<http://example.org/test1234> rdfs:label "test1234" .
{code}
Jena-text indexes the rdfs:label predicate. The benchmarks show 5 different implementations for the following query:
{code}
select *
where {
?s rdf:type <http://example.org/Entity>
# lucene query string below varied to adjust the number of results
?s ?s text:query ( rdfs:label "test1*" )
}
{code}
The 5 implementations are as follows:
# *Original* - the current jena-text implementation with a concrete subject
# *Lucene Index Join* - Option 1 described above. Added the subject as a parameter to the lucene query
# *Hash Join* - Option 2 described above. Perform the lucene query for all results, which are then cached in the ExecutionContext in a hashmap
# *Non-Indexed* - The query above is modified to remove the {{text:query}} statement and instead simply retrieve the label from the RDF store and use {{strstarts()}} to filter the results
# *Text Search First* - The current jena-text implemenation, but the query above is modified by switching the order of the two BGPs. This is the unbound subject case.
>From these initial results, it seems worthwhile to try to implement a version of option 2 (the hash join). This seems quite doable without the ugly hack of storing stuff in the ExecutionContext by doing as you suggest and implementing {{PropertyFunction}} ourselves instead of extending {{PropertyFunctionBase}}.
was (Author: sallen):
!jena-text benchmarks.png|width=800!,height=718!
I've attached a chart (note log-log scale) of some performance benchmarks. This is an in-memory TDB database with an in-memory lucene index. There are 10,000 entities ("test0" to "test9999") in the system. There are two statements per entity:
{code}
<http://example.org/test1234> rdf:type <http://example.org/Entity> .
<http://example.org/test1234> rdfs:label "test1234" .
{code}
Jena-text indexes the rdfs:label predicate. The benchmarks show 5 different implementations for the following query:
{code}
select *
where {
?s rdf:type <http://example.org/Entity>
# lucene query string below varied to adjust the number of results
?s ?s text:query ( rdfs:label "test1*" )
}
{code}
The 5 implementations are as follows:
# *Original* - the current jena-text implementation with a concrete subject
# *Lucene Index Join* - Option 1 described above. Added the subject as a parameter to the lucene query
# *Hash Join* - Option 2 described above. Perform the lucene query for all results, which are then cached in the ExecutionContext in a hashmap
# *Non-Indexed* - The query above is modified to remove the {{text:query}} statement and instead simply retrieve the label from the RDF store and use {{strstarts()}} to filter the results
# *Text Search First* - The current jena-text implemenation, but the query above is modified by switching the order of the two BGPs. This is the unbound subject case.
>From these initial results, it seems worthwhile to try to implement a version of option 2 (the hash join). This seems quite doable without the ugly hack of storing stuff in the ExecutionContext by doing as you suggest and implementing {{PropertyFunction}} ourselves instead of extending {{PropertyFunctionBase}}.
> Poor jena-text query performance when a bound subject is used
> -------------------------------------------------------------
>
> Key: JENA-999
> URL: https://issues.apache.org/jira/browse/JENA-999
> Project: Apache Jena
> Issue Type: Improvement
> Reporter: Stephen Allen
> Assignee: Stephen Allen
> Priority: Minor
> Attachments: jena-text benchmarks.png
>
>
> When executing a jena-text query, the performance is terrible if the subject is already bound to a variable. This is because the current code will execute a new lucene query that does not have the subject/entity bound on every iteration and then iterate through the lucene results to join against the subject. This is quite inefficient.
> Example query:
> {code}
> select *
> where {
> ?s rdf:type <http://example.org/Entity> .
> ?s text:query ( rdfs:label "test" ) .
> }
> {code}
> This would be quite slow if there were a lot of entities in the system.
> Two potential solutions present themselves:
> # Craft a more explicit lucene query that specifies the entity URI, so that the results coming back from lucene are much smaller. However, this would cause problems with the score not being correct across multiple iterations. Additionally we are still potentially running a lot of lucene queries, each of which has a probably non-negligble constant cost (parsing the query string, etc).
> # Execute the more general lucene query the first time it is encountered, then caching the results somewhere. From there, we can then perform a hash table lookup against those cached results.
> I would like to pursue option 2, but there is a problem. Because jena-text is implemented as a property function instead of a query op in and of itself (like QueryIterMinus is for example), we have to find a place to stash the lucene results. I believe this can be done by placing it in the ExecutionContext object, using the lucene query as a cache key. Updates provide a slightly troubling case because you could have an update request like:
> {code}
> insert data { <urn:test1> rdf:type <http://example.org/Entity> ; rdfs:label "test" } ;
> delete { ?s ?p ?o }
> where { ?s rdf:type <http://example.org/Entity> ; text:query ( rdfs:label "test" ) . ?p ?o . } ;
> insert data { <urn:test2> rdf:type <http://example.org/Entity> ; rdfs:label "test" } ;
> delete { ?s ?p ?o }
> where { ?s rdf:type <http://example.org/Entity> ; text:query ( rdfs:label "test" ) ; ?p ?o . }
> {code}
> And then the end result should be an empty database. But if the ExecutionContext was the same for both delete queries, you would be using the cached results from the first delete query in the second delete query, which would result in {{<urn:test2>}} not being deleted properly.
> If the ExecutionContext is indeed shared between the two update queries in the situation above, I think this can be solved by making the cache key for the lucene resultset be a combination of both the lucene query and the QueryIterRoot or BindingRoot. I need to investigate this. An alternative, if there was a way to be notified when a query has finished executing, we could clear the cache in the ExecutionContext.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)