9

I have Spring framework created REST services back-end and now I need to find a way to process complex filters in some requests from the front-end.

I'm using QueryDsl (v3.4.2) framework to do queries construction throughout the back-end.

I think using a FIQL or RSQL parser is the best approach, so I'm trying to integrate jirutka/rsql-parser to my back-end project.

I'm very new to it and also to QueryDsl.

Now I'm confused so here is my request for help:
Has anybody integrated jirutka/rsql-parser and QueryDsl in a rest spring project before? and How?

Jirutka/rsql-parser documentation only says:

Nodes are visitable, so to traverse the parsed AST (and convert it to SQL query maybe), you can implement the provided RSQLVisitor interface or simplified NoArgRSQLVisitorAdapter.

And has the following example on how to do it:

Node rootNode = new RSQLParser().parse("name==RSQL;version=ge=2.0");
rootNode.accept(yourShinyVisitor);

Seems pretty easy, right?

So I crated my visitor like this:

public class RsqlParserVisitor extends NoArgRSQLVisitorAdapter<BooleanExpression> {

Implemented all the methods the interfaces required me to.

Here I add two examples:

@Override
public BooleanExpression visit(AndNode arg0) {
    // TODO Auto-generated method stub

    String methodNameTmp = "AndNode";
    logger.debug(methodNameTmp + ". arg0: " + arg0);
    logger.debug("operator: " + arg0.getOperator().name());
    for (Node node : arg0) {
        logger.debug(methodNameTmpp + ". node: " + node);
    }

    return null; //DO SOMETHING TO CREATE A BooleanExpression;
}

and

@Override
public BooleanExpression visit(EqualNode arg0) {
    // TODO Auto-generated method stub
    String methodNameTmp = "EqualNode";
    logger.debug(methodNameTmp + ". arg0: " + arg0);

    logger.debug("operator: " + arg0.getOperator());
    for (String arg: arg0.getArguments()) {
        logger.debug(methodNameTmp + ". arg: " + arg);
    }

    return null; //DO SOMETHING TO CREATE A BooleanExpression;
}

Now I'm stuck:

a) In order to create a QueryDsl BooleanExpression, I need to know the class I'm processing for example:

    QUser qUser = QUser.user;
    BooleanExpression filter = qUser.firstName.eq("Bob");

or

    PathBuilder<User> user = new PathBuilder<User>(User.class, "user");
    BooleanExpression filter = user.getString("firstName").eq("Bob");

b) When I test my code, it only executes the public BooleanExpression visit(OrNode arg0) method, then nothing. It stops right there.

At that moment I can't do much. Can't create a BooleanExpression yet, since I need to go through some ComparisonNode methods first and then join them with an "or" or "and" boolean expression. Right?

If at least I could go through all the nodes, then I could manage to find a way to pass the class, I'm not worried about it. But don't understand how to traverse all the nodes, and haven't been able to do it.

Any pointers to fix this, will be really appreciated.

elysch
  • 1,846
  • 4
  • 25
  • 43
  • Did you ever find a complete solution for this? I'd love to hear about it. – Justin Lewis Sep 02 '15 at 20:33
  • I ended up tweeking my own... and so far so good: it's working. But every now and then I need to fix things when not working; I know it's not perfect. I would gladly help if you have specific questions. I hope some time soon I find some free time (ha ha) and can put here some pointers, but cant promise anything. :) – elysch Sep 06 '15 at 14:05

1 Answers1

2

As always, after asking the question made great (I think) progress.

Found a way to traverse all the nodes and found a way to pass the QueryDsl PathBuilder<?> Object to the visitor.

PathBuilder<?> is the parent class of all QSomething classes created by QueryDsl.

Now I'm stuck again looking for a way to create the BooleanExpression.

Any help would be greatly appreciated.

I now have the following in my Service:

@Override
public Page<User> getUsers(Emisor pEmisor, int pPage, int pSize, Sort pSort, String pRSQLFilters) {

    Pageable pageable = new PageRequest(pPage, pSize, pSort);

    BooleanExpression filters = null;

    Node queryTree;
    try {
        logger.debug("Parsing query: {}", pRSQLFilters);
        queryTree = new RSQLParser().parse(pRSQLFilters);

        RsqlParserVisitor<BooleanExpression, QUser> rsqlParserVisitor = new RsqlParserVisitor<BooleanExpression, QUser>();
        filters = queryTree.accept(rsqlParserVisitor, QUser.user);

    } catch (TokenMgrError e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (RSQLParserException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }


    Page<User> lista  = userRepository.findAll(filtros, pageable);

    return lista;

}    

And this in the visitor:

public class RsqlParserVisitor<BooleanExpression, A> implements RSQLVisitor<BooleanExpression, EntityPathBase<?>> {
...
    @Override
    public BooleanExpression visit(OrNode node, EntityPathBase<?> param) {
        // TODO Auto-generated method stub

        String nombreTmp = "OrNode";
        printLogicalNode(nombreTmp, node, param);

        return null;
    }

    @Override
    public BooleanExpression visit(EqualNode node, EntityPathBase<?> param) {
        // TODO Auto-generated method stub

        String nombreTmp = "EqualNode";
        printComparisonNode(nombreTmp, node, param);

        return null;
    }

...

    public void printLogicalNode(String pNombreNodo, LogicalNode pNode,
            EntityPathBase<?> pParam) {
        logger.debug(pNombreNodo + ". node: " + pNode + ". param: " + pParam);

        logger.debug("operator: " + pNode.getOperator().name());

        for (Node subNode : pNode) {
            logger.debug(pNombreNodo + ". subNode: " + subNode);
            subNode.accept(this, pParam);    <=========== this was the key to be able to traverse every node
        }
    }

    public void printComparisonNode(String pNombreNodo, ComparisonNode pNode,
            EntityPathBase<?> pParam) {
        logger.debug(pNombreNodo + ". node: " + pNode + ". param: " + pParam);

        logger.debug("Selector: " + pNode.getSelector());
        logger.debug("operator: " + pNode.getOperator());

        for (String argTmp : pNode.getArguments()) {
            logger.debug(pNombreNodo + ". argTmp: " + argTmp);
        }

    }

}
elysch
  • 1,846
  • 4
  • 25
  • 43
  • 1
    Hi, I have a simple open-source project that combines rsql-parser v2.x.x and querydsl-core v3.x.x, it is still in progress. I have derived it from my work experience combining rsql-parser v1.x.x and querydsl. This time I want to expose it in github, you can find it here. https://github.com/vineey/rsql-querydsl Currently, I am working on this branch https://github.com/vineey/rsql-querydsl/tree/feature/filter-api – vine Sep 26 '15 at 11:22
  • FYI, I am planning to finish this library on or before October 2015 ends. – vine Sep 26 '15 at 11:29
  • FYI, I am pleased to announce the first production release of my opensource project rsql-queryds tagged as 1.0.0.RELEASE https://github.com/vineey/archelix-rsql/ – vine May 09 '16 at 15:19
  • Great. I hope I can take a look soon – elysch May 09 '16 at 22:21