Jooq dsl one to many relations - java

I'm using Spring Data + Jooq DSL. As result entity I'm using not jooq generated entity, but simple one, without any annotations and for relations One To Many getting result:
[{
"id":2,
"name":"James",
"addresses":[
{
"id":null,
"country":null,
"street":null
}
]
}]
Is any way to return an empty array for addresses?
My code to perform a request:
public Set<User> getUserById(Set<Long> id) {
Set<User> result = new HashSet<>();
ResultQuery users = dsl.select(
field("u.id", Long.class).as("id"),
field("u.name", String.class).as("name"),
field("a.id", Long.class).as("addresses_id"),
field("a.country", String.class).as("addresses_country"),
field("a.street", String.class).as("addresses_street")
).from("schema.user_table u")
.leftJoin("schema.address_table a")
.on("u.id = a.user_id")
.where(field("u.id").in(id));
try(ResultSet rs = users.fetchResultSet()) {
JdbcMapper<User> mapper = JdbcMapperFactory
.newInstance()
.addKeys("id")
.newMapper(User.class);
result = mapper.stream(rs).collect(Collectors.toSet());
} catch (Exception e) {
e.printStackTrace();
}
return result;
}

Why not just use SQL/JSON to produce JSON documents directly from within your database?
public String getUserById(Set<Long> id) {
return dsl.select(coalesce(jsonArrayAgg(
jsonObject(
key("id").value(field("u.id", Long.class)),
key("name").value(field("u.name", String.class)),
key("addresses").value(coalesce(
jsonArrayAgg(jsonObject(
key("id").value(field("a.id", Long.class)),
key("country").value(field("a.country", String.class)),
key("street").value(field("a.street", String.class))
)),
jsonArray()
))
),
jsonArray()
)))
.from("schema.user_table u")
.leftJoin("schema.address_table a")
.on("u.id = a.user_id")
.where(field("u.id").in(id))
.fetchSingle().value1().data();
}
If you really need the intermediate User representation, then you can either:
Use Jackson or Gson to map the JSON document to the nested User DTO structure using reflection (works with jOOQ 3.14)
Use jOOQ 3.15's new MULTISET value constructor operator or MULTISET_AGG aggregate function along with ad-hoc converters, see below:
public Set<User> getUserById(Set<Long> id) {
return dsl.select(
field("u.id", Long.class),
field("u.name", String.class),
multisetAgg(
field("a.id", Long.class),
field("a.country", String.class),
field("a.street", String.class)
).convertFrom(r -> r == null
? Collections.<Address>emptyList()
: r.map(Records.mapping(Address::new)))
)
.from("schema.user_table u")
.leftJoin("schema.address_table a")
.on("u.id = a.user_id")
.where(field("u.id").in(id))
.fetchSet(Records.mapping(User::new));
}
Side note on code generation and execution
While not strictly relevant to this question, unless your schema is dynamic (not known at compile time), I really urge you to reconsider using source code generation. If you're not using it, you're missing out on a lot of jOOQ API advantages, just like when you're executing a jOOQ query with something other than jOOQ.

For me worked specify as a key addressId:
.addKeys("id", "addresses_id")

Related

HIbernate Search not returning search results on primary key

I'm using HIbernate search(5.8.2). The search works smoothly on every field except for the primary key. It returns an empty list when I pass anything to it. I followed the hibernate documentation, used #documentId annotation for primary key. What am I missing?
Here's my model:
#SuppressWarnings("serial")
#Entity
#Indexed
#Table(name = "MAIN",schema="maindb")
public class MAIN implements Serializable {
#Id
#DocumentId
private String poNo; // my primary key which has values like "PO123"
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String postatus;
My search function:
public List<?> search(String poNumber, String status) {
QueryBuilder qb =
fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(MAIN.class).get();
BooleanQuery.Builder finalLuceneQuery = new BooleanQuery.Builder();
org.hibernate.query.Query hibQuery =
fullTextSession.createFullTextQuery(finalLuceneQuery.build(),MAIN.class);
org.apache.lucene.search.Query querypono1 =
qb2.simpleQueryString().onField("poNo").matching(poNumber)
.createQuery();
org.apache.lucene.search.Query queryStatus =
qb.simpleQueryString().onField("po01_status")
.matching("postatus").createQuery();
finalLuceneQuery.add(querypono1, BooleanClause.Occur.MUST);
finalLuceneQuery.add(queryStatus , BooleanClause.Occur.MUST);
hibQuery.setFirstResult(0);
List<?> resultArchive = new ArrayList<String>();
try {
result = hibQuery.getResultList();
} catch (Exception e) {
e.printStackTrace();
// log.log(ERROR, "ERROR FETCHING RESULT LIST FROM DATABASE");
}
return result;
}
The issue is that "PO123" is transformed to "po123" by the simple query string parser. I wonder why, I have to check that, it's probably a bug, or it's at least an unexpected behavior.
That being said, you shouldn't use the simpleQuery() entry point for an exact matching.
Replace:
org.apache.lucene.search.Query querypono1 = qb2.simpleQueryString().onField("poNo").matching(poNumber).createQuery();
By:
org.apache.lucene.search.Query querypono1 = qb2.keyword().onField("poNo").matching(poNumber).createQuery();
(keyword() instead of simpleQueryString())
I will follow up on this issue though as it's not the behavior I would have expected. Thanks for raising it.
-> JIRA issue: https://hibernate.atlassian.net/browse/HSEARCH-3039 , will be included in the upcoming 5.10.0.Final.

Hibernate OGM result of aggregation query

I am using hibernate ogm 5.1 in my application and I construct this query. This query
String query = "db.SWMessages.aggregate( [ {0}, {1} ] )";
Document q1 = new Document("$match",new Document("messageUuid",new
Document("$ne" , id.toString())));
Document q2 = new Document("$group", new Document("_id",
"$localReference").append("max", new Document("$max","$creationDate")));
Object[] param = { q1.toJson(), q2.toJson() };
String nativeQuery = MessageFormat.format(query, param);
List<SWMessageR> records = (List<SWMessageR>) em.createNativeQuery(nativeQuery, SWMessageImpl.class)
.getResultList();
the above code produces the query like this:
db.SWMessages.aggregate([ { "$match" : { "messageUuid" : { "$ne" : "9c1464d7-311d-4b50-8b81-005bad055232" } } } , { "$group" : { "_id" : "$localReference", "max" : { "$max" : "$creationDate" } } } ])
My question is that the result of this query would return back an entity object which is the SWMessageR?
Well, yes the way you execute your query it will return a List Object of your class. In this way you don't need to use casting, so you can use it like this:
List<SWMessageR> records = em.createNativeQuery(nativeQuery, SWMessageImpl.class).getResultList();
Though, in case you are expecting a single result, I would suggest to use this way:
SWMessageR record = (SWMessageR) em.createNativeQuery( query1, SWMessageR.class ).getSingleResult();
Have a look at the Hibernate Search Documentation Query Section and everything will be clear.
Cheers.
Hibernate OGM can convert the result into an entity: https://docs.jboss.org/hibernate/stable/ogm/reference/en-US/html_single/#ogm-mongodb-queries-native
I'm not sure what your query returns, but if it's a document in the collection that represents your entity, I expect it to work.
You can see an example in our code base:
https://github.com/hibernate/hibernate-ogm/blob/master/mongodb/src/test/java/org/hibernate/ogm/datastore/mongodb/test/query/nativequery/MongoDBSessionCLIQueryTest.java#L111
Make sure to pass the expected Entity type, it will look something like this:
List<SWMessageR> results = em
.createNativeQuery( nativeQuery, SWMessageR.class )
.getResultList();
Assuming that SWMessageR is the entity class.

Spring Data MongoDB and Bulk Update

I am using Spring Data MongoDB and would like to perform a Bulk Update just like the one described here: http://docs.mongodb.org/manual/reference/method/Bulk.find.update/#Bulk.find.update
When using regular driver it looks like this:
The following example initializes a Bulk() operations builder for the items collection, and adds various multi update operations to the list of operations.
var bulk = db.items.initializeUnorderedBulkOp();
bulk.find( { status: "D" } ).update( { $set: { status: "I", points: "0" } } );
bulk.find( { item: null } ).update( { $set: { item: "TBD" } } );
bulk.execute()
Is there any way to achieve similar result with Spring Data MongoDB ?
Bulk updates are supported from spring-data-mongodb 1.9.0.RELEASE. Here is a sample:
BulkOperations ops = template.bulkOps(BulkMode.UNORDERED, Match.class);
for (User user : users) {
Update update = new Update();
...
ops.updateOne(query(where("id").is(user.getId())), update);
}
ops.execute();
You can use this as long as the driver is current and the server you are talking to is at least MongoDB, which is required for bulk operations. Don't believe there is anything directly in spring data right now (and much the same for other higher level driver abstractions), but you can of course access the native driver collection object that implements the access to the Bulk API:
DBCollection collection = mongoOperation.getCollection("collection");
BulkWriteOperation bulk = collection.initializeOrderedBulkOperation();
bulk.find(new BasicDBObject("status","D"))
.update(new BasicDBObject(
new BasicDBObject(
"$set",new BasicDBObject(
"status", "I"
).append(
"points", 0
)
)
));
bulk.find(new BasicDBObject("item",null))
.update(new BasicDBObject(
new BasicDBObject(
"$set", new BasicDBObject("item","TBD")
)
));
BulkWriteResult writeResult = bulk.execute();
System.out.println(writeResult);
You can either fill in the DBObject types required by defining them, or use the builders supplied in the spring mongo library which should all support "extracting" the DBObject that they build.
public <T> void bulkUpdate(String collectionName, List<T> documents, Class<T> tClass) {
BulkOperations bulkOps = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, tClass, collectionName);
for (T document : documents) {
Document doc = new Document();
mongoTemplate.getConverter().write(document, doc);
org.springframework.data.mongodb.core.query.Query query = new org.springframework
.data.mongodb.core.query.Query(Criteria.where(UNDERSCORE_ID).is(doc.get(UNDERSCORE_ID)));
Document updateDoc = new Document();
updateDoc.append("$set", doc);
Update update = Update.fromDocument(updateDoc, UNDERSCORE_ID);
bulkOps.upsert(query, update);
}
bulkOps.execute();
}
Spring Mongo template is used to perform the update. The above code will work if you provide the _id field in the list of documents.

Converting ExecutionResult object to json

I need to expose a public API, and i need to convert the result of a cypher query into json. I have begun the process, but i am having problems serializing an object of type scala.collection.convert.Wrappers$SeqWrapper that gets returned when using collect() in cypher.
Here is the cypher query:
MATCH (orders:Orders {id:'locationrestaurant'}), (order:Order), (orders)-[:hasOrder]-(order),(order)-[:orderedProduct]->(product),(client)-[:ordered]->(order) return (order),(client), collect(product) as products;
How can i handle this type of object? Can i cast it to a List? Also, are there any libraries for converting ExecutionResult to json?
If you need any more details, please ask. Here is my code
public QueryResult runCypher(String query, Map<String,Object> params)
{
QueryResult result = new QueryResult();
Transaction tx = service.beginTx();
ExecutionResult execResult = null;
boolean success = true;
System.out.println(query);
try
{
if(params!=null) execResult = engine.execute(query, params);
else execResult = engine.execute(query);
result.result = getReturnedObjectsToJson(execResult);
}
catch(Exception e)
{
System.out.println(e.getMessage()+" exception message");
result.result = e.getMessage();
success = false;
}
finally
{
if(success) tx.success();
else tx.failure();
}
tx.close();
result.success = success;
return result;
}
Basically, getReturnedObjectsToJson does the work.
How can i handle this type of object?
data.get("labels") instanceof java.util.Collection
Can i cast it to a List?
yes
Also, are there any libraries for converting ExecutionResult to json?
ExecutionResult is iterable, i think you can use any popular java json framework, for example gson or jackson
You can use Jacksons databind framework. That will allow you to turn it back into an execution result at the destination.
If you want to get the actual graph back from an execution result so that the Json is in some kind of graph schema. That is a lot more work.
I have been writing a library that will parse arbitary graphs into Cypher and execution results back into graphs to work with a virtual query builder, and it is reasonably hard work. Hopefully it will all be working soon. :)

Querying Morphia by Id

I am using Morphia, the Pojo mapper for MongoDB, and I find difficult a task that in my view should be very simple: getting an object by id. I am able to find all the objects in a collection but I cannot figure out the simple task of querying using an id I got from the list. I am actually talking about the ObjectId. If I try to render it in JSON I see
This question seems incomplete.
It also seems like the answer to you question is on the Morphia QuickStart page. Seems to be as simple as follows.
Datastore ds = morphia.createDatastore("testDB");
String hotelId = ...; // the ID of the hotel we want to load
// and then map it to our Hotel object
Hotel hotel = ds.get(Hotel.class, hotelId);
So you'll definitely need more details.
Datastore ds = morphia.createDatastore("testDB");
String hotelId = "516d41150364a6a6697136c0"; // the ID of the hotel we want to load
ObjectId objectId = new ObjectId(hotelId);
// and then map it to our Hotel object
Hotel hotel = ds.get(Hotel.class, objectId);
If you're finding by id and the id is provided by the user (means that it could be whatever type of data), you shouldn't use the solutions given above.
As explained in the documentation, an ObjectId consists of 12 bytes, so if you pass something else to new ObjectId(myValue), your code will throw an IllegalArgumentException.
Here is how I implemented the method to find by id :
public Model findById(String id) throws NotFoundException {
if (!ObjectId.isValid(id)) {
throw new NotFoundException();
}
ObjectId oid = new ObjectId(id);
Model m = datastore().find(Model.class).field("_id").equal(oid).get();
if (m == null) {
throw new NotFoundException();
}
return m;
}
The previous explanation used the deprecated methods: datastore().find(Model.class).field("_id").equal(oid).get(); My variant of it on Morphia 2.2.10:
public Optional<Hotel> findById(final String id) {
if (!ObjectId.isValid(id)) {
throw new SomeException();
}
final Query<Hotel> query = datastore.find(Hotel.class)
.filter(eq("_id", new ObjectId(id)));
return Optional.ofNullable(query.first());
}

Categories

Resources