Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jirutka/rsql-parser and QueryDSL

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.

like image 592
elysch Avatar asked Oct 21 '14 16:10

elysch


People also ask

What is RSQL parser?

RSQL is a query language for parametrized filtering of entries in RESTful APIs. It's based on FIQL (Feed Item Query Language) – an URI-friendly syntax for expressing filters across the entries in an Atom Feed.

What is RSQL?

RSQL is a query language for RESTful APIs. It uses an URI-friendly syntax, there are no unsafe characters, so URL encoding is not required. It supports some logical and comparison operators and can be easily extended by custom operators.

What is FIQL?

Introduction The Feed Item Query Language (FIQL, pronounced "fickle") is a simple but flexible, URI-friendly syntax for expressing filters across the entries in a syndicated feed.


1 Answers

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);
        }

    }

}
like image 73
elysch Avatar answered Nov 10 '22 04:11

elysch