I have a map in redis cache with the following structure :
Cache entry : Map<String, Map<String, String>>
The inner map could have as many as 25000 elements/buckets.
Is there a way in redis to retrieve a specific value from the inner map based on the keys(redis key, outer map's key and inner map's key) without having to fetch the entire redis entry in my Java method that accesses this redis cache?
Currently, if I have to delete a particular key from the inner map, am doing it the following way :
Map<String, Map<String, String>> mapFromRedis = redis.get("myRediscacheKey");
Map<String, String> innerMap = new HashMap<>();
if (!mapFromRedis.isEmpty()) {
innerMap = mapFromRedis.get("key");
}
if (innerMap.containsKey("keyToBeDeleted")) {
innerMap.remove("keyToBeDeleted");
mapFromRedis.put("key", innerMap);
}
redis.set("myRediscacheKey", mapFromRedis);
Your data structure has 3-level mapping: Redis key, outer map key, and inner map key. From your example, we can get the following mapping: myRediscacheKey -> key -> keyToBeDeleted -> value
However, Redis can only has at most 2-level mapping, i.e. HASH. In order to achieve your goal, you have to do compress the 3-level mapping to a 2-level mapping, or even 1-level mapping.
Compress to 2-level mapping
You can combine the Redis key with your outer map key as the key of a HASH, and take the inner map key as the field of the HASH. In that way, we can get the 2-level mapping as follows: myRediscacheKey:key -> keyToBeDeleted -> value.
// set inner map key
hset myRediscacheKey:key keyToBeDeleted value
// remove inner map key
hdel myRediscacheKey:key keyToBeDeleted
Compress to 1-level mapping
You can also combine the 3 keys into one key: myRediscacheKey:key:keyToBeDeleted -> value
// set inner map key
set myRediscacheKey:key:keyToBeDeleted value
// remove inner map key
del myRediscacheKey:key:keyToBeDeleted
You can easily do it with Redisson. It allows to reference Redis object to another Redis object though special reference objects which handled by Redisson. Here is the code sample:
RMap<String, RMap<String, String>> mapFromRedis = redisson.getMap("myRediscacheKey");
// Redisson loads only small reference object
// to another Redis map and NOT entire map
Map<String, String> innerMap = mapFromRedis.get("key");
if (innerMap.remove("keyToBeDeleted") != null) {
// key has been deleted
}
// or with faster approach
if (innerMap.fastRemove("keyToBeDeleted") == 1) {
// key has been deleted
}
That's it! You don't need to execute huge update on each inner map change. Under the hood it works using only 2 operations over Redis: get and hdel
RMap extends standard Java Map and ConcurrentMap interfaces. Thus you don't need to handle with keys/connections/serialization. This all done by Redisson.
Related
Using this code
Map<String,Object> payloadMap = new HashMap<String,Object>();
payloadMap = (Map<String,Object>) new Gson().fromJson(result, payloadMap.getClass());
, I convert this json:
{
"name":"name1",
"job":"prosecutor",
"department": {
"department_name":"prosecutor's office"
}
}
to the map (map with unlimited number of child maps):
This done well, but now I want to get an access to values of child (nested) maps.
In parent map child maps "wrapped" to Object.
So, I tried to get "wrapped" child maps from the Object-values of parent map.
public void mapRequestNode (Map<String,Object> payloadMap) {
payloadMap.entrySet().forEach(node->this.getDataFromNode(node));
}
As you can see from the above picture, there are no way to use child map "department", which had been "wrapped" to Object. I have an access to Object-methods, but not to the Map-methods (for example, I cant use "value.get("department_name")". I tried cast "(Map<String, Object>)value", but without success...
The "department" name in case above is only for example! I dont know concrete name of json child-objects. There may be unlimited number of names! So I cant use something like this "payloadMap.get("department")"
Following
((Map<String, Object>)payloadMap.get("department")).get("department_name")
should work, dont?
Your variable value is of type Object, which means that the compiler will not know anything else about the variable. Even if the object you retrieve from your json file is a map, as you store it in a Object variable, the compiler will handle it as an Object and not as a Map. That is why you cannot do value.get("department"); : the method get does not exist for the type Object.
You need to cast whatever is stored in value.get("department") as a Map<String, Object> to be able to handle it as a Map.
I have found a special solution.
I convert json to Map<String,Map<String,Object>>.
Not in Map<String,Object>. In this case I can successfully use child-maps of parent dto-map.
But this solution is special (not general) in the meaning that, I can handle in this way json, which consist only of objects.
For example, if I try to get the value of "job" in following example:
{
"job": "prosecutor",
"department": {
"department_name":"prosecutor's office"
}
}
by using Map.Entry<String, Map<String, Object>> payloadNodeEntry.getValue,
I will receive ClassCastException (cant cast String to Map<String, Object> ).
My requirement is collect both the keys and values of keys from json and print it in list view as key value,so there will be no pre defined keys.
I have a nested object called as ListDetailModel, in that there are several other objects of other Pojo classes.
Right now I am able to derive the keys of parent object that is ListDetailModel.
My question is how to derive the keys of nested Pojos from the same entry set object. What I know is I can make separate objects for the required Pojo classes and derive keys.But can I do it from same class?
Map<String,ListDetailModel> result = gson.fromJson(response , Map.class); //result.get("").getExcavatorInformation().getClass()
//noinspection HardCodedStringLiteral
ListDetailModel model = new ListDetailModel();
for (Map.Entry<String,ListDetailModel> entry : result.entrySet()) {
String key = entry.getKey();
Log.d("++++++++++++++"," " +key);
// do stuff
}
So lets say there is another CarDetailModel inside ListDetailModel, cannot I do something like entry.get("CarDetailModel").getKey() and retrieve those keys ?
Map.Entry<String,ListDetailModel> is a node of the Map<String,ListDetailModel>.
You can only call getKey() or getValue() on it, not get("CarDetailModel").
If you want to query a value in a Map by Key, then you have to call the function get(Key k) on the map itself, not on one of its Entries
Use GSON by Google rather than parsing yourself
For Demos: https://github.com/google/gson
I am new to solr, and I am facing a problem when I try to serialize/deserialize a Map in Solr.
I use Spring Data Solr in my Java application as follow:
#Field("mapped_*")
private Map<String, String> values;
It flatten and serializes my map in Solr as follow:
"key1" : "value1"
"key2" : "value2"
...
However, when I run a search, the returned objects have this field always set as NULL. Deserialization does not work on this particular field, it looks like it does not recognize the key1, key2... as part of the Map.
Does anyone know how to make the derialization work? Do I have to implement a custom converter?
At this time Spring Data Solr does not automatically prefix values contained in the map with the given #Field#value, but will just use the Map#key as fieldname. There's an improvement (DATASOLR-202) open.
At this time having key1, key2,.. in values requires the fieldname to be key* in order to read back values correctly.
#Field("key*")
private Map<String, String> values;
I've been using Jackson for a while to parse json files and load the attribute and value into a Map. This is essentially what my code looks like:
Map<String, String> map = new HashMap<String, String>();
ObjectMapper mapper = new ObjectMapper();
File file = new File(pathToSource);
map = mapper.readValue(file, new TypeReference<HashMap<String, String>>() {});
This has worked well for flat json files where the keys are flat, just containing attribute/value pairs.
{
"attr":"value"
"attr":"value"
...
}
Now one of my sources has begun putting a key inside another key and th readValue method pukes when it hits the inner key.
{ "key1":{
"attr":"value"
"attr":"value"
"key2":{
"attr":"value"
"attr":"value"
}
}
}
One caveat of my need is I want to capture the attribute name and the value both. If I parse the json more granularly, taking attributes one by one, I can't access the attribute name.
I've been looking at this for a bit now and can't find the right combination to parse the keys, while capturing the attribute name and value.
Any suggestions welcome.
One thing to note is that if you just used simpler version:
Map<String, Object> map = mapper.readValue(file, Map.class);
you would get a Map that contains Strings, Lists and Maps as values, corresponding to matching JSON Structure (String, Array, Object).
You can generally use type java.lang.Object to mean "use the matching basic Java type", so signature you are asking for is Map<String,Object>, unless you want to enforce specific value, or use a POJO type.
How can I copy content of one HashMap<String,AddressDTO> to another HashMap<String,AddressBO> of a different type. There is no
inheritance between AdressDTO and AddressBO ,both are POJOs with the same set of attributes:
AddressDTO addDTO = new AddressDTO();
addDTO.setAdd1("add1");
addDTO.setAdd2("add2");
addDTO.setAddtype("pri");
addDTO.setCity("city");
Map<String,Object> map1 = new HashMap<String,Object>();
map1.put("primary", addDTO);
Map<String,Object> map2 = new HashMap<String,Object>(map1);
AddressBO addnew = (AddressBO) map2.get("primary");
//this will give me runtime error AddressDTO cannot be cast to AddressBO
System.out.println(addnew.getAdd1());
System.out.println(addnew.getAdd2());
System.out.println(addnew.getAddtype());
You can loop through the results of entry set which will give you the key value pairs and allow you to copy.
Also, HashMap takes a map. Probably other maps do too.
You cannot cast one Object to another based on field similarity only. You should use inheritance, or use some kind of transformer to create AddressBO from AddressDTO
If both Object contains exactly the same fields, there is no need for two classes.
There are various tools available that will use reflection to copy the values.
Alternatively you could just set the reference in the new map to be the same as the old map (then they will both share the same HashMap).
But it sounds like your real problem is mapping from the DTO to the BO.
For example if you create a constructor for the BO that accepts a DTO and creates a new BO from it then you could just do:
for (Entry<String, DTO> e: map1) {
map2.put(e.getKey(), new BO(e.getValue()));
}