I am trying to build secured query in Apache Jena v.3.10.0.
I want to pass some query, modify the query according to existing SecurityEvaluator and execute it later.
However, I don't understand how it should be used with certain format of queries.
I tried doing it using simplified version of SecuredQueryEngine.
public class GenSecuredEngine extends QueryEngineMain {
private static Logger LOG = LoggerFactory
.getLogger(SecuredQueryEngine.class);
private SecurityEvaluator securityEvaluator;
private Node graphIRI;
public GenSecuredEngine(final Query query, final DatasetGraph dataset,
final SecurityEvaluator evaluator,
final Binding input, final Context context) {
super(query, dataset, input, context);
this.securityEvaluator = evaluator;
graphIRI = NodeFactory.createURI("urn:x-arq:DefaultGraph");
}
#Override
protected Op modifyOp(final Op op) {
final OpRewriter rewriter = new OpRewriter(securityEvaluator, graphIRI);
LOG.debug("Before: {}", op);
op.visit(rewriter);
Op result = rewriter.getResult();
result = result == null ? op : result;
LOG.debug("After: {}", result);
result = super.modifyOp(result);
LOG.debug("After Optimize: {}", result);
return result;
}
}
Then we have such code modifying the given query into Op, checking the permissions and building the new Op object.
Op oporiginal = new AlgebraGenerator().compile(query);
Op result = securedEngine.modifyOp(oporiginal);
System.out.println(OpAsQuery.asQuery(result));
If I pass the query like this
select *
where {
graph <forbiddenGraphUri> {?a ?b ?c}
}
then everything works fine and the "forbiddenUri" is checked against SecurityEvaluator and throws org.apache.jena.shared.ReadDeniedException: Model permissions violation.
But what if user wants to execute something like this:
select * where { graph ?g {?a ?b ?c}}
In this case we can check only URI of "?g" which is not really informative. I understand that AlgebraGenerator does not fill the missing spots so this is probably the wrong approach.
So, how can it be done? For example, if user wants to run the query against many named graphs, how to filter the not allowed ones? Is it possible at all with the existing tools?
Related
I am trying to pass a java String to Apache StandardQueryparser to get the Querynode.
Input - "fq=section:1"
All I need is section:1 in FILTER clause in QueryNode. This looks pretty straightforward but it throws
INVALID_SYNTAX_CANNOT_PARSE: Syntax Error, cannot parse fq=section:1:
Use a ConstantScoreQuery. It won't affect it score, and is the same was as the fq parameter is implemented in Solr:
public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
// SolrRequestInfo reqInfo = SolrRequestInfo.getRequestInfo();
if (!(searcher instanceof SolrIndexSearcher)) {
// delete-by-query won't have SolrIndexSearcher
return new BoostQuery(new ConstantScoreQuery(q), 0).createWeight(searcher, scoreMode, 1f);
}
SolrIndexSearcher solrSearcher = (SolrIndexSearcher)searcher;
DocSet docs = solrSearcher.getDocSet(q);
// reqInfo.addCloseHook(docs); // needed for off-heap refcounting
return new BoostQuery(new SolrConstantScoreQuery(docs.getTopFilter()), 0).createWeight(searcher, scoreMode, 1f);
}
Domain class:
class Transaction {
String roundId
BigDecimal amount
:
}
The SQL we wish to execute the following:
"select sum(t.amount) from transaction t where t.roundId = xxx"
We have been unable to find an example which does not return Transaction rows.
We assume there are two approaches:
Use projections and/or criteria etc? All the examples we have found only return lists of transaction rows, not the sum.
Use raw SQL. How do we call SQL, and get a handle on the BigDecimal it returns?
I tried this:
class bla{
def sessionFactory
def someMethod() {
def SQLsession = sessionFactory.getCurrentSession()
def results = SQLsession.createSQLQuery("select sum(t.credit) from transaction t where t.round_id = :roundId", [roundId: roundId])
But this fails with
groovy.lang.MissingMethodException: No signature of method: org.hibernate.internal.SessionImpl.createSQLQuery() is applicable for argument types: (java.lang.String, java.util.LinkedHashMap)
Also, I have no idea what the return type would be (cant find any documentation). I am guessing it will be a list of something: Arrays? Maps?
==== UPDATE ====
Found one way which works (not very elegant or grails like)
def SQLsession = sessionFactory.getCurrentSession()
final query = "select sum(t.credit) from transaction t where t.round_id = :roundId"
final sqlQuery = SQLsession.createSQLQuery(query)
final results = sqlQuery.with {
setString('roundId', roundId)
list() // what is this for? Is there a better return value?
}
This seems to return an array, not a list as expected, so I can do this:
if (results?.size == 1) {
println results[0] // outputs a big decimal
}
Strangely, results.length fails, but results.size works.
Using Criteria, you can do
Transaction.withCriteria {
eq 'roundId', yourRoundIdValueHere
projections {
sum 'amount'
}
}
https://docs.jboss.org/hibernate/core/3.3/api/org/hibernate/classic/Session.html
Query createSQLQuery(String sql, String[] returnAliases, Class[] returnClasses)
Query createSQLQuery(String sql, String returnAlias, Class returnClass)
The second argument of createSQLQuery is one or more returnAliases and not meant for binding the statement to a value.
Instead of passing your values in the 2nd argument, use the setters of your Query object i.e. setString, setInteger, etc.
results.setInteger('roundId',roundId);
I'd like to use Jena's inference capabilities, but I'm having some performance problems when I am using InfModel.
Here's a simplified overview of my ontology:
Properties:
hasX (Ranges(intersection): X, inverse properties: isXOf)
|-- hasSpecialX (Ranges(intersection): X, inverse properties: isSpecialXOf)
isXOf (Domains(intersection): X, inverse properties: hasX)
|--isSpecialXOf (Domains(intersection): X, inverse properties: hasSpecialX)
Furthermore there's a class 'Object':
Object hasSpecialX some X
Explicitly stored is the following data:
SomeObject a Object
SomeX a X
SomeObject hasSpecialX SomeX
Using the following query I'd like to determine to which class an instance belongs. According to the assumptions made, only 'SomeObject' should be returned.
SELECT ?x WHERE { ?x :hasX :SomeX . }
However, querying against ds.getDefaultModel() doesn't work, because the data isn't stored explicitly. When I'm using infModel on the other hand, the query never finishes. At the longest I've been waiting for 25 minutes before aborting. (The triplestore has a size of about 180 MB)
This is my code:
OntModel ont = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM_MICRO_RULE_INF, null);
ont.read("file:..." , "RDF/XML");
Reasoner reasoner = ReasonerRegistry.getOWLMicroReasoner();
reasoner = reasoner.bindSchema(ont);
Dataset dataset = TDBFactory.createDataset(...);
Model model = dataset.getDefaultModel();
InfModel infModel = ModelFactory.createInfModel(reasoner, model);
QueryExecution qe = null;
ResultSet rs;
try {
String qry = "SELECT ?x WHERE { ?x :hasX :SomeX . }";
qe = QueryExecutionFactory.create(qry, infModel);
rs = qe.execSelect();
while(rs.hasNext()) {
QuerySolution sol = rs.nextSolution();
System.out.println(sol.get("x"));
}
} finally {
qe.close();
infModel.close();
model.close();
dataset.close();
}
Is there anything wrong with the code above, or what else could be the reason it doesn't work?
Beside that, I'd like to know if I can increase the performance if I do 'Export inferred axioms as ontology' (as provided by Protege)?
EDIT:
I the meantime I've tried to use Pellet, but still I can't get an inferred model, as I've described in my other question: OutOfMemoryError using Pellet as Reasoner. So what else can I do?
Regarding performance, it is better to do the inference before asserting the data and not and do the SPARQLs with the Jena inference mechanism off. You are already using TDB which the right Jena component for big datasets.
If by using the inferred data directly you do not get the expected performance then I recommend moving to a more scalable triple store (4store or Virtuoso).
I know variants of this question have been asked before (even by me), but I still don't understand a thing or two about this...
It was my understanding that one could retrieve more documents than the 128 default setting by doing this:
session.Advanced.MaxNumberOfRequestsPerSession = int.MaxValue;
And I've learned that a WHERE clause should be an ExpressionTree instead of a Func, so that it's treated as Queryable instead of Enumerable. So I thought this should work:
public static List<T> GetObjectList<T>(Expression<Func<T, bool>> whereClause)
{
using (IDocumentSession session = GetRavenSession())
{
return session.Query<T>().Where(whereClause).ToList();
}
}
However, that only returns 128 documents. Why?
Note, here is the code that calls the above method:
RavenDataAccessComponent.GetObjectList<Ccm>(x => x.TimeStamp > lastReadTime);
If I add Take(n), then I can get as many documents as I like. For example, this returns 200 documents:
return session.Query<T>().Where(whereClause).Take(200).ToList();
Based on all of this, it would seem that the appropriate way to retrieve thousands of documents is to set MaxNumberOfRequestsPerSession and use Take() in the query. Is that right? If not, how should it be done?
For my app, I need to retrieve thousands of documents (that have very little data in them). We keep these documents in memory and used as the data source for charts.
** EDIT **
I tried using int.MaxValue in my Take():
return session.Query<T>().Where(whereClause).Take(int.MaxValue).ToList();
And that returns 1024. Argh. How do I get more than 1024?
** EDIT 2 - Sample document showing data **
{
"Header_ID": 3525880,
"Sub_ID": "120403261139",
"TimeStamp": "2012-04-05T15:14:13.9870000",
"Equipment_ID": "PBG11A-CCM",
"AverageAbsorber1": "284.451",
"AverageAbsorber2": "108.442",
"AverageAbsorber3": "886.523",
"AverageAbsorber4": "176.773"
}
It is worth noting that since version 2.5, RavenDB has an "unbounded results API" to allow streaming. The example from the docs shows how to use this:
var query = session.Query<User>("Users/ByActive").Where(x => x.Active);
using (var enumerator = session.Advanced.Stream(query))
{
while (enumerator.MoveNext())
{
User activeUser = enumerator.Current.Document;
}
}
There is support for standard RavenDB queries, Lucence queries and there is also async support.
The documentation can be found here. Ayende's introductory blog article can be found here.
The Take(n) function will only give you up to 1024 by default. However, you can change this default in Raven.Server.exe.config:
<add key="Raven/MaxPageSize" value="5000"/>
For more info, see: http://ravendb.net/docs/intro/safe-by-default
The Take(n) function will only give you up to 1024 by default. However, you can use it in pair with Skip(n) to get all
var points = new List<T>();
var nextGroupOfPoints = new List<T>();
const int ElementTakeCount = 1024;
int i = 0;
int skipResults = 0;
do
{
nextGroupOfPoints = session.Query<T>().Statistics(out stats).Where(whereClause).Skip(i * ElementTakeCount + skipResults).Take(ElementTakeCount).ToList();
i++;
skipResults += stats.SkippedResults;
points = points.Concat(nextGroupOfPoints).ToList();
}
while (nextGroupOfPoints.Count == ElementTakeCount);
return points;
RavenDB Paging
Number of request per session is a separate concept then number of documents retrieved per call. Sessions are short lived and are expected to have few calls issued over them.
If you are getting more then 10 of anything from the store (even less then default 128) for human consumption then something is wrong or your problem is requiring different thinking then truck load of documents coming from the data store.
RavenDB indexing is quite sophisticated. Good article about indexing here and facets here.
If you have need to perform data aggregation, create map/reduce index which results in aggregated data e.g.:
Index:
from post in docs.Posts
select new { post.Author, Count = 1 }
from result in results
group result by result.Author into g
select new
{
Author = g.Key,
Count = g.Sum(x=>x.Count)
}
Query:
session.Query<AuthorPostStats>("Posts/ByUser/Count")(x=>x.Author)();
You can also use a predefined index with the Stream method. You may use a Where clause on indexed fields.
var query = session.Query<User, MyUserIndex>();
var query = session.Query<User, MyUserIndex>().Where(x => !x.IsDeleted);
using (var enumerator = session.Advanced.Stream<User>(query))
{
while (enumerator.MoveNext())
{
var user = enumerator.Current.Document;
// do something
}
}
Example index:
public class MyUserIndex: AbstractIndexCreationTask<User>
{
public MyUserIndex()
{
this.Map = users =>
from u in users
select new
{
u.IsDeleted,
u.Username,
};
}
}
Documentation: What are indexes?
Session : Querying : How to stream query results?
Important note: the Stream method will NOT track objects. If you change objects obtained from this method, SaveChanges() will not be aware of any change.
Other note: you may get the following exception if you do not specify the index to use.
InvalidOperationException: StreamQuery does not support querying dynamic indexes. It is designed to be used with large data-sets and is unlikely to return all data-set after 15 sec of indexing, like Query() does.
I need to find the number of documents that are in the raven database , so that I can properly page the documents out. I had the following implementation -
public int Getcount<T>()
{
IQueryable<T> queryable = from p in _session.Query<T>().Customize(x =>x.WaitForNonStaleResultsAsOfLastWrite())
select p;
return queryable.Count();
}
But if the count is too large then it times out.
I tried the method suggested in FAQs -
public int GetCount<T>()
{
//IQueryable<T> queryable = from p in _session.Query<T>().Customize(x => x.WaitForNonStaleResultsAsOfLastWrite())
// select p;
//return queryable.Count();
RavenQueryStatistics stats;
var results = _session.Query<T>()
.Statistics(out stats);
return stats.TotalResults;
}
This always returns 0.
What am I doing wrong?
stats.TotalResults is 0 because the query was never executed. Try this instead:
var results = _session
.Query<T>()
.Statistics(out stats)
.Take(0)
.ToArray();
The strange syntax to get the statistics tripped me up as well. I can see why the query needs to be run in order to populate the statistic object but the syntax is a bit verbose imo.
I have written the following extension method for use in my unit tests. It helps keep the code terse.
Extension Method
public static int QuickCount<T>(this IRavenQueryable<T> results)
{
RavenQueryStatistics stats;
results.Statistics(out stats).Take(0).ToArray();
return stats.TotalResults;
}
Unit Test
...
db.Query<T>().QuickCount().ShouldBeGreaterThan(128);
...