Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to save a HashMap (as object property) to the Datastore

I'm using the GAE (Google App Engine) datastore to store persons.

Each person has a name, an unique token, a list of messages they received (Strings) and a list of tests they completed (Strings). These are all supported types.

However, I also want to store a HashMap containing String keys and String values with information on the tests they have completed.

An example Map i'm trying to save (note that this is example data, not actual data)

[
    <'test_easy_1', 'completed in 3 minutes'>
    <'test_easy_2', 'completed in 5 minutes'>
    <'test_hard_1', 'completed in 15 minutes'>
    <'test_hard_2', 'completed in 3 minutes.. Did you cheat?'>
]

I'm trying to save the person like this

@Override
public boolean savePerson(Person p) {
    if (p.getEntity() == null) {
        return false;
    }

    p.getEntity().setProperty("name", p.getName());
    p.getEntity().setProperty("token", p.getToken());
    p.getEntity().setProperty("messages", p.getMessages());
    p.getEntity().setProperty("completedTests", p.getCompletedTests());
    p.getEntity().setProperty("testInformation", p.getTestInformation()); // does not work
    DatastoreServiceFactory.getDatastoreService().put(p.getEntity());

    return true;
}

Now for my question:

How do I save HashMaps as object properties to the datastore?

I hope to perform this in a way where I don't have to create an entire class and new Datastore index just for the HashMap. However, if this is the only way to go, I'd like to be informed how to do so. Googling this has not resulted in a clear and obvious way to deal with these kind of situations.

like image 824
Bassdrop Cumberwubwubwub Avatar asked Jan 15 '15 15:01

Bassdrop Cumberwubwubwub


People also ask

Can HashMap store objects?

HashMap in Java works on hashing principles. It is a data structure that allows us to store object and retrieve it in constant time O(1) provided we know the key. In hashing, hash functions are used to link keys and values in HashMap.

How do you store a HashMap?

In the ArrayList chapter, you learned that Arrays store items as an ordered collection, and you have to access them with an index number ( int type). A HashMap however, store items in "key/value" pairs, and you can access them by an index of another type (e.g. a String ).

Are Hashmaps memory efficient?

The HashMap will most likely need more memory, even if you only store a few elements. By the way, the memory footprint should not be a concern, as you will only need the data structure as long as you need it for counting. Then it will be garbage collected, anyway.


2 Answers

Albeit other answers are probably correct, I was mainly looking for a way to achieve this without using external libraries.

I have done some research on beforementioned EmbeddedEntity to find that this is indeed what I was looking for.

I'm writing and accepting this answer as it gives an exact solution to my problem, and not just guidelines towards solving it. I hope to guide people in the future who stumble upon similar problems by doing it this way.

Saving the person now looks like this:

@Override
public boolean savePerson(Person p) {
    if (p.getEntity() == null) {
        return false;
    }

    p.getEntity().setProperty("name", p.getName());
    p.getEntity().setProperty("token", p.getToken());
    p.getEntity().setProperty("messages", p.getMessages());
    p.getEntity().setProperty("completedTests", p.getCompletedTests());

    EmbeddedEntity ee = new EmbeddedEntity();
    Map<String, String> testInformation = p.getTestInformation();

    for (String key : testInformation.keySet()) { // TODO: maybe there is a more efficient way of solving this
        ee.setProperty(key, testInformation.get(key));
    }

    p.getEntity().setProperty("testInformation", ee);
    DatastoreServiceFactory.getDatastoreService().put(p.getEntity());

    return true;
}

Loading the person now looks like this:

Person p = new Person(id);
p.setName((String) e.getProperty("name"));
p.setToken((String) e.getProperty("token"));
p.setMessages((List<String>) e.getProperty("messages"));
p.setCompletedTests((List<String>) e.getProperty("completedTests"));

Map<String, String> ti = new HashMap<>();
EmbeddedEntity ee = (EmbeddedEntity) e.getProperty("testInformation");
if (ee != null) {
    for (String key : ee.getProperties().keySet()) {
        ti.put(key, (String) ee.getProperty(key));
    }
    p.setTestInformation(ti);
}
p.setEntity(e);
like image 181
Bassdrop Cumberwubwubwub Avatar answered Nov 15 '22 04:11

Bassdrop Cumberwubwubwub


You can save it as an embedded entity.

I'd recommend using Objectify which simplifies storage and reduces boilerplate: https://code.google.com/p/objectify-appengine/wiki/Entities#Maps

You can create indexes by just annotating @Index on the Map field.

like image 25
tj-recess Avatar answered Nov 15 '22 05:11

tj-recess