Querydsl: How to write a "complex" query - java

I'm trying to create an sql sentence using querydsl. What I'm trying to get is:
SELECT P.KEY, COUNT(P.VALUE)
FROM RESOURCES R JOIN PROPERTIES P ON R.ID = P.ID
WHERE P.KEY = "key" AND p.VALUE = "value"
GROUP BY P.VALUE;
I've tried to write some querydsl code:
String s = queryFactory
.query()
.from(QResource.resource)
.join(QProperty.property)
.where(QResource.resource.properties.any().key.eq("key").and(QResource.resource.properties.any().value.eq("value")))
.groupBy(QProperty.property.value)
.select(QProperty.property.key, QProperty.property.value.count())
.toString();
I'm guessing it can be simplified and by other hand I don't quite see if it's well querydsl-coded.
Any ideas?

A more simplified version would be:
QResource r = QResource.resource;
QProperty p = QProperty.property;
queryFactory
.select(p.key, p.value.count())
.from(r)
.join(p).on(r.id.eq(p.id))
.where(p.key.eq("key"), p.value.eq("value"))
.groupBy(p.value)
.fetchOne();

Adding to #natros answer, Boolean operators can be used for 'and' logic
QResource r = QResource.resource;
QProperty p = QProperty.property;
queryFactory
.select(p.key, p.value.count())
.from(r)
.join(p).on(r.id.eq(p.id))
.where(p.key.eq("key").and(p.value.eq("value")))
.groupBy(p.value)
.fetchOne();
Boolean builders can be also used for complex logics
BooleanBuilder builder = new BooleanBuilder();
builder.and(p.key.eq("key"));
builder.and(p.value.eq("value"));
queryFactory
.select(p.key, p.value.count())
.from(r)
.join(p).on(r.id.eq(p.id))
.where(builder)
.groupBy(p.value)
.fetchOne();

Related

How to write update Query in CriteriaUpdate using joins?

I trying to write code regarding CriteriaUpdate using joins.
but, in CriteriaUpdate joins are not working.
Then I tried using subqueries like below ..,
CriteriaBuilder userBuilder = deviceSession.getCriteriaBuilder();
CriteriaUpdate<Switch> deviceUpdate = userBuilder.createCriteriaUpdate(Switch.class);
Root<Switch> deviceUpdateRoot = deviceUpdate.from(Switch.class);
Subquery<Switch> deviceSubquery = deviceUpdate.subquery(Switch.class);
Root<Switch> roomRoot = deviceSubquery.from(Switch.class);
Join<Switch, Rooms> join = roomRoot.join("room" ,JoinType.LEFT);
deviceUpdate.set(deviceUpdateRoot.get("displayName"),"SSS");
deviceUpdate.set(deviceUpdateRoot.get("device"),"device");
deviceUpdate.set(deviceUpdateRoot.get("operation"),"operation");
***deviceUpdate.set(join.get("roomId"),device.getRoomId());***
deviceUpdate.where(userBuilder.equal(deviceUpdateRoot.get("deviceName"),device.getDeviceName()));
int returnValue = deviceSession.createQuery(deviceUpdate).executeUpdate();
but I am getting error like org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'generatedAlias1.roomId'
can you please help from this isuue.

How to use JCypher to send unrelated queries in one go and get the results properly

I'm using JCypher 4.2.0 with Neo4J Server 4.0.2 with the builtin Movie graph.
I'm trying to unite couple simple, independent queries to one bigger for my needs.
first query: MATCH (people:Person) RETURN people.name, second query: MATCH (m:Movie) WHERE m.title = "Apollo 13" RETURN m.title.
and in JCypher:
JcNode
people = new JcNode("people"),
m = new JcNode("m");
JcQuery
query1 = new JcQuery(new IClause[]{
MATCH.node(people).label("Person"),
RETURN.value(people.property("name"))
}),
query2 = new JcQuery(new IClause[]{
MATCH.node(m).label("Movie"),
WHERE.valueOf(m.property("title")).EQUALS("Apollo 13")
RETURN.value(m.property("title"))
});
Of course, m.title should return only 1 result.
is it possible?

How to set a property path in Jena's Sparql API?

I would like to avoid passing SPARQL queries around as Strings. Therefore I use Jena's API for creating my queries. Now I need a PropertyPath in my query, but I can't find any Java class supporting this. Can you give me a hint?
Here's some example code where I would like to insert this (Jena 3.0.1):
private Query buildQuery(final String propertyPath) {
ElementTriplesBlock triplesBlock = new ElementTriplesBlock();
triplesBlock.addTriple(
new Triple(NodeFactory.createURI(this.titleUri.toString()),
//How can I set a property path as predicate here?
NodeFactory.???,
NodeFactory.createVariable("o"))
);
final Query query = buildSelectQuery(triplesBlock);
return query;
}
private Query buildSelectQuery(final ElementTriplesBlock queryBlock) {
final Query query = new Query();
query.setQuerySelectType();
query.setQueryResultStar(true);
query.setDistinct(true);
query.setQueryPattern(queryBlock);
return query;
}
You can use PathFactory to create property paths
Consider the graph below:
#prefix dc: <http://purl.org/dc/elements/1.1/>.
#prefix ex: <http://example.com/>.
ex:Manager ex:homeOffice ex:HomeOffice
ex:HomeOffice dc:title "Home Office Title"
Suppose you want to create a pattern like:
?x ex:homeOffice/dc:title ?title
The code below achieves it:
//create the path
Path exhomeOffice = PathFactory.pathLink(NodeFactory.createURI("http://example.com/homeOffice"));
Path dcTitle = PathFactory.pathLink(NodeFactory.createURI("http://purl.org/dc/elements/1.1/title"));
Path fullPath = PathFactory.pathSeq(exhomeOffice,dcTitle);
TriplePath t = new TriplePath(Var.alloc("x"),fullPath,Var.alloc("title"));

How do I build a SPARQL list input using jena querybuilder?

I have a bunch of code that uses the Apache Jena querybuilder API (SelectBuilder class). I am trying to add a term like this to my existing SPARQL query:
(?a ?b ?c) :hasMagicProperty ?this .
I have verified that this query works in TopBraid, but I can't figure out how to represent (?a, ?b, ?c) in the Jena API. What do I have to do to convert this list of Vars into a valid Jena resource node?
I am willing to explore alternate SPARQL-building frameworks, if they have robust support for typed literals, IRIs, and filters, as well as this list construct. I have skimmed over several other frameworks for building up SPARQL queries, but none of them seem to have a list construct.
Edit
My query building code (in Groovy) looks something like this:
def selectBuilder = new SelectBuilder()
selectBuilder.addPrefixes(...)
def thisVar = Var.alloc('this')
selectBuilder.addOptional(thisVar, 'rdf:type', ':MyEntity')
def aVar = Var.alloc('a')
def bVar = Var.alloc('b')
def cVar = Var.alloc('c')
List<Var> abc = [aVar, bVar, cVar]
//this doesn't work!!!
selectBuilder.addWhere(abc, ':hasMagicProperty', thisVar)
selectBuilder.addWhere(aVar, ':hasACode', 'code A')
selectBuilder.addWhere(bVar, ':hasBCode', 'code B')
selectBuilder.addWhere(cVar, ':hasCCode', 'code C')
def sparqlQuery = selectBuilder.buildString()
I have spent a couple of hours trying to work with the RDFList class, and I haven't figured it out. I'll keep trying, and see if I can grok it. In the meantime, any help would be appreciated. :)
Edit
Here is an unsuccessful attempt to use RDFList:
//this code does not work!
def varNode = NodeFactory.createVariable('a')
def model = ModelFactory.createDefaultModel()
def rdfNode = model.asRDFNode(varNode)
def rdfList = new RDFListImpl(model.createResource().asNode(), model)
//this line throws an exception!!
rdfList.add(rdfNode)
selectBuilder.addWhere(rdfList, ':hasMagicProperty', thisVar)
//com.hp.hpl.jena.shared.PropertyNotFoundException: http://www.w3.org/1999/02/22-rdf-syntax-ns#rest
The following method is a workaround, using multiple triples to recursively build up the RDF list:
/*
* Jena querybuilder does not yet support RDF lists. See:
* http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#collections
*/
private Node buildRdfCollection(SelectBuilder queryBuilder, List<?> itemList) {
if (itemList.isEmpty()) {
return RDF.nil.asNode()
}
def head = itemList.first()
def rest = buildRdfCollection(queryBuilder, itemList.subList(1, itemList.size()))
def listNode = NodeFactory.createAnon()
queryBuilder.addWhere(listNode, RDF.first, head)
queryBuilder.addWhere(listNode, RDF.rest, rest)
return listNode
}
...
def listNode = buildRdfCollection(queryBuilder, abc)
queryBuilder.addWhere(listNode, ':hasMagicProperty', thisVar)
The generated SPARQL code looks like this:
_:b0 rdf:first ?c ;
rdf:rest rdf:nil .
_:b1 rdf:first ?b ;
rdf:rest _:b0 .
_:b2 rdf:first ?a ;
rdf:rest _:b1 ;
:hasMagicProperty ?this .
This is a long-winded equivalent to:
(?a ?b ?c) :hasMagicProperty ?this .
I wrote the queryBuilder and I don't think that in it's current state it will do what you want. Query builder is based on (but does not yet fully implement) the w3c SPARQL 1.1 recommendation:
http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#rQuery
However, I think you can create your query using the Jena QueryFactory
String queryString = "SELECT * WHERE { "+
" OPTIONAL { ?this a :MyEntity } ."+
" (?a ?b ?c) :hasMagicProperty ?result . "+
" ?a :hasACode 'code A' . "+
" ?b :hasACode 'code B' . "+
" ?c :hasACode 'code C' ."+
" }";
Query query = QueryFactory.create(queryString) ;
Unfortunately, I don't think this is what you really want. Notice that ?this is not bound to any of the other statements and so will produce a cross product of all :MyEntity type subjects with the ?a, ?b, ?c and `?result`` bindings.
If you can create the query with QueryFactory, I can ensure that QueryBuilder will support it.
UPDATE
I have updated QueryBuilder (the next Snapshot should contain the changes). You should now be able to do the following:
Var aVar = Var.alloc('a')
Var bVar = Var.alloc('b')
Var cVar = Var.alloc('c')
selectBuilder.addWhere(selectBuilder.list(aVar, bVar, cVar), ':hasMagicProperty', thisVar)
selectBuilder.addWhere(aVar, ':hasACode', 'code A')
selectBuilder.addWhere(bVar, ':hasBCode', 'code B')
selectBuilder.addWhere(cVar, ':hasCCode', 'code C')
If you can also simply add the standard text versions of values in the list parameters like:
selectBuilder.list( "<a>", "?b", "'c'" )

How to get fully materialized query from querydsl

I am trying to use querydsl for building dynamic queries for dynamic schemas. I am trying to get just the query instead of having to actually execute it.
So far I have faced two issues:
- The schema.table notation is absent. Instead I only get the table name.
- I have been able to get the query but it separates out the variables and puts '?' instead which is understandable. But I am wondering if there is some way to get fully materialized query including the parameters.
Here is my current attempt and result(I am using MySQLTemplates to create the configuration):
private SQLTemplates templates = new MySQLTemplates();
private Configuration configuration = new Configuration(templates);
String table = "sometable"
Path<Object> userPath = new PathImpl<Object>(Object.class, table);
StringPath usernamePath = Expressions.stringPath(userPath, "username");
NumberPath<Long> idPath = Expressions.numberPath(Long.class, userPath, "id");
SQLQuery sqlQuery = new SQLQuery(connection, configuration)
.from(userPath).where(idPath.eq(1l)).limit(10);
String query = sqlQuery.getSQL(usernamePath).getSQL();
return query;
And what I get is:
select sometable.username
from sometable
where sometable.id = ?
limit ?
What I wanted to get was:
select sometable.username
from someschema.sometable
where sometable.id = ?
limit ?
Update: I came up with this sort of hack to get parameters materialized(Not ideal and would love better solution) But still could not get Schema.Table notation to work:
Hack follows. Please suggest cleaner QueryDsl way of doing it:
String query = cleanQuery(sqlQuery.getSQL(usernamePath));
private String cleanQuery(SQLBindings bindings){
String query = bindings.getSQL();
for (Object binding : bindings.getBindings()) {
query = query.replaceFirst("\\?", binding.toString());
}
return query;
}
To enable schema printing use the following pattern
SQLTemplates templates = MySQLTemplates.builder()
.printSchema()
.build();
SQLTemplates subclasses were used before, but since some time the builder pattern is the official way to customize the templates http://www.querydsl.com/static/querydsl/3.3.1/reference/html/ch02s03.html#d0e904
And to enable direct serialization of literals use
//configuration level
configuration.setUseLiterals(true);
//query level
configuration.setUseLiterals(true);
Here is a full example
// configuration
SQLTemplates templates = MySQLTemplates.builder()
.printSchema()
.build();
Configuration configuration = new Configuration(templates);
// querying
SQLQuery sqlQuery = new SQLQuery(connection, configuration)
.from(userPath).where(idPath.eq(1l)).limit(10);
sqlQuery.setUseLiterals(true);
String query = sqlQuery.getSQL(usernamePath).getSQL();
If you always just want the SQL query string out, move setUseLiterals from query to configuration.
Concerning the usage of Querydsl expressions the usage of code generation like documented here is advised http://www.querydsl.com/static/querydsl/3.3.1/reference/html/ch02s03.html
It will make your code typesafe, compact and readable.
If you want to try Querydsl without code generation you can replace
Path<Object> userPath = new PathImpl<Object>(Object.class, variable);
with
Path<Object> userPath = new RelationalPathBase<Object>(Object.class, variable, schema, table);
When working with QueryDSL, you must provide a template for the database platform to build the query for. I see you are already are doing this here:
private SQLTemplates templates = new MySQLTemplates();
private Configuration configuration = new Configuration(templates);
To make the schema name appear in the generated query, the only way I have found to do this is (there may be an easier way) is to extend the template class and explicitly call this.setPrintSchema(true); inside the constructor. Here is a class that should work for MySql:
import com.mysema.query.sql.MySQLTemplates;
public class NewMySqlTemplates extends MySQLTemplates {
public NewMySqlTemplates() {
super('\\', false);
}
public NewMySqlTemplates(boolean quote) {
super('\\', quote);
}
public NewMySqlTemplates(char escape, boolean quote) {
super(escape, quote);
this.setPrintSchema(true);
}
}
Then simply use this NewMySqlTemplates class in place of the MySQLTemplates class like this:
private SQLTemplates templates = new NewMySQLTemplates();
private Configuration configuration = new Configuration(templates);
I have this working using PostgresTemplates, so I may have a typo or mistake in the NewMySqlTemplates class above, but you should be able to get it to work. Good luck!

Categories

Resources