Parse HQL to AST Structure and convert AST back to HQL - java

I have a HQL query:
query = select item.itemNumber from items item where item.stock>0 and item.price<100.00
i like to parse this query and convert it into a tree structure:
AST queryTree = parse(query);
than i like to iterate through the nodes, change some values, and convert the tree back to a string represenation:
Iterator<ASTNode> it = queryTree.nodeIterator();
while(it.hasNext())
{
ASTNode node = it.next();
System.out.println( node.text() + "->" + node.value() );
}
query = queryTree.toString();
it would be nice if the parse method would throw Exceptions in case the HQL grammer is violated, but its not necessary.
Has anyone an idea how this can be accomplished? Are there any API methods offered by hibernate to accomplish that task?
Thanks,

You could take a look at the new experimental parser that is being worked on here: https://github.com/hibernate/hibernate-hql-parser

Related

Spring MongoDB getting only values instead of key:value in result

Hello so I am trying to get list of IDs from mongoDB , wrote this code that returns map of id:value I just want it to return just values .
query=new Query(Criteria.where("_id").is("47b3b1ab-2d80-42cf-b289-e3d45497b59f"));
query.fields().include("recordList.id").exclude("_id");
System.out.println( mongoTemplate.findOne(query, Map.class,"Company3"));
{recordList=[{id=rec4vCGPy3EnXRuCM}, {id=recAivYlqtDzZP62C}, {id=recbcLfxuLLB6Jjn0}, {id=reckIA8RdQtDUKCYI}, {id=rectnZZzBJ2iKN8eO}]}
But I need something like this
[rec4vCGPy3EnXRuCM, recAivYlqtDzZP62C, recbcLfxuLLB6Jjn0, reckIA8RdQtDUKCYI, rectnZZzBJ2iKN8eO]
Yes I know I can manipulate result like this to get desired result but I want to know if its possible to achieve same result directly from DB and not like this
List<Map<String,String>> list = (List<Map<String, String>>) mongoTemplate.findOne(query, Map.class,"Company3").get("recordList");
List<String> idList=new ArrayList<>();
for (Map<String, String> stringStringMap : list) {
idList.add(stringStringMap.get("id"));
}
This is what my data looks like
mongodb document. Sorry for inserting image , couldnt copy it without it being unreadable .
oblivion02's solution was a little bit wrong but definitely hinted me in right direction , thank you.
Query query=new Query(Criteria.where("_id").is("adfe377d-6e5b-48f0-b5bb-12b09f57285d"));
System.out.println(mongoTemplate.findDistinct(query,"recordList.id","Company4",String.class));
Just these two lines give me a nice clean list of just id values
[rec4vCGPy3EnXRuCM, recAivYlqtDzZP62C, recbcLfxuLLB6Jjn0, reckIA8RdQtDUKCYI]
Maybe distinct could help. https://docs.mongodb.com/manual/reference/method/db.collection.distinct/
Query query=new Query(Criteria.where("_id").is("adfe377d-6e5b-48f0-b5bb-12b09f57285d"));
System.out.println(mongoTemplate.findDistinct(query,"recordList.id","Company4",String.class));
You can not do that using Mongodb. This database is document oriented, meaning that given a criteria (in this case, an id), you will get a list of documents satifying the criteria where each document has some properties and some values.
To make it easier, you could rewrite your code so you could map your result to a pojo which only contains the list of ids you want and no key.
It would be something similar to the following:
public class Result {
private List<String> ids;
// getters and setters here
#override
public String toString(){
return ids.toString();
}
}
Now your repository method retrieving data will look like the following:
query = new Query(Criteria.where("_id").is("47b3b1ab-2d80-42cf-b289-e3d45497b59f"));
// No need for this
//query.fields().include("recordList.id").exclude("_id");
System.out.println( mongoTemplate.findOne(query, Result.class,"Company3"));

Morphia Mongodb searching an array of string

I was trying to write a query criteria using Morphis for something like:
db.Response.find( { $and: [ { fields: [ "NAME","EMAIL"]},{intent:"CHECKUSR" }] } )
Can you help me with this? The problem here is I need to check with a array called fields, and the above query in working in MongoDB, but not able to find a suitable option in morphia for this.
Any help will be appreciated.
Datastore ds = ...
Query<Record> q = ds.createQuery(Record.class);
q.and(
q.criteria("Intent").equal(CHECKUSR),
q.criteria("fields.email").equal(EMAIL)
);
//list
List<Record> entities = q.asList();
another approach would be
Query q = ds.createQuery(Record.class).field("fields").hasThisElement(email);
this basically is an example on how you can write your criteria. You can tweak it for your purpose.
in the above samples Record is a class representing your document

JOOQ - convert result into Pojo

I have seen that JOOQ can automatically return a POJO when we use .selectFrom(TABLE) or .fetchInto(POJO.class);
But is it possible to convert the result of a complex query into multiple POJO ?
Example :
This query will return an array of all columns into tables Support and Box. It is possible to convert them into a Support and Box Pojo ?
Result<Record> results = query.select()
.from(BOX)
.join(SUPPORT)
.on(SUPPORT.ID.equal(BOX.SUPPORT_ID))
.where(SUPPORT.ID.equal("XXXX"))
.orderBy(BOX.ID)
.fetch();
I have tested the method .intoGroups(SUPPORT.ID, Box.class) , it works fine. But I doesn't have the support object.
Instantiate to SelectSeekStep1
With aliases it's more convenient:
Box b = BOX.as("b");
Support s = SUPPORT.as("s");
SelectSeekStep1<Integer, Integer> sql = query.select(b.ID, s.ID /* other columns */)
.from(b)
.join(s)
.on(s.ID.eq(b.SUPPORT_ID))
.where(s.ID.eq("XXXX"))
.orderBy(b.ID)
;
Then just fetch what/as you need:
List<BoxRecord> boxes = sql.fetchInto(BOX);
SupportRecord support = sql.limit(1).fetchOneInto(SUPPORT);
For future readers, if you want to achieve the same behaviour with insert methods you should use:
insertInto(BOX)
.set(BOX.COLUMN1, UInteger.valueOf(1))
.set(BOX.COLUMN2, "test")
.returning()
.fetchOne()
.into(<POJO_class>.class);

How to get just the desired field from an array of sub documents in Mongodb using Java

I have just started using Mongo Db . Below is my data structure .
It has an array of skillID's , each of which have an array of activeCampaigns and each activeCampaign has an array of callsByTimeZone.
What I am looking for in SQL terms is :
Select activeCampaigns.callsByTimeZone.label,
activeCampaigns.callsByTimeZone.loaded
from X
where skillID=50296 and activeCampaigns.campaign_id= 11371940
and activeCampaigns.callsByTimeZone='PT'
The output what I am expecting is to get
{"label":"PT", "loaded":1 }
The Command I used is
db.cd.find({ "skillID" : 50296 , "activeCampaigns.campaignId" : 11371940,
"activeCampaigns.callsByTimeZone.label" :"PT" },
{ "activeCampaigns.callsByTimeZone.label" : 1 ,
"activeCampaigns.callsByTimeZone.loaded" : 1 ,"_id" : 0})
The output what I am getting is everything under activeCampaigns.callsByTimeZone while I am expecting just for PT
DataStructure :
{
"skillID":50296,
"clientID":7419,
"voiceID":1,
"otherResults":7,
"activeCampaigns":
[{
"campaignId":11371940,
"campaignFileName":"Aaron.name.121.csv",
"loaded":259,
"callsByTimeZone":
[{
"label":"CT",
"loaded":6
},
{
"label":"ET",
"loaded":241
},
{
"label":"PT",
"loaded":1
}]
}]
}
I tried the same in Java.
QueryBuilder query = QueryBuilder.start().and("skillID").is(50296)
.and("activeCampaigns.campaignId").is(11371940)
.and("activeCampaigns.callsByTimeZone.label").is("PT");
BasicDBObject fields = new BasicDBObject("activeCampaigns.callsByTimeZone.label",1)
.append("activeCampaigns.callsByTimeZone.loaded",1).append("_id", 0);
DBCursor cursor = coll.find(query.get(), fields);
String campaignJson = null;
while(cursor.hasNext()) {
DBObject campaignDBO = cursor.next();
campaignJson = campaignDBO.toString();
System.out.println(campaignJson);
}
the value obtained is everything under callsByTimeZone array. I am currently parsing the JSON obtained and getting only PT values . Is there a way to just query the PT fields inside activeCampaigns.callsByTimeZone .
Thanks in advance .Sorry if this question has already been raised in the forum, I have searched a lot and failed to find a proper solution.
Thanks in advance.
There are several ways of doing it, but you should not be using String manipulation (i.e. indexOf), the performance could be horrible.
The results in the cursor are nested Maps, representing the document in the database - a Map is a good Java-representation of key-value pairs. So you can navigate to the place you need in the document, instead of having to parse it as a String. I've tested the following and it works on your test data, but you might need to tweak it if your data is not all exactly like the example:
while (cursor.hasNext()) {
DBObject campaignDBO = cursor.next();
List callsByTimezone = (List) ((DBObject) ((List) campaignDBO.get("activeCampaigns")).get(0)).get("callsByTimeZone");
DBObject valuesThatIWant;
for (Object o : callsByTimezone) {
DBObject call = (DBObject) o;
if (call.get("label").equals("PT")) {
valuesThatIWant = call;
}
}
}
Depending upon your data, you might want to add protection against null values as well.
The thing you were looking for ({"label":"PT", "loaded":1 }) is in the variable valueThatIWant. Note that this, too, is a DBObject, i.e. a Map, so if you want to see what's inside it you need to use get:
valuesThatIWant.get("label"); // will return "PT"
valuesThatIWant.get("loaded"); // will return 1
Because DBObject is effectively a Map of String to Object (i.e. Map<String, Object>) you need to cast the values that come out of it (hence the ugliness in the first bit of code in my answer) - with numbers, it will depend on how the data was loaded into the database, it might come out as an int or as a double:
String theValueOfLabel = (String) valuesThatIWant.get("label"); // will return "PT"
double theValueOfLoaded = (Double) valuesThatIWant.get("loaded"); // will return 1.0
I'd also like to point out the following from my answer:
((List) campaignDBO.get("activeCampaigns")).get(0)
This assumes that "activeCampaigns" is a) a list and in this case b) only has one entry (I'm doing get(0)).
You will also have noticed that the fields values you've set are almost entirely being ignored, and the result is most of the document, not just the fields you asked for. I'm pretty sure you can only define the top-level fields you want the query to return, so your code:
BasicDBObject fields = new BasicDBObject("activeCampaigns.callsByTimeZone.label",1)
.append("activeCampaigns.callsByTimeZone.loaded",1)
.append("_id", 0);
is actually exactly the same as:
BasicDBObject fields = new BasicDBObject("activeCampaigns", 1).append("_id", 0);
I think some of the points that will help you to work with Java & MongoDB are:
When you query the database, it will return you the whole document of
the thing that matches your query, i.e. everything from "skillID"
downwards. If you want to select the fields to return, I think those will only be top-level fields. See the documentation for more detail.
To navigate the results, you need to know that a DBObjects are returned, and that these are effectively a Map<String,
Object> in Java - you can use get to navigate to the correct node,
but you will need to cast the values into the correct shape.
Replacing while loop from your Java code with below seems to give "PT" as output.
`while(cursor.hasNext()) {
DBObject campaignDBO = cursor.next();
campaignJson = campaignDBO.get("activeCampaigns").toString();
int labelInt = campaignJson.indexOf("PT", -1);
String label = campaignJson.substring(labelInt, labelInt+2);
System.out.println(label);
}`

How do I parse a base query (à la Google Data) in Java?

I have a system where I query a REST / Atom server for documents. The queries are inspired by GData and look like :
http://server/base/feeds/documents?bq=[type in {'news'}]
I have to parse the "bq" parameter to know which type of documents will be returned without actually doing the query. So for example,
bq=[type = 'news'] -> return ["news"]
bq=[type in {'news'}] -> return ["news"]
bq=[type in {'news', 'article'}] -> return ["news", "article"]
bq=[type = 'news']|[type = 'article'] -> return ["news", "article"]
bq=[type = 'news']|[title = 'My Title'] -> return ["news"]
Basically, the query language is a list of predicate that can be combined with OR ("|") or AND (no separator). Each predicate is constraint on a field. The constraint can be =, <, >, <=, >=, in, etc... There can be spaces everywhere where it make sense.
I'm a bit lost between Regexp, StringTokenizer, StreamTokenizer, etc... and I am stuck with Java 1.4, so no Parser ...
Who can point me in the right direction ?
Thanks !
The right way would be to use parser generator like Antlr, JFlex or JavaCC.
A quick and dirty way would be:
String[] disjunctedPredicateGroups = query.split("\|");
List<String[]> normalizedPredicates = ArrayList<String[]>;
for (String conjunction : disjunctedPredicateGroups ) {
normalizedPredicates.add(conjunction.split("\[|\]"));
}
// process each predicate

Categories