MongoTemplate database query, instead of in-service code, to get required data - java

I need to create a mongotemplate database query to get a specific number of elements into a list.
At the moment I just get all the elements with findAll(), and then I modify the obtained data using code that I have writen within the service class.
Initially, I have a Laptop class with fields price::BigDecimal and name::String and I use findAll() to get a list of them.
Then I put those in a HashMap, where key is the name field, sorted from most expensive to cheapest.
Map<String, List<Laptop>> laptopsMap = laptopsFrom.stream()
.collect(Collectors.groupingBy(Laptop::getName,
Collectors.collectingAndThen(Collectors.toList(),
l -> l.stream()
.sorted(Comparator.comparing(Laptop::getPrice).reversed())
.collect(Collectors.toList())
))
);
So the results are like below:
[{"MSI", [2200, 1100, 900]},
{"HP", [3200, 900, 800]},
{"Dell", [2500, 2000, 700]}]
Then, I use the code in the bottom of the question, to create a Laptop list with the following contents:
[{"HP", 3200}, {"Dell", 2500}, {"MSI", 2200},
{"Dell", 2000}, {"MSI", 1100}, {"HP", 900},
{"MSI", 900}, {"HP", 800}, {"Dell", 700}]
So basically, I iterate the map and from each key, I extract the next in line element of the list.
do {
for (Map.Entry<String, List<Laptop>> entry :
laptopsMap.entrySet()) {
String key = entry.getKey();
List<Laptop> value = entry.getValue();
finalResultsList.add(value.get(0));
value.remove(0);
if (value.size() == 0) {
laptopsMap.entrySet()
.removeIf(pr -> pr.getKey().equals(key));
} else {
laptopsMap.replace(key, value);
}
}
} while(!laptopsMap.isEmpty());
I instead of all this in-class code need to use a mongoTemplate database argument, but I cant seem to figure out how to create such a complex query. I have read material about Aggregation but I have not found anything helpful enough. At the moment, I have started putting a query together as shown below:
Query query = new Query();
query.limit(numOfLaptops);
query.addCriteria(Criteria.where(Laptop.PRICE).gte(minPrice));

Related

How to map list of elements using java

I am new to java I want to map my for each loop list elements using map and get the value from map, but I am not able to use map in my for each statement. For your reference I post my code
private Item getItemManufacturerPriceCodes(Item item) {
List<ItemPriceCode> itemPriceCodes = item.getItemPriceCodes;
for(ItemPriceCode ipc : itemPriceCodes) {
Optional<ManufacturerPriceCodes> mpc = manufacturerPriceCodesRepository.findByManufacturerIDAndPriceCodeAndRecordDeleted(item.getManufacturerID(), ipc.getPriceCode(), NOT_DELETED);
if(mpc.isPresent())
ipc.setManufacturerPriceCode(mpc.get().getName());
}
item.getItemPriceCodes()
.removeIf(ipc -> DELETED.equals(ipc.getRecordDeleted()));
return item;
}
I want to use my query code line above for each loop and pass list of price code inside map then get the values from map. this above code works fine . when I pass one price code value the loop move one time but when pass ten value in that case loop move ten times. But I want loop always move one time how many value I pass using map. how can I do it.
I want to use below line above for each loop
Optional<ManufacturerPriceCodes> mpc = manufacturerPriceCodesRepository.findByManufacturerIDAndPriceCodeAndRecordDeleted(item.getManufacturerID(), ipc.getPriceCode(), NOT_DELETED);
Getting same result using map. First of pass list of elements price code inside map and get the values from map then set those values.
I tried below way but it not working as above programme
private Item getItemManufacturerPriceCodes(Item item) {
List<ItemPriceCode> itemPriceCodes = item.getItemPriceCodes();
Optional<ManufacturerPriceCodes> mpc = manufacturerPriceCodesRepository.findByManufacturerIDAndPriceCodeAndRecordDeleted(item.getManufacturerID(), itemPriceCodes, NOT_DELETED);
for(ItemPriceCode ipc : itemPriceCodes) {
if(mpc.isPresent())
ipc.setManufacturerPriceCode(mpc.get().getName());
}
item.getItemPriceCodes()
.removeIf(ipc -> DELETED.equals(ipc.getRecordDeleted()));
return item;
}
How can I map these list of price code and set them. My main aim is
modify those piece of code using map and work same as my above code
that i explain in my problem statement.
Is it possible to modify those codes using map.
Seems like you are trying to find a way to call setManufacturerPriceCode on a list of itemPriceCodes based on the ManufacturerPriceCodes that references them. Assuming there is no reference from the ItemPriceCode to the ManufacturerPriceCodes, I'd go about this differently:
In your ManufacturerPriceCodesRepository repository:
#Query("Select ipc.id, mpc.name from ManufacturerPriceCodes mpc join mpc.priceCode ipc where mpc.id = :id and ipc in :itemPriceCodes and mpc.recordDeleted = :notDeleted")
List<Object[]> findMFPNameByIdAndRecordDeletedAndPriceCodes(String Id, <type> recordDeleted, List<ItemPriceCode> itemPriceCodes)
This will return a List<Object[]>, which each Object[] representing one ItemPriceCode id/name pair. You can use this in your getItemManufacturerPriceCodes method:
private Item getItemManufacturerPriceCodes(Item item) {
List<ItemPriceCode> itemPriceCodes = item.getItemPriceCodes();
List<Object[]> keyPairs= manufacturerPriceCodesRepository.findMFPNameByIdAndRecordDeletedAndPriceCodes(item.getManufacturerID(), NOT_DELETED, itemPriceCodes);
Map<String,String> ipcToMFPNameMap = keyPairs.stream().collect(
Collectors.toMap(x -> x[0], x->x[1]));
itemPriceCodes
.forEach(ipc ->{
if (ipcToMFPNameMap.get(ipc.getId())!=null)
ipc.setManufacturerPriceCode(ipcToMFPNameMap.get(ipc.getId());
})
.removeIf(ipc -> DELETED.equals(ipc.getRecordDeleted()));
return item;
}
I'm sure there are more elegant ways than dealing with object[] with Spring, and certainly for the stream handling, but this is a general idea.

filter KeyValueGrouped Dataset in spark

I have a typed dataset of a custom class and use groupbykey method on it. You know that it results a KeyValueGroupedDataset. I want to filter this new dataset but there is no filter method for this type of dataset. So, My question is: How can I filter on this type of dataset? (Java solution is needed. spark version: 2.3.1).
sampleData:
"id":1,"fname":"Gale","lname":"Willmett","email":"gwillmett0#nhs.uk","gender":"Female"
"id":2,"fname":"Chantalle","lname":"Wilcher","email":"cwilcher1#blinklist.com","gender":"Female"
"id":3,"fname":"Polly","lname":"Grandisson","email":"pgrandisson2#linkedin.com","gender":"Female"
"id":3,"fname":"Moshe","lname":"Pink","email":"mpink3#twitter.com","gender":"Male"
"id":2,"fname":"Yorke","lname":"Ginnelly","email":"yginnelly4#apple.com","gender":"Male"
And What I did:
Dataset<Person> peopleDS = spark.read().format("parquet").load("\path").as(Encoders.bean(Person.class));
KeyValueGroupedDataset<String, Person> KVDS = peopleDS.groupByKey( (MapFunction<Person, String> ) f -> f.getGender() , Encoders.STRING());
//How Can I filter on KVDS's id field?
Update1 (use of flatMapGroups):
Dataset<Person> persons = KVDS.flatMapGroups((FlatMapGroupsFunction <String,Person,Person>) (f,k) -> (Iterator<Person>) k , Encoders.bean(Person.class));
Update2 (use of MapGroups)
Dataset<Person> peopleMap = KVDS.mapGroups((MapGroupsFunction <String,Person,Person>) (f,g) -> {
while (g.hasNext()) {
//What can I do here?
}
},Encoders.bean(Person.Class);
Update3 : I want to filter those groups that distinct of their ids is greater than 1. for example in below picture: I want just Female groups because distinct of their ids is greater that 1 (first field is id. Others are fname,lname,email and gender).
Update4: I did What I want with "RDD", but I want to do exactly this part of code with "Dataset":
List<Tuple2<String, Iterable<Person>>> f = PersonRDD
.mapToPair(s -> new Tuple2<>(s.getGender(), s)).groupByKey()
.filter(t -> ((Collection<Person>) t._2()).stream().mapToInt(e -> e.getId).distinct().count() > 1)
.collect();
Why don't you filter on id before grouping ? GroupByKey is an expensive action, it should be faster to filter first.
If you really want to group first, you may have to then use .flatMapGroups with identity function.
Not sure about java code but scala version would be something as follow:
peopleDS
.groupByKey(_.gender)
.mapGroups { case (gender, persons) => persons.filter(your condition) }
But again, you should filter first :). Specially since your ID field is already available before grouping.
Grouping is used for aggregation functions, you can find functions like "agg" in "KeyValueGroupedDataset" class. If you apply aggregation function for ex. "count", you will get "Dataset", and "filter" function will be available.
"groupBy" without aggregation function looks strange, other function, for ex. "distinct" can be used.
Filtering example with "FlatMapGroupsFunction":
.flatMapGroups(
(FlatMapGroupsFunction<String, Person, Person>) (f, k) -> {
List<Person> result = new ArrayList<>();
while (k.hasNext()) {
Person value = k.next();
// filter condition here
if (value != null) {
result.add(value);
}
}
return result.iterator();
},
Encoders.bean(Person.class))

Getting NoFastSuchElementException, when I use valueMap() in gremlin query with java

GraphTraversal<Vertex, Map<String, Object>> tsList = traversalSource.V().has("request","id_key",114).valueMap();
while(tsList.hasNext())
{
System.out.println(tsList.next().get("status")); //prints result
System.out.println(tsList.next().get("tree_id_key")); //prints result
System.out.println(tsList.next().get("username")); //here throws Exception for any vertex.
System.out.println(tsList.next().get("tree_status"));
}
when I run the above query, I am getting values for any first and second propertyKey. but after getting answers for two Keys It throws NoFastSuchElementException for any propertyKey...Kindly help me..how can I resolve this?
but using below query I am able to get the answer:
GraphTraversal<Vertex, Map<String, Object>> tsList = traversalSource.V().has("request","id_key",114).values("status","tree_id_key","username","tree_status");
while(tsList.hasNext())
{
System.out.println(tsList.next());//prints "status" value
System.out.println(tsList.next());//prints "tree_id_key" value
System.out.println(tsList.next());//prints "username" value
System.out.println(tsList.next());//prints "tree_status" value
}
updated:
GraphTraversal<Vertex, Map<String, Object>> ts = traversalSource.V().has("request","id_key",113).valueMap();
while(ts.hasNext())
{
Map<String, Object> tsList=ts.next();
SuiteIdKey=(long)((ArrayList)tsList.get("suiteKey")).get(0);
seqe=(int)((ArrayList)tsList.get("sequence")).get(0);
}
In the above query, ts will return many rows and for each row I will get suiteIdkey,seqe..I am able to get the answer. But tsList is Map<>, it is not directly returning answer when I use like this =(long)tsList.get("suiteKey"), without ArrayList. Is it the correct way? Kindly help me.
You are asking for two different things in those two traversals. The first one with valueMap() asks that the vertex be converted to a Map, so you don't want to keep calling next() in that loop. Just do:
Map<String, List<Object>> m = traversalSource.V().has("request", "id_key", 114).
valueMap().next();
System.out.println(m.get("status").get(0));
System.out.println(m.get("tree_id_key").get(0));
System.out.println(m.get("username").get(0));
System.out.println(m.get("tree_status").get(0));
Keep in mind above that i'm assuming your traversal always returns one vertex...account for that otherwise if that is not true.
In your second traversal where you do values() you are asking Gremlin to convert grab the values of those specific properties on that vertex and stream them out and thus you need to iterate with multiple calls to next.
Note that typically returning the entire vertex object isn't a recommended practice. It is somewhat akin to SELECT * FROM table in SQL. It is better to get only the fields that you want to get which then gets rid of the ugly multi-property issue of Map<String, List<Object>>:
Map<String, Object> m = traversalSource.V().has("request", "id_key", 114).
project('s`,'tik', 'u', 'ts').
by('status').
by('tree_id_key').
by('username')
by('tree_status').
next();
System.out.println(m.get("s"));
System.out.println(m.get("tik"));
System.out.println(m.get("u"));
System.out.println(m.get("ts"));

Compare data of two or more maps

I am stuck with the below requirement and not sure how can I proceed with it:
I have a function like:
public void compareExcel(Map<Object,List<HashMap>>) compareMaps){}
This function will take a map as an input parameter. This map will contain the sheet name vs Sheet values(Column name - column values) mapping.
Basically the function input parameters will be like:
<Excel1,(scenario:10)
(timing: 20)
Excel2,(scenario:30)
(timing: 40)
Excel3,(scenario:50)
(timing: 60)
>
Here my excel1 having two columns(scenario and timings) and having values as 10 and 20 respectively.
In the result, I will be needing the comparison like:
Map>
<scenario, <excel1,10>
<excel2,30>
<excel3,50>
timing, <excel1,20>
<excel2,40>
<excel3,60>
>
Any help will be appreciated.
Create/initialize the details of you output data-structure
LOOP (over the excelName:List pairs in you input)
LOOP (over the List that is the value in the pair)
//Each entry in the list is a map
Get the key-name (e.g. "scenario")
Get the value (e.g. "10")
//You already know the out key (i.e the excelName)
With the three known values, build/add to your output data-structure
On mobile so I can't even check syntax, but...
Map recopilation = new HashMap();
for(Object sheetName : compareMaps.keySet()) {
Map sheet = compareMaps.get(sheetName);
for (Object columnName : sheet.keySet()) {
if (recopilation.get(columnName) == null) {
recopilation.put(columnName, new HashMap());
}
((Map) recopilation.get(columnName)).put(sheetName, sheet.get(columnName));
}
}
Something like that. If it works, you should really throw some generics in there, I didn't mostly to save some typing.

Query in DynamoDB using Java API

I need to implement a Query operation on DynamoDB. Right now I'm doing it by giving the HashKey and then filtering out the results according to my conditions on non-key attributes.
This is what I'm doing :
MusicData hashKey = new MusicData();
hashKey.setID(singer);
DynamoDBQueryExpression<MusicData> queryExpression = new DynamoDBQueryExpression<MusicData>().withHashKeyValues(hashKey);
List<MusicData> queryResult = mapper.query(MusicData.class, queryExpression);
for (MusicData musicData : queryResult) {
if( my condtions ) {
do something;
}
}
What I'm trying to do is to be able to do something like this :
MusicData hashKey = new MusicData();
hashKey.setID(singer);
hashKey.setAlbum(sampleAlbum);
hashKey.setSinger(duration);
DynamoDBQueryExpression<MusicData> queryExpression = new DynamoDBQueryExpression<MusicData>().withHashKeyValues(hashKey);
List<MusicData> queryResult = mapper.query(MusicData.class, queryExpression);
for (MusicData musicData : queryResult) {
if( my condtions ) {
do something;
}
}
And get results already filtered out. Is there a way to do this in DynamoDB?
Yes, you can ask DynamoDB to perform filtering of queries before it returns results. However, you will still incur the 'cost' of reading these items even though they are not returned to your client. This is still a good practice as it will eliminate unnecessary transfer of items over the network.
To do this you will call additional methods on your DynamoDBQueryExpression object, specifically withFilterExpression and addExpressionAttributeNamesEntry / addExpressionAttributeValuesEntry to complete the expression.
Without the specific example of what type of conditions you want to apply it is hard to give an example, but depending on how simple your condition you want to apply is you could contain it in just the withFilterExpression method.
DynamoDBQueryExpression<MusicData> queryExpression = new DynamoDBQueryExpression<MusicData>().withHashKeyValues(hashKey).withFilterExpression("foo > 10");

Categories

Resources