I am receiving a where clause from Rest API Get and I should convert properties, So I need to convert this string to java object with logical and .... for example :
String where = "prop = 1 and prop2 = 'ssdf' or date > 20121204";
Is there any java library that converts where clause to java object with separate condition and operator ?
Finally I found this library :
https://github.com/JSQLParser/JSqlParser
Seems like very good for query parsing
Using JSqlParser one could use an already implemented utility method to parse conditions like in your where clause:
String where = "prop = 1 and prop2 = 'ssdf' or date > 20121204";
Expression expr = CCJSqlParserUtil.parseCondExpression(where);
System.out.println(expr);
This works with JSqlParser V0.9.x (https://github.com/JSQLParser/JSqlParser)
Then you have an object tree with separated operators, ands, ors, values. This one you could traverse using the ExpressionVisitorAdapter, e.g.
ExpressionVisitorAdapter visitor = new ExpressionVisitorAdapter() {
#Override
public void visit(Column column) {
System.out.println(column);
}
};
expr.accept(visitor);
Related
My spring-boot application is generating GraphQL queries, however I want to compare that query in my test.
So basically I have two strings where the first one is containing the actual value and the latter one the expected value.
I want to parse that in a class or tree node so I can compare them if both of them are equal.
So even if the order of the fields are different, I need to know if it's the same query.
So for example we have these two queries:
Actual:
query Query {
car {
brand
color
year
}
person {
name
age
}
}
Expected
query Query {
person {
age
name
}
car {
brand
color
year
}
}
I expect that these queries both are semantically the same.
I tried
Parser parser = new Parser();
Document expectedDocument = parser.parseDocument(expectedValue);
Document actualDocument = parser.parseDocument(actualValue);
if (expectedDocument.isEqualTo(actualDocument)) {
return MatchResult.exactMatch();
}
But found out that it does nothing since the isEqualTo is doing this:
public boolean isEqualTo(Node o) {
if (this == o) {
return true;
} else {
return o != null && this.getClass() == o.getClass();
}
}
I know with JSON I can use Jackson for this purpose and compare treenodes, or parsing it into a Java object and have my own equals() implementation, but I don't know how to do that for GraphQL Java.
How can I parse my GraphQL query string into an object so that I can compare it?
I have recently solved this problem myself. You can reduce the query to a hash and compare the values. You can account for varied query order by utilizing a tree structure. You can take advantage of the QueryTraverser and QueryReducer to accomplish this.
First you can create the QueryTraverser, the exact method for creating this will depend on your execution point. Assuming you are doing it in the AsyncExecutor with access to the ExecutionContext the below code snippet will suffice. But you can do this in instrumentation or the data fetcher itself if you so choose;
val queryTraverser = QueryTraverser.newQueryTraverser()
.schema(context.graphQLSchema)
.document(context.document
.operationName(context.operationDefinition.name)
.variables(context.executionInput?.variables ?: emptyMap())
.build()
Next you will need to provide an implementation of the reducer, and some accumulation object that can add each field to a tree structure. Here is a simplified version of an accumulation object
class MyAccumulation {
/**
* A sorted map of the field node of the query and its arguments
*/
private val fieldPaths = TreeMap<String, String>()
/**
* Add a given field and arguments to the sorted map.
*/
fun addFieldPath(path: String, arguments: String) {
fields[path] = arguments
}
/**
* Function to generate the query hash
*/
fun toHash(): String {
val joinedFields = fieldPaths.entries
.joinToString("") { "${it.key}[${it.value}]" }
return HashingLibrary.hashingfunction(joinedFields)
}
A sample reducer implementation would look like the below;
class MyReducer : QueryReducer<MyAccumulation> {
override fun reduceField(
fieldEnvironment: QueryVisitorFieldEnvironment,
acc: MyAccumulation
): MyAccumulation {
if (fieldEnvironment.isTypeNameIntrospectionField) {
return acc
}
// Get your field path, this should account for
// the same node with different parents, and you should recursively
// traverse the parent environment to construct this
val fieldPath = getFieldPath(fieldEnvironment)
// Provide a reproduceable stringified arguments string
val arguments = getArguments(fieldEnvironment.arguments)
acc.addFieldPath(fieldPath, arguments)
return acc
}
}
Finally put it all together;
val queryHash = queryTraverser
.reducePreOrder(MyReducer(), MyAccumulation())
.toHash()
You can now generate a repdocueable hash for a query that does not care about the query structure, only the actual fields that were requested.
Note: These code snippets are in kotlin but are transposable to Java.
Depending on how important is to perform this comparison you can inspect all the elements of Document to determine equality.
If this is to optimize and return the same result for the same input I would totally recommend just compare the strings and kept two entries (one for each string input).
If you really want to go for the deep compare route, you can check the selectionSet and compare each selection.
Take a look at the screenshot:
You can also give EqualsBuilder.html.reflectionEquals(Object,Object) a try but it might inspect too deep (I tried and returned false)
I am new to Java and Vertx and I have a query string with the following format:
GET /examples/1/data?date_1[gt]=2021-09-28&date_1[lt]=2021-10-28
Here I have this date_1 parameter which is within a certain range. I have been using HttpServerRequest class to extract simple parameters like integers but not sure how to proceed with these kind of range parameters.
With the simple parameters, I can do something like:
String param = request.getParam(paramName);
paramAsInteger = Integer.valueOf(paramAsString);
However, confused as to how to deal with the gt and lt options and the fact that we have same parameter twice.
You say that you have difficulties parsing out these tokens. Here's how you can handle this.
The first thing to understand is that the parameter name is NOT "date1"
There are actually two parameters here
2.1. "date_1[gt]" with a value of "2021-09-28"
2.2. "date_1[lt]" with a value of "2021-10-28"
This is because in the URI parameter definition everything before the "=" sign is the parameter name and everything after is the parameter value.
You can just do
String dateAsString = request.getParam("date1[gt]");
paramAsInteger = toDate(dateAsString)
To implement the toDate() function read this simple article how to convert a string object into a data object using a standard library
(link)
Vert.x will treat these parameters as two separate ones. So RoutingContext#queryParam("date_1[gt]") will only give you the value for [gt]. If you want the value for [lt] you need to get that separately.
That being said, you can move this tedious logic into an extra handler and store the values in the RoutingContext. Something like this might be easier:
private void extractDates(RoutingContext ctx) {
var startDate = ctx.queryParam("date_1[gt]");
var endDate = ctx.queryParam("date_1[lt]");
var parsedStartDate = DateTimeFormatter.ISO_LOCAL_DATE.parse(startDate.get(0));
var parsedEndDate = DateTimeFormatter.ISO_LOCAL_DATE.parse(endDate.get(0));
// things we put in the context here can be retrieved by later handlers
ctx.put("startDate", parsedStartDate);
ctx.put("endDate", parsedEndDate);
ctx.next();
}
Then, in your actual handler you can access the two dates as follows:
router.get("/date")
.handler(this::extractDates)
.handler(ctx -> {
var responseBody = ctx.get("startDate") + " - " + ctx.get("endDate");
ctx.end(responseBody);
});
This allows you to keep your actual business logic concise.
I put query param for my list services for example:
tablename/list?query=id:10
it is running but I added other param
'personTNo'
tablename/list?query=id:10&personTNo=101035678
id is Integer but personTNo is Long
when I try to this sql returns select * from TABLENAME WHERE personTNo=10L
but this I want to return without 'L' for Long value. It is my code's a bit section in RepositoryCustom class
public List<TABLENAME> getTable(Specification aTablenameSpec) {
CriteriaBuilder builder = mEntityManager.getCriteriaBuilder();
CriteriaQuery<Object> query = builder.createQuery();
Root<TABLENAME> root = query.from(TABLENAME.class);
String queryWhere = null;
org.hibernate.query.Query hibernateQuery = null;
Predicate predicate = aTablenameSpec.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
query.select(root);
TypedQuery<Object> typedQuery = mEntityManager.createQuery(query);
hibernateQuery = typedQuery.unwrap(org.hibernate.query.Query.class);
String queryString = hibernateQuery.getQueryString();
This row returns with L result, How to remove 'L' value in sql
Use INTEGER() function in the sql query. You can also try CAST() or CONVERT() functions in the query
Based on the problem description and code, it seems safe to assume the tech stack includes: JPA and Spring Data JPA.
And I understand that you want to remove the Long value L suffixes, but it's not clear if that's because the suffixes are causing a problem or exactly why you want the suffixes removed.
I only say that because the example query string appears to be a valid JPA query:
select * from TABLENAME WHERE personTNo = 10L
JPA support for the use of literal values in queries includes support for standard Java numeric (integer/long/float/double) literal value syntax.
Which means the L suffix on the literal Long value of personTNo, as defined in your query (10L), is legitimate, valid, and should not cause a problem.
Please let me know if I've missed the point, made an incorrect assumption, or overlooked something, and I will follow up.
I am having trouble retrieving values from queried documents in MongoDB.
For example, the doc structure is like:
{
"_id": {
"$oid": "50f93b74f9eccc540b302462"
},
"response": {
"result": {
"code": "1000",
"msg": "Command completed successfully"
},
"resData": {
"domain:infData": {
"domain:name": "ritesh.com",
"domain:crDate": "2007-06-15T12:02:36.0000Z",
"domain:exDate": "2013-06-15T12:02:36.0000Z"
}
}
}
}
And the query code is:
DBCollection collection = db.getCollection("domains");
BasicDBObject p = new BasicDBObject("response.resData.domain:infData.domain:name", "ritesh.com");
DBCursor c = collection.find(p);
while(c.hasNext()) {
DBObject obj = c.next();
Object value = obj.get("response.resData.domain:infData.domain:name");
}
It queries fine and fetches the doc, but I can't seem to figure out how to extract the value of "response.resData.domain:infData.domain:name" or other similarly nested values from the DBObject (or BasicDBObject since c.next() returns type BasicDBObject).
I could fetch the objects one at a time like:
((DBObject)obj.get("response")).get("resData")....
but that seems very cumbersome.
I thought since you can put() a nested field value in BasicDBObject like:
basicDBObject.put("response.resData.domain:infData.domain:name", "ritesh.com");
that I could similarly use get() to fetch from the BasicDBObject result using the same kind of key. Like I attempted to do in the code above with:
Object value = obj.get("response.resData.domain:infData.domain:name");
But that is returning a null value.
It's probably something straightforward, but I can't seem to figure it out. And everywhere I've checked on the net the examples only fetch values that aren't nested, from the result. Like
doc.get("name");
instead of something like:
doc.get("name.lastname.clanname");
Any help would be appreciated. Thanks!
There's no way to chain a property name like you're doing using the Java driver (gets for sure, and according to the this, put isn't supposed to work either).
You'll need to get the objects one at a time like you suggested.
((DBObject)obj.get("response")).get("resData")
See here for a potential future feature that would allow your syntax to possibly work (although, likely with a new method name).
I ran into the same problem and I wrote a small function to fetch chained properties.
private Object getFieldFromCursor(DBObject o, String fieldName) {
final String[] fieldParts = StringUtils.split(fieldName, '.');
int i = 1;
Object val = o.get(fieldParts[0]);
while(i < fieldParts.length && val instanceof DBObject) {
val = ((DBObject)val).get(fieldParts[i]);
i++;
}
return val;
}
I hope it helps.
How can I use eval in groovy to evaluate the following String:
{key1=keyval, key2=[listitem1, listitem2], key3=keyval2}
All the list items and keyval is a String.
doing Eval.me("{key1=keyval, key2=[listitem1, listitem2], key3=keyval2}") is giving me the following error:
Ambiguous expression could be either a parameterless closure expression or an isolated open code block;
solution: Add an explicit closure parameter list, e.g. {it -> ...}, or force it to be treated as an open block by giving it a label, e.g. L:{...} at
I want to get HashMap
Is there no way you can get the data in JSON format? Then you could just use one of the parsers mentioned here.
You can parse that string by translating some of the characters, and writing your own binding to return variable names when Groovy tries to look them up, like so:
class Evaluator extends Binding {
def parse( s ) {
GroovyShell shell = new GroovyShell( this );
shell.evaluate( s )
}
Object getVariable( String name ) { name }
}
def inStr = '{key1=keyval, key2=[listitem1, listitem2], key3=keyval2}'
def inObj = new Evaluator().parse( inStr.tr( '{}=', '[]:' ) )
println inObj
But this is a very brittle solution, and getting the data in a friendlier format (as suggested by #Stefan) is definitely the best way to go...