I'm using DynamoDBMapper from the AWS Java SDK and working with a fairly simple item: it has a string attribute (used as the hash key) and a map attribute.
#DynamoDBTable(tableName = "MyTable")
public class MyItem {
private String myStringAttr;
private Map<String, String> myMapAttr;
#DynamoDBHashKey(attributeName = "MyStringAttribute")
public String getMyKeyAttr() { return myKeyAttr; }
public void setMyKeyAttr(String myKeyAttr) { this.myKeyAttr = myKeyAttr; }
#DynamoDBAttribute(attributeName = "MyMapAttribute")
public Map<String, String> getMyMapAttr() { return myMapAttr; }
public void setMyMapAttr(Map<string, string> myMapAttr) { this.myMapAttr = myMapAttr; }
}
I can read and write my object using the load() and save() methods just fine. The issue I'm running into is when I need to update or add a single entry in the map for an existing item in the table. Without knowing the existing entries of the map for said item (and I don't want to perform a load() each time before I attempt to update or add), the best I seem to be able to do is wipe out the entire map and replace it with the single entry I'm trying to update or add. Is it possible to add/update a single entry to an existing item's map attribute using the DynamoDBMapper?
I've looked at the various options provided by DynamoDBSaveExpression and DynamoDBMapperConfig. The closest option I can find is DynamoDBMapperConfig.SaveBehavior.APPEND_SET, but this is for set type attributes rather than the map type I'm using.
I'm able to achieve this exact use case using the Document API's updateItem() method along with an UpdateItemSpec containing an UpdateExpression as shown below. This will add a new entry to the map if the given key doesn't yet exist, or update the value to the one specific if an existing entry does exist, all without touching the other entries in the map. However, I have found the Document API rather cumbersome to work with and would prefer to stick with the DynamoDBMapper if at all possible.
Table table = dynamoDB.getTable("MyTable");
UpdateItemSpec updateItemSpec = new UpdateItemSpec()
.withPrimaryKey("MyStringAttribute", 1)
.withUpdateExpression("set #mma.#mek = :mev")
.withNameMap(new NameMap()
.with("#mma", "MyMapAttribute")
.with("#mek", "SomeMapEntryKey")
.withValueMap(new ValueMap()
.withString(":mev", "Some map entry value"));
UpdateItemOutcome outcome = table.updateItem(updateItemSpec);
One possible solution would be:-
1) Get the data from the table "MyTable" using DynamoDBMapper load
2) Add new entries to the map in the object retrieved in step 1 and save that object using DynamoDBMapper with config as mentioned below
DynamoDBMapperConfig dynamoDBMapperConfig = new DynamoDBMapperConfig(SaveBehavior.UPDATE);
I know that this is a two step process. However, when I was in the same situation, I have tried a lot and couldn't find any solution with single direct update for "Map" data type using DynamoDBMapper.
Hope this helps!
Related
I am trying to serialize Observable Maps with an Serialiazation Proxy.
These two properties need to be saved.
private final MapProperty<Integer, Cargo> allCargoStoredMap;
private final MapProperty<Cargo, CargoLogisticData> cargoLogisticDataMap;
I thought i can save the values in an array like this:
this.cargoLogisticDataAsArray = warehouseManagement.getCargoCargoLogisticDataMap().values().toArray(new CargoLogisticData[0]);
this.cargoAsArray = warehouseManagement.getAllCargoStoredMapProperty().values().toArray(new Cargo[0]);
Then i want to pass i to an Constructor, and rebuild the properties.
public Constructor (List<Cargor> cargos, List<CargoLogisticData>) {
ObservableMap<Integer, Cargo> cargos = FXCollections.observableHashMap();
//How do i add my cargos to a map?
this.allCargoStoredMap = new SimpleMapProperty<>(cargos);
}
Any Idea how i can add the the List Entrys to my map?
I thought i maybe have to save 4 Arrays. Two with key, two with values. And then add those again.
Any Ideas ?
This can be a simple solution if I don't get it wrong
allCargoStoredMap.values().stream().forEach(cargo -> {
CargoLogisticData logisticsData=cargoLogisticDataMap.get(cargo);
/*
You have cargo and related logistics data here.
You can create which data model you want to store and use it
*/
})
I am working on an XML file. In my XML file, there are somes nodes which have childs. This XML file has multiple tags.
<Cat categorie="CAT 1" guid="e9fdsd8ff">
<!--Electric energie management-->
**<item Var="VAR1" guid="2795ddsd410d">
<desc> Energie Smart Management
</desc>
<App guid="240adsqsd" />
<App guid="90dqddqsq" />**
</item>
</Cat>
Like you can see, my node "item " has the argument VAR=var1 and has 2 childs.
So I created a hashMap to put, for 1 node his childs like below
private Map<String, Collection <String >> multiMap = new HashMap <> ();
So I Have something like that actually : [Key=Var1, Value = [gui0,guid1,...]]
Now, I would like to know if you knew how to verify if a guid is contained in a collection associated with a key in order to retrieve this key.
For example, if I have this String : 240adsqsd. I want to recover Var1.
Thanks for your help
It is possible.
Say you have the key myKey and you want to know if the string mySearchString is contained in the collection behind that key.
multiMap.get("myKey").contains("mySearchString");
It will return true if mySearchString equals (case sensitive) any object in the colelction.
You have to be careful though, the method contains on a collection uses the case sensitive equals method and will only work when they are 100% equal. So when your collection contains something like "MYSEARCHstring", it won't work, as well as "The sentence that contains mySearchString".
EDIT:
(Thanks Nikolas and Dici)
Here a more complete example how to achieve that.
String mySearchString = "mySearchString";
Map<String, Collection<String>> multiMap = new HashMap<>();
for (String key : multiMap.keySet()) {
if (multiMap.get(key) != null && multiMap.get(key).contains(mySearchString)) {
return key;
}
}
If you don't know the key, you have to iterate over your map, check if one of the collections contains the searched string and then, when you found the collection (and its key) return the key.
A test without map modification would be:
boolean contained = multiMap.getOrDefault(key, Collections.emptyList()).contains(key);
Then there are Map.computeIfAbsent, computeIfPresent, merge if you want to update the map.
If I understand your question, you actually want to reverse your map because a map is good at accessing a value given a key not at finding a key given a value. Here's some pseudo-code to build the map:
map = new Map()
for item in items
for app in item.apps
map.put(app.guid, item.guid) // assuming guids are always unique
That would give you a Map<String, String> rather than Map<String, Collection<String>>. The former is good at telling you which item contains an application, the later is good at telling you which apps a given item contains. Given your reverse mapping map, you will be able to do the following:
// could just have Map<App, Item> appToItem if you build your map differently
// and App and Item define hashCode and equals
public boolean findVar(String appId, Map<String, String> appToItem, Map<String, Item> itemsById) {
Item item = itemsById.get(appToItem.get(appId));
if (item == null) return null;
return item.getVar();
}
Thank you to everyone for your answers.
If I understand correctly, it is preferable that I look for a value not his key.
So let's admit that I choose this option.
Can I recure each value for a key.
If my key is Var1 for example, would it be better for me to recover all its values?
I am trying to do a partial indexing of a Map, with a condition based on the key name of the map.
My goal is to index only the A key, because I know that I will query only on that.
Here is a simple demo code:
Map<String, String> map = new HashMap<>();
map.put("A", "1");
map.put("B", "2");
MainClass mainClass = new MainClass();
mainClass.mapIndexed = map;
mainClass.mapUnindexed = map;
ObjectifyService.register(MainClass.class);
ObjectifyService.ofy().save().entities(mainClass);
And the test class
#Entity
public class MainClass {
#Id
Long id;
#Unindex
Map<String, String> mapUnindexed;
#Index
Map<String, String> mapIndexed;
}
As you can see from the result, both A and B keys are indexed, but I want to index only the A key.
I already know that Objectify provided the concept of partial-indexing, but it seems that I can use this only on the entire map (index the entire map or don't index the map at all).
Is there a way to index only some keys of the map based on custom conditions? (like the name of the key)
Interesting. Unfortunately, there is not currently an easy way to do this; please file an enhancement request in Github.
If this is absolutely critical to address immediately (ie, you're burning big $ in the datastore), you can copy the EmbeddedMapTranslatorFactory, change the save() code, and enable this new version on the field using the #Translate annotation.
Or, if you're feeling adventurous and want to submit a PR, the important change is to pass a Path into the If<?,?> methods.
I have a project where I save some data coming from different channels of a Soap Service, for example:
String_Value Long_timestamp Double_value String_value String_value Int_value
I can have many lines (i.e. 200), with different values, like the one above.
I thought that I could use an ArrayList, however data can have a different structure than the one above, so an ArrayList maybe isn't a good solution in order to retrieve data from it.
For example above I have, after the first two values that are always fixed, 4 values, but in another channel I may have 3, or 5, values. What I want retrieve data, I must know how many values have a particular line, and I think that Arraylist doesn't help me.
What solution could I use?
When you have a need to uniquely identify varying length input, a HashMap usually works quite well. For example, you can have a class:
public class Record
{
private HashMap<String, String> values;
public Record()
{
// create your hashmap.
values = new HashMap<String, String>();
}
public String getData(String key)
{
return values.get(key);
}
public void addData(String key, String value)
{
values.put(key, value);
}
}
With this type of structure, you can save as many different values as you want. What I would do is loop through each value passed from Soap and simply add to the Record, then keep a list of Record objects.
Record rec = new Record();
rec.addData("timestamp", timestamp);
rec.addData("Value", value);
rec.addData("Plans for world domination", dominationPlans);
You could build your classes representing the entities and then build a parser ... If it isn't in a standard format (eg JSON, YAML, ecc...) you have no choice to develop your own parser .
Create a class with fields.
class ClassName{
int numberOfValues;
String dataString;
...
}
Now create an ArrayList of that class like ArrayList<ClassName> and for each record fill that class object with numberOfValues and dataString and add in Arraylist.
I am using a class where I am taking input as the file name and the file location. I have a pre defined file names, so I will match the predefined file names with the file name that I received and then store the values accordingly. Please look at the code below
//Set of storage maps and tables
public class storage
{
//Storage set
public static Set<Integer> tiger = new HashSet<Integer>();
//Storage set
public static Set<Integer> lion = new HashSet<Integer>();
//This is the table used for storing the browser customer count
public static Table<String,String,Integer> elephant = HashBasedTable.create();
//Storage map
public static Map<String, String> monkey = new HashMap<String, String>();
public static void storeDataDirector(String fileLocation,String fileName) throws Exception
{
if (fileName = monkey)
**update the "monkey map"**
}
This is my problem, also I have lot of maps and tables to be used so I wouldn't be able to use multiple if conditions and then check and update the same.
What I would like to know is the below
As I have said earlier, The file name that I am sending to the program which is "String filename" has the same name of the "Map monkey" but the former is a String and the latter is the map. I would like to know if I will be able to use the string variable as a reference to the map instance as both of them have the same name . This will highly avoid the if conditions that I am using in the program and thus I would like to possible solution for this ... Anything related to type caseting ort
You need to have another Map - whose key is a String and value is a Map. Something like Map<String,Map> allMaps = new HashMap<String,Map>()
Once you have this map , populate it with all your filenames and the corresponding maps monkey.
allMaps .put("monkey", monkey)
If a string filename corresponds to not a map but to a set , then you need to declare something more general Map<String,Object> allMaps = new HashMap<String,Object>(). Ofcourse this means you need to cast the value to its particular type before you can do any meaningful thing with it.
Then , to use this map , use your filename argument
Map monkeyAgain = allMaps.get(filename)
You can use reflection:
Storage.class.getField(fileName).get(null)
You will still have to cast the returned object. I do not think this the right approach.
The idea is to relate them in a Map, and use the file name as a key for example
Map<String, Map<String, String>>
// file store structure
If you need a generic solution, you could solve this by implementing an abstraction of your store structure, by implementing an interface similar to this one:
// T is the store type and U is the original type (String from file for instance...)
public interface StoreUnit<T, U> {
void update(U record);
List<T> list();
}
so you will have an implementation for each case (Set, Map, Table ...) and will relate it in a map using the file name as key.
monkeyFileName => MapStoreUnit<Entry<String,String>,String>
tigerFileName => SetStoreUnit<Integer, String>
elephantFileName => TableStoreUnit<Entry<Entry<String,String>,String>,String> // not sure if for Table there is something better than Entry ;)
When you wanna update some store you perform a get over the map using the file name as key, and invoking update method implemented with the record (that could be an String, complex Object) and so on. When you need to read something from there you could use the list method.