JPA Criteria API "IN" Predicate not working - java

I need to write the following with JPA Criteria API:
p.id IS NULL AND nv.organizationalstat IN ('EMPLOYEE', 'FELLOW')`
My code is:
List<String> corePredicateInList = Arrays.asList("EMPLOYEE", "FELLOW");
In<String> corePredicateIn = cb.in(nvRoot.get("organizationalstat"));
corePredicateInList.forEach(p -> corePredicateIn.value(p)); // Setting opts into IN
Predicate p1CorePredicate = cb.and(
cb.isNull(plans.get("id")),
cb.in(nvRoot.get("organizationalstat"), corePredicateIn)
);
The runtime error is
antlr.NoViableAltException: unexpected token: in
...
where ( ( ( ( ( ( generatedAlias0.id is null )
and ( generatedAlias1.organizationalstat in (:param0, :param1) in () ) )
or ( generatedAlias0.id is not null ) )
It looks like there's a double IN somewhere. There are no syntax errors in the code.
I can't do cb.in(List<String>) directly, that's wrong syntax. I have to go through in.value() as indicated in this thread.

Using CriteriaBuilder.In
Predicate p1CorePredicate = cb.and(cb.isNull(plans.get("id")),corePredicateIn);
Or using Expression.In
Predicate p1CorePredicate = cb.and(cb.isNull(plans.get("id")),
nvRoot.get("organizationalstat").in(corePredicateInList));

A solution that worked for me is to do root.in(List<String>) (not cb.in), as follows:
List<String> corePredicateInList = Arrays.asList("EMPLOYEE", "FELLOW");
Predicate p1CorePredicate = cb.and(
cb.isNull(plans.get("id")),
nvRoot.get("organizationalstat").in(corePredicateInList)
);

Related

Searching into postgres Jsonb column using CriteriaBuilder JPA

I am facing issues while retrieving/searching through Postgres JSONB column using CriteriaBuilder.
Postgres Query : SELECT * FROM table where lower(jsonb_extract_path(tags,'Case')::text) = lower('"normal"') as text));
Tags: datatype of tags column is JSONB.
Above query is working fine using postgres client.
But I was facing issues while trying to get it done from CriteriaBuilder.
Following is my code:
private Specification<TemplateMetadata> findByTagsFilter(Map<String, String[]> requestParameterMap) {
return (root, query, builder) -> {
List<Predicate> predicates = new ArrayList<>();
if (!StringUtils.isEmpty(requestParameterMap)) {
predicates = requestParameterMap.entrySet().stream()
.filter(keyValue1 -> validateKeyValues(keyValue1.getKey()))
.map(keyValue2 -> Arrays.asList(keyValue2.getValue()).stream()
.map(v -> builder.equal(builder.function("jsonb_extract_path", String.class, builder.lower(root.<String>get("tags")), builder.literal(keyValue2.getKey())) , builder.lower(builder.literal("\""+v+"\"")).as(JsonDataType.class)))
.flatMap(list -> list.distinct())
.collect(Collectors.toList());
}
return builder.and(
predicates.toArray(new Predicate[predicates.size()])
);
};
}
requestParameterMap is filled by REST (API URI should looks like : ?testtag1=testtag1&testtag2=testtag2)
I am getting follwoing Error
ERROR: function lower(jsonb) does not exist\n Hint: No function matches the given name and argument types. You might need to add explicit type casts.\n Position: 444
Can some one help me on this.

Querydsl: How to write a "complex" query

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

When to use $em->getConnection()->close();, should I use it after every query

I am new to doctrine2(PHP) which is inspired from hibernate(java). I have searched at many places by didn't get appropriate answer.
what is the use of following code of line, what will happen if I do not use it?
$em->getConnection()->close();
For example If I have made following method in my class :
public static function deleteWish( $about_ilook_user_id, $link_ilook_user_id, $flush_automatic = TRUE )
{
$em = \Zend_Registry::get('em');
$new_link_wishes_objs = $em->getRepository( '\Entities\new_link_wishes' )->findBy( array( 'ilookUser' => $about_ilook_user_id, 'link_ilook_user_id' => $link_ilook_user_id ) );
foreach ( $new_link_wishes_objs as $new_link_wishes_obj )
{
$em -> remove($new_link_wishes_obj);
}
$em -> flush();
$em->getConnection()->close();
}
Should I call getConnection()->close(); on $em(entity manager).

Neo4j check if node exists before creating?

I have a feeling I'm going about this all wrong. But anyway.
I have an sql database which has essentially a purposefully denormalised table which I've constructed to make this task easier for me, so I can just grab stuff from one table.
What I have is a table of pairs, something like this:
user_lo | user_hi | something_else | other stuff
1000 | 1234 | 1231251654 | 123
1050 | 1100 | 1564654 | 45648
1080 | 1234 | 456444894648 | 1
And so on.
So for my neo4j graph db, I want each user id as a node, the other stuff isn't too important but will be the stuff in the relations basically.
I only want one node for each user, so my feeling is that if I do something like this:
while (rs.next()) {
node_lo = db.createNode();
node_lo.setProperty("user_id", rs.getInt(1));
node_hi = db.createNode();
node_hi.setProperty("user_id", rs.getInt(2));
}
That when we add the node with user_id 1234 for the second time, it will just create a new node, but I what I want is for it to just grab this node instead of creating it so I can add it to the relationship to 1080 in this case.
So what is the way to do this?
Have you looked at CREATE UNIQUE?
If you can't use Cypher, maybe you can use unique nodes?
Use an index to search, and if no result of found, create a new one.
Index<Node> userIndex = graphDatabaseService.index().forNodes('UserNodes');
IndexHits<Node> userNodes = userIndex.get('id', 1234);
if(!userNodes.hasNext()){
//Create new User node
} else {
Node userNode = userNodes.next();
}
Is this the type of operation you are looking for?
You'll probably want to use the UniqueNodeFactory provided by Neo4j.
public Node getOrCreateUserWithUniqueFactory( String username, GraphDatabaseService graphDb )
{
UniqueFactory<Node> factory = new UniqueFactory.UniqueNodeFactory( graphDb, "UserNodes" )
{
#Override
protected void initialize( Node created, Map<String, Object> properties )
{
created.setProperty( "id", properties.get( "id" ) );
}
};
return factory.getOrCreate( "id", id );
}
Normalize your SQL tables to look like nodes and relationships. Then with cypher in your migration you can make the migration rerunnable by something like
start a = node:node_auto_index('id:"<PK_Value>"')
delete a
create a = {id: "<PK_VALUE>", etc}
for nodes and since you should have in your many-to-many middle table:
start LHS = node:node_auto_index('id:"<LHS_PK>"'),
RHS = node:node_auto_index('id:"<RHS_PK>"')
create unique LHS=[:<relType> {<rel props>}]->RHS
now you will end up with no duplicates and can rerun as much as you like.
use this function:
where:
ID is the key which you want to check if already exist
Type: is the type of the node ( the label)
this function will create the node and return it, then you can add more properties.
public static Node getOrCreateUserWithUniqueFactory( long ID, GraphDatabaseService graphDb, String Type )
{
UniqueFactory<Node> factory = new UniqueFactory.UniqueNodeFactory( graphDb, Type )
{
#Override
protected void initialize( Node created, Map<String, Object> properties )
{
created.addLabel( DynamicLabel.label( Type ) );
created.setProperty( "ID", properties.get( "ID" ) );
}
};
return factory.getOrCreate( "ID", ID );
}
using cypher query, you can create a unique node with the following syntax,
CYPHER 2.0 merge (x:node_auto_index{id:1})
when making a REST call, one can make batch insertion like
$lsNodes[] = array(
'method'=> 'POST', 'to'=> '/cypher',
'body' => array(
'query' => 'CYPHER 2.0 merge (x:node_auto_index{id_item:{id}})',
'params' => array('id'=>1)
),
'id'=>0
);
$sData = json_encode($lsNodes);
similarly for creating relationships in a batch request, do the following
$lsNodes[] = array(
'method'=> 'POST', 'to'=> '/cypher',
'body' => array(
'query' => 'start a=node:node_auto_index(id={id1}), b = node:node_auto_index(id={id2}) create unique a-[:have{property:30}}]-b;',
'params' => array(
'id1' => 1, 'id2'=> 2
)
),
'id' => 0
);
$sData = json_encode($lsNodes);

Use sun-codemodel to generate expression like b().c()

I'm trying to use sun-codemodel to generate source code, checking the API for a long time however no luck.
JBlock body2 = method2.body();
JInvocation arg = body2.invoke( "a" ).arg( xxx ).invoke( "c" ).arg( xxx );
only generate the first part, i.e. a(xxx)
Any ideas?
Problem solved by using
JInvocation invoke = JExpr._this()
.invoke( "fun" )
.arg( "arg1" )
.invoke( "fun2" )
.arg( "arg2" );
method.body().add(invoke);

Categories

Resources