Objectify - Partial indexing of a Map<> object based on key name - java

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.

Related

MultiMap with Collection<String>

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?

JAVA : Best performance-wise method to find an object stored in hashMap

I have a bunch of objects stored in hashMap<Long,Person> i need to find the person object with a specific attribute without knowing its ID.
for example the person class:
public person{
long id;
String firstName;
String lastName;
String userName;
String password;
String address;
..
(around 7-10 attributes in total)
}
lets say i want to find the object with username = "mike". Is there any method to find it without actually iterating on the whole hash map like this :
for (Map.Entry<Long,Person> entry : map.entrySet()) {
if(entry.getValue().getUserName().equalsIgnoreCase("mike"));
the answers i found here was pretty old.
If you want speed and are always looking for one specific attribute, your best bet is to create another 'cache' hash-map keyed with that attribute.
The memory taken up will be insignificant for less than a million entries and the hash-map lookup will be much much faster than any other solution.
Alternatively you could put all search attributes into a single map (ie. names, and ids). Prefix the keys with something unique if you're concerned with collisions. Something like:
String ID_PREFIX = "^!^ID^!^";
String USERNAME_PREFIX = "^!^USERNAME^!^";
String FIRSTNAME_PREFIX = "^!^FIRSTNAME^!^";
Map<String,Person> personMap = new HashMap<String,Person>();
//add a person
void addPersonToMap(Person person)
{
personMap.put(ID_PREFIX+person.id, person);
personMap.put(USERNAME_PREFIX+person.username, person);
personMap.put(FIRSTNAME_PREFIX+person.firstname, person);
}
//search person
Person findPersonByID(long id)
{
return personMap.get(ID_PREFIX+id);
}
Person findPersonByUsername(String username)
{
return personMap.get(USERNAME_PREFIX+username);
}
//or a more generic version:
//Person foundPerson = findPersonByAttribute(FIRSTNAME_PREFIX, "mike");
Person findPersonByAttribute(String attr, String attr_value)
{
return personMap.get(attr+attr_value);
}
The above assumes that each attribute is unique amongst all the Persons. This might be true for ID and username, but the question specifies firstname=mike which is unlikely to be unique.
In that case you want to abstract with a list, so it would be more like this:
Map<String,List<Person>> personMap = new HashMap<String,List<Person>>();
//add a person
void addPersonToMap(Person person)
{
insertPersonIntoMap(ID_PREFIX+person.id, person);
insertPersonIntoMap(USERNAME_PREFIX+person.username, person);
insertPersonIntoMap(FIRSTNAME_PREFIX+person.firstname, person);
}
//note that List contains no duplicates, so can be called multiple times for the same person.
void insertPersonIntoMap(String key, Person person)
{
List<Person> personsList = personMap.get(key);
if(personsList==null)
personsList = new ArrayList<Person>();
personsList.add(person);
personMap.put(key,personsList);
}
//we know id is unique, so we can just get the only person in the list
Person findPersonByID(long id)
{
List<Person> personList = personMap.get(ID_PREFIX+id);
if(personList!=null)
return personList.get(0);
return null;
}
//get list of persons with firstname
List<Person> findPersonsByFirstName(String firstname)
{
return personMap.get(FIRSTNAME_PREFIX+firstname);
}
At that point you're really getting into a grab-bag design but still very efficient if you're not expecting millions of entries.
The best performance-wise method I can think of is to have another HashMap, with the key being the attribute you want to search for, and the value being a list of objects.
For your example this would be HashMap<String, List<Person>>, with the key being the username. The downside is that you have to maintain two maps.
Note: I've used a List<Person> as the value because we cannot guarantee that username is unique among all users. The same applies for any other field.
For example, to add a Person to this new map you could do:
Map<String, List<Person>> peopleByUsername = new HashMap<>();
// ...
Person p = ...;
peopleByUsername.computeIfAbsent(
p.getUsername(),
k -> new ArrayList<>())
.add(p);
Then, to return all people whose username is i.e. joesmith:
List<Person> matching = peopleByUsername.get("joesmith");
Getting one or a few entries from a volatile map
If the map you're operating on can change often and you only want to get a few entries then iterating over the map's entries is ok since you'd need space and time to build other structures or sort the data as well.
Getting many entries from a volatile map
If you need to get many entries from that map you might get better performance by either sorting the entries first (e.g. build a list and sort that) and then using binary search. Alternatively you could build an intermediate map that uses the attribute(s) you need to search for as its key.
Note, however, that both approaches at least need time so this only yields better performance when you're looking for many entries.
Getting entries multiple times from a "persistent" map
If your map and its valuies doesn't change (or not that often) you could maintain a map attribute -> person. This would mean some effort for the initial setup and updating the additional map (unless your data doesn't change) as well as some memory overhead but speeds up lookups tremendously later on. This is a worthwhile approach when you'd do very little "writes" compared to how often you do lookups and if you can spare the memory overhead (depends on how big those maps would be and how much memory you have to spare).
Consider one hashmap per alternate key.
This will have "high" setup cost,
but will result in quick retrieval by alternate key.
Setup the hashmap using the Long key value.
Run through the hashmap Person objects and create a second hashmap (HashMap<String, Person>) for which username is the key.
Perhaps, fill both hashmaps at the same time.
In your case,
you will end up with something like HashMap<Long, Person> idKeyedMap and HashMap<String, Person> usernameKeyedMap.
You can also put all the key values in the same map,
if you define the map as Map<Object, Person>.
Then,
when you add the
(id, person) pair,
you need to also add the (username, person) pair.
Caveat, this is not a great technique.
What is the best way to solve the problem?
There are many ways to tackle this as you can see in the answers and comments.
How is the Map is being used (and perhaps how it is created). If the Map is built from a select statement with the long id value from a column from a table we might think we should use HashMap<Long, Person>.
Another way to look at the problem is to consider usernames should also be unique (i.e. no two persons should ever share the same username). So instead create the map as a HashMap<String, Person>. With username as the key and the Person object as the value.
Using the latter:
Map<String, Person> users = new HashMap<>();
users = retrieveUsersFromDatabase(); // perform db select and build map
String username = "mike";
users.get(username).
This will be the fastest way to retrieve the object you want to find in a Map containing Person objects as its values.
You can simply convert Hashmap to List using:
List list = new ArrayList(map.values());
Now, you can iterate through the list object easily. This way you can search Hashmap values on any property of Person class not just limiting to firstname.
Only downside is you will end up creating a list object. But using stream api you can further improve code to convert Hashmap to list and iterate in single operation saving space and improved performance with parallel streams.
Sorting and finding of value object can be done by designing and using an appropriate Comparator class.
Comparator Class : Designing a Comparator with respect to a specific attribute can be done as follows:
class UserComparator implements Comparator<Person>{
#Override
public int compare(Person p1, Person p2) {
return p1.userName.compareTo(p2.userName);
}
}
Usage : Comparator designed above can be used as follows:
HashMap<Long, Person> personMap = new HashMap<Long, Person>();
.
.
.
ArrayList<Person> pAL = new ArrayList<Person>(personMap.values()); //create list of values
Collections.sort(pAL,new UserComparator()); // sort the list using comparator
Person p = new Person(); // create a dummy object
p.userName="mike"; // Only set the username
int i= Collections.binarySearch(pAL,p,new UserComparator()); // search the list using comparator
if(i>=0){
Person p1 = pAL.get(Collections.binarySearch(pAL,p,new UserComparator())); //Obtain object if username is present
}else{
System.out.println("Insertion point: "+ i); // Returns a negative value if username is not present
}

Update existing entry within a map attribute using DynamoDBMapper

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!

How do I retrieve a key object from a Map?

Take two instances of the same class A, called foo and bar, where:
foo != bar
foo.equals(bar) == true
In other words, foo and bar are different instances but with the same hash code. Now take an instance of Map<A,B> called "map", where foo is a key in map. Is it possible to retrieve foo from Map, using bar? Currently I iterate through the key set and compare every key but is there a faster way? There don't seem to be any methods in Map for retrieving keys.
I am willing to try any data structure that implements Map or can work like a map.
Why do I want to do this? I'm trying to avoid retaining any more instances than necessary. Once I find foo I can release bar.
Thanks in advance...
You can use Apache Commons Collections ™. It has Bidirectional Maps BidiMap.
These represent maps where the key can lookup the value and the value can lookup the key with equal ease.
BidiMap bidi = new TreeBidiMap();
bidi.put("SIX", "6");
bidi.get("SIX"); // returns "6"
bidi.getKey("6"); // returns "SIX"
bidi.removeValue("6"); // removes the mapping
BidiMap inverse = bidi.inverseBidiMap(); // returns a map with keys and values swapped
See also
Commons Collections user guide
HashMap actually does have a method to retrieve the entry but it is package-private. I am not really sure why it isn't public to be honest. I don't think it exposes anything. You can, of course, call it with reflection.
Map<String, String> map = new HashMap<String, String>();
map.put(new String("hello"), "world!");
Method method = (
HashMap.class.getDeclaredMethod("getEntry", Object.class)
);
method.setAccessible(true);
#SuppressWarnings("unchecked")
Map.Entry<String, String> entry = (Map.Entry<String, String>)(
method.invoke(map, new String("hello"))
);
System.out.println(entry.toString().replace("=", " ")); // hello world
The reflection probably makes it not useful in the scenario you've described but I guess it could be useful to others. I wouldn't recommend using it.
Just using the existing map to find duplicztes doesn't seem to be enough. Use a second map, where you put(key,key) pairs. Then:
map.get(key) == null if the key is not already there
map.get(key) == firstObjectAllocated otherwise
This way may faster.
Iterator<A> mapKeyIterator=map.keySet().iterator();
while (mapKeyIterator.hasNext()){
A key;
if((key=mapKeyIterator.next()).equals(bar)) {
return key;
}
}
You can refer to the source code of HashMap
If based on your implementation:
foo.hashCode() == bar.hashCode() and foo.equals(bar)
Then, yes you could directly get the value for key foo by
map.get(bar)
updated: sorry misunderstanding your question before
if you want to keep the key, why you don't just keep the key cached. Then to retrieve the key is hash tree mapping, should be fast.
HashMap<K,V> map = new HashMap<K,V>();
HashMap<K,K> cacheKeys = new HashMap<K,K>();
cacheKeys.put(foo,foo);
map.put(foo,value);
//now you have var bar; you could retrieve the cached key
bar = cacheKeys.get(bar);//this will make bar = foo; the real bar will be gc
//then get the value
val = map.get(bar);

Using one variable as an another variable of different data type but the same name

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.

Categories