Creating morphia query with regex - java

I have a following classes:
class Document{
Map<EnumChannelType, Channel> data;
//some more fields
}
class Channel{
String topic;
//some more fields
}
enum EnumChannelType{
BASIC_CHANNEL(1), ADVANCED_CHANNEL(2),......;
int value;
//constructor and some methods
}
Now I want to query on topic inside Channel. If channelType is known, we can easily query as below:
Query<Document> createQuery(EnumChannelType channelType, String topic){
Query<Document> query = dao.createQuery().disableValidation();
query.field("data." + channelType.name() + ".topic").equal(topic);
return query;
}
But what if I want to get query for given only topic (channelType can be anything)? How can we create query for this?
One option is using or as follows:
Query<Document> createQueryForTopic(String topic) {
Query<Document> query = dao.createQuery().disableValidation();
// add all possible Channel Types
query.or(query.criteria("data." + EnumChannelType.BASIC_CHANNEL.name() + ".topic").equal(topic),
query.criteria("data." + EnumChannelType.ADVANCED_CHANNEL.name() + ".topic").equal(topic),
/*...add criteria for all possible channel types*/);
return query;
}
But this is not feasible if EnumChannelType is changing over time or if EnumChannelType has large number of members (like BASIC_CHANNEL(1), ADVANCED_CHANNEL(2),....).
I'm looking for something like...
Query<Document> createQuery(String topic){
Query<Document> query = dao.createQuery().disableValidation();
// use some regex instead of ????
query.field("data." + ???? + ".topic").equal(topic);
return query;
}

I almost sure that Morphia and MongoDB doesn't support regex on field names. In this case, the best option is use $or operator. You could traverse the whole enum to avoid errors:
List<Criteria> criterias = new ArrayList<Criteria>();
for(EnumChannelType v : EnumChannelType.values()) {
criterias.add(query.criteria("data." + v.name() + ".topic").equal(topic));
}
query.or(criterias.toArray(new Criteria[criterias.size()]);
Remember that $or operator executes queries in parallel and then merge the results.
Info: http://docs.mongodb.org/manual/reference/operator/or/#op._S_or

Related

Generic DAO search with group by / unqiue

I am using hibernate-generic-dao for a searching function. Since I only need to show one of the records if they have same value on a column field. But I am not sure how to achieve this by the search / filter functions.
package com.googlecode.genericdao.search;
PersonContact domain object:
...
#Column(name = "group_key", length = 20)
public String getGroupKey() {
return groupKey;
}
#Formula(value = "(SELECT status from person_contact m " +
" WHERE m.case = case AND m.movement_id = movement_id )")
public String getActiveRecord() {
return activeRecord;
}
...
Search search = new Search();
search.addFilterNotNull("groupKey"); //groupKey is the field I want to use "group by / unqiue" with it
search.addFilterEqual("type","C");
search.addFilterCustom("{activeRecord} != 'I' ");
search.setMaxResults(limit);//for paging
search.setFirstResult(startIdx);
SearchResult<PersonContact> resultObj = PersonContactDAO.searchAndCount(search);
You should probably ask this question by opening an issue in the repository for that project here: https://github.com/vincentruan/hibernate-generic-dao
It seems though as if the project is abandoned, so unless you feel like digging into the details, you should probably try to get away from it.

Search by the combination of multiple parameters via Native Query

I'm currently working on a fetaure that will allow the system to search public services receipts by the combination of 6 parameters which can be null meaning that receipts shouldn't be filtered by this parameter: accountNumber, amountRangeMin, amountRangeMax, dateRangeMin, dateRangeMax, publicServiceId. However making a method for each combination of the parameters is not an option, I'm thinking that there must be a better way, at first my approach was as following:
On my Service I have this method:
public Map<String,Object> findPublicServiceReceiptsByParams(Integer accountNumber, BigDecimal amountRangeMin,
BigDecimal amountRangeMax, LocalDate dateRangeMin, LocalDate dateRangeMax, Integer publicServiceId) {
Map<String,Object> publicServiceReceipts = new HashMap<String,Object>();
String accountNumberFilter = !(accountNumber==null) ? accountNumber.toString() : "AccountNumberTableName";
String amountRangeMinFilter = !(amountRangeMin==null) ? amountRangeMin.toString() : "table.AmountColumnName";
String amountRangeMaxFilter = !(amountRangeMax==null) ? amountRangeMax.toString() : "table.AmountColumnName";
String dateRangeMinFilter = !(dateRangeMin==null) ? dateRangeMin.toString() : "Table.ReceiptCreationDateColumn";
String dateRangeMaxFilter = !(dateRangeMax==null) ? dateRangeMax.toString() : "Table.ReceiptCreationDateColumn";
String publicServiceIdFilter = !(publicServiceId==null) ? publicServiceId.toString() : "table.publicServiceIdColumn";
publicServiceReceipts = publicServiceReceiptRepository.findPublicServiceReceiptsByParams(accountNumberFilter,
amountRangeMinFilter, amountRangeMaxFilter, dateRangeMinFilter, dateRangeMaxFilter,
publicServiceIdFilter);
return publicServiceReceipts;
}
And then in my repository I had:
final static String FIND_PUBLIC_SERVICES_BY_ARGS = "Select (Insert whatever logic should go in here to select columns from receipts the where clause is the one that matters)"
+ " WHERE ACT.ACT_AccountNumber=:accountNumberFilter\n"
+ " AND PSE.PSE_Id=:publicServiceIdFilter\n"
+ " AND PSR.PSR_CreateDate BETWEEN :dateRangeMinFilter AND :dateRangeMaxFilter\n"
+ " AND PSR.PSR_Amount BETWEEN :amountRangeMinFilter AND :amountRangeMaxFilter\n"
+ " order by PSR.PSR_CreateDate desc";
#Query(nativeQuery = true, value = FIND_PUBLIC_SERVICES_BY_ARGS)
Map<String, Object> findPublicServiceReceiptsByParams(#Param("accountNumberFilter") String accountNumberFilter,
#Param("amountRangeMinFilter") String amountRangeMinFilter,
#Param("amountRangeMaxFilter") String amountRangeMaxFilter,
#Param("dateRangeMinFilter") String dateRangeMinFilter,
#Param("dateRangeMaxFilter") String dateRangeMaxFilter,
#Param("publicServiceIdFilter") String publicServiceIdFilter);
}
My reasoning was that if a parameter was null meant that whoever consumed the Web Service is not interested in that paramater so if that happens I set that variable as the Column Name so that it wouldn't affect in the WHERE clause and in theory make it simpler, but what I found was that It would send the names as Strings so it wouldn't be recognized as an sql statement which was the flaw in my thinking and as I said there must be another way other than writing each method for each combination, I appreciate any help :).
You should use the Criteria API, which was designed for creating dynamic queries. Named queries aren't really meant to be used in this case.
With it you can do something like this:
#PersistenceContext
EntityManager em;
List<YourEntity> method(String argument) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourEntity> cq = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cq.from(YourEntity.class);
List<Predicate> predicates = new ArrayList<>();
if (argument == null) {
predicates.add(cb.equal(root.get("yourAttribute"), argument);
}
// rest of your logic goes here
cq.where(predicates.toArray(new Predicate[]{}));
return em.createQuery(cq).getResultList();
}
I found a way to fix this, I did it like this (I'm going to show only the native Query since it's the inly thing that i changed):
DECLARE #actNum varchar(50),#crdNum varchar(50),#pseId varchar(50),#dateMin varchar(50),#dateMax varchar(50),#amountMin varchar(50),#amountMax varchar(50)
SET #actNum = :actNum
SET #crdNum = :crdNum
SET #pseId = :pseId
SET #dateMin = :dateMin
SET #dateMax = :dateMax
SET #amountMin = :amountMin
SET #amountMax = :amountMax
--Whatever Select with joins statement
WHERE ACT.ACT_AccountNumber = CASE WHEN #actNum = 'N/A'
THEN ACT.ACT_AccountNumber
ELSE #actNum END
AND CRD_CardNumber = CASE WHEN #crdNum = 'N/A'
THEN CRD_CardNumber
ELSE #crdNum END
AND PSE.PSE_Id= CASE WHEN #pseId = 'N/A'
THEN PSE.PSE_Id
ELSE #pseId END
AND PSR.PSR_CreateDate >= CASE WHEN #dateMin = 'N/A'
THEN PSR.PSR_CreateDate
ELSE #dateMin END
AND PSR.PSR_CreateDate <= CASE WHEN #dateMax = 'N/A'
THEN PSR.PSR_CreateDate
ELSE #dateMax END
AND PSR.PSR_Amount BETWEEN CASE WHEN #amountMin = 'N/A'
THEN PSR.PSR_Amount
ELSE #amountMin END
AND CASE WHEN #amountMax = 'N/A'
THEN PSR.PSR_Amount
ELSE #amountMax END
ORDER BY PSR.PSR_CreateDate DESC
The backend will send the parameters as either "N/A" (if it shouldn't be used to filter data) or the actual value, this worked fine for me!

How to do batch insert using neo4j cypher and java

I am performing single insert using for each loop for each value.
How can we do batch insert using cypher queries.
Here is my code...
Controller
#PostMapping("/geohash")
public Set<String> create(#RequestParam String name, #RequestBody LatLng[] latLngs) {
double[][] polygonPoints = convertTo2dArrayOfLatLng(latLngs);
Set<String> geoHashesForPolygon = GeoHashUtils.geoHashesForPolygon(6, polygonPoints);
for (String geohash : geoHashesForPolygon) {
min = Math.min(min, geohash.length());
geohashes = neoService.create(name, geohash);
}
return geoHashesForPolygon;
}
I want to insert each geoHashesForPolygon as single node..
Cypher query
#Query("MATCH (c:C) WHERE c.name = {name} CREATE (g: G{name : {geohash}} )<-[:cToG]-(c) RETURN c,g")
public GeohashOfCluster create(#Param("name") String name,#Param("geohash") String geohash);
You can have the params in a list and unwind it to create nodes. Your query would be like WITH [{name:'',geohash:''},{name:'',geohash:''},{name:'',geohash:''}] as data UNWIND data as d MATCH (c:C) WHERE c.name = d.name CREATE (g: G{name :d.geohash} )<-[:cToG]-(c) RETURN c,g
Hope this helps!

Setting Parameters through url javaplay

so as part of some work I've been doing I was given a file with WebServices that are being used in a Swift application. I have zero familiarity with WebServices and only know Java through syntax understanding. I need to call one of these gets with a parameter from the swift application. What I'm trying to figure out first and foremost is how I can call one of these webservices with a parameter from the URL it's associated with. For example down below I want to call the method
http://localhost:9000/ListVehicleByPlateNumber
and I want to specify the parameter through the URL say something like
http://localhost:9000/ListVehicleByPlateNumber?para="123"
But this doesn't assign any value to the parameter and I'm not getting results. If I hardcode so that the string used in the function is = "123" it gives me the results I'm looking for. I just need to know how I can pass this parameter through the url, syntax-wise.
Routes file
GET /ListVehicleByPlateNumber controllers.NewVehicle.listVehicleByPlateNumber(para: String ?="")
Controller
public Result listVehicleByPlateNumber(String para){
NewVehicleModel v = new NewVehicleModel();
List<NewVehicleModel> vehiclesC = v.searchByPlateVehicle(para);
ObjectNode wrapper = Json.newObject();
ObjectNode msg = Json.newObject();
if(vehiclesC != null) {
msg.set("VehicleList", toJson(vehiclesC));
wrapper.set("success", msg);
return ok(wrapper);
}else{
msg.put("error", "There are no vehicles with the plate number");
wrapper.set("error", msg);
return badRequest(wrapper);
}
}
Where it's called
public List<NewVehicleModel> searchByPlateVehicle(String plateNumber){
Transaction t = Ebean.beginTransaction();
List<NewVehicleModel> vehicles = new ArrayList<>();
try {
String sql = "SELECT V.idNewVehicle, V.VehicleType,V.PlateNumber,V.VehicleJurisdiction,V.State,V.Vin,V.Year, " +
"V.Make,V.modelos,V.RegistrationNumber,V.InsuranceCompany,V.PurchaseDate,V.ExpirationDate,V.idPersonaFK " +
"FROM NewVehicle V " +
"WHERE V.PlateNumber = :plateNumber";
RawSql rawSql = RawSqlBuilder.parse(sql)
.columnMapping("V.idNewVehicle", "idNewVehicle")
.columnMapping("V.State", "state")
.columnMapping("V.VehicleType", "vehicleType")
.columnMapping("V.PlateNumber", "plateNumber")
.columnMapping("V.VehicleJurisdiction", "vehicleJurisdiction")
.columnMapping("V.Vin", "vin")
.columnMapping("V.Year", "year")
.columnMapping("V.Make", "make")
.columnMapping("V.modelos", "modelos")
.columnMapping("V.RegistrationNumber", "registrationNumber")
.columnMapping("V.InsuranceCompany", "insuranceCompany")
.columnMapping("V.PurchaseDate", "purchaseDate")
.columnMapping("V.ExpirationDate", "expirationDate")
.columnMapping("V.idPersonaFK", "idPersonaFK")
.create();
Query<NewVehicleModel> query = Ebean.find(NewVehicleModel.class);
query.setRawSql(rawSql)
.setParameter("plateNumber", plateNumber);
vehicles = query.findList();
t.commit();
}
catch (Exception e){
System.out.println(e.getMessage());
}finally {
t.end();
}
return vehicles;
}
Found my own answer. I ended up casting from Integer to String here's how it looks in routes
GET /ListVehicleByPlateNumber/:para controllers.NewVehicle.listVehicleByPlateNumber(para: Integer )
Controller
public Result listVehicleByPlateNumber(int para){
String p = String.valueOf(para);
URI Format for value 123 example.
http://localhost:9000/ListVehicleByPlateNumber/123

Java Mongo DB Unable to get valid next id

I am trying insert an item in MongoDB using Java MongoDB driver.Before inserting I am trying to get nextId to insert,but not sure why I am always getting nextId as 4 .I am using below given method to get nextId before inserting any item in Mongo.
private Long getNextIdValue(DBCollection dbCollection) {
Long nextSequenceNumber = 1L;
DBObject query = new BasicDBObject();
query.put("id", -1);
DBCursor cursor = dbCollection.find().sort(query).limit(1);
while (cursor.hasNext()) {
DBObject itemDBObj = cursor.next();
nextSequenceNumber = new Long(itemDBObj.get("id").toString()) + 1;
}
return nextSequenceNumber;
}
I have total 13 record in my mongodb collection.What I am doing wrong here?
Please don't do that. You don't need create a bad management id situation as the driver already do this in the best way, just use the right type and annotation for the field:
#Id
#ObjectId
private String id;
Then write a generic method to insert all entites:
public T create(T entity) throws MongoException, IOException {
WriteResult<? extends Object, String> result = jacksonDB.insert(entity);
return (T) result.getSavedObject();
}
This will create a time-based indexed hash for id's which is pretty much more powerful than get the "next id".
https://www.tutorialspoint.com/mongodb/mongodb_objectid.htm
How can you perform Arithmetic operations like +1 to String
nextSequenceNumber = new Long(itemDBObj.get("id").toString()) + 1;
Try to create a Sequence collection like this.
{"id":"MySequence","sequence":1}
Then use Update to increment the id
// Query for sequence collection
Query query = new Query(new Criteria().where("id").is("MySequence"));
//Increment the sequence by 1
Update update = new Update();
update.inc("sequence", 1);
FindAndModifyOptions findAndModifyOptions = new FindAndModifyOptions();
findAndModifyOptions.returnNew(true);
SequenceCollection sequenceCollection = mongoOperations.findAndModify(query, update,findAndModifyOptions, SequenceCollection.class);
return sequenceModel.getSequence();
I found the work around using b.collection.count().I simply find the total count and incremented by 1 to assign id to my object.

Categories

Resources