Aerospike всегда хранит целочисленные типы (short, int, long и др. c) в виде длинных 64-бина в базе данных. Если вы вставите более короткий тип цифры c, он автоматически преобразует его в длинный. Это необходимо для поддержки языков, которые не имеют более коротких чисел c типов.
Поэтому при извлечении карты ключи вашей карты будут возвращены как длинные. Следовательно, этот код должен заменить ваш поисковый код:
Record record = client.get(null, key); // using same key for testing
Map<Long, Widgets> map2 = (Map<Long, Widgets>) record.getMap("widgets"); // here, I do get a map back... but its serialized
map2.forEach( (k,v) -> System.out.println(k));
Примечание. Вы правильно упоминаете, что классы Widgets () будут храниться в Aerospike как сериализованный объект Java. Это, вероятно, не желательно. Если в какой-то момент в будущем вы смените технологию на что-то другое, ваши данные будут недоступны для чтения. Aerospike является языковой независимостью c, поэтому вы можете записать свои данные в Java и прочитать их, например, в C. Храня Java сериализованных объектов, вы предотвращаете эту способность.
Вы можете видеть это в вашем AQL (обратите внимание на 0xaced Java magi c number):
aql> select * from test.users
*************************** 1. row ***************************
widgets: MAP('{1:AC ED 00 05 73 72 00 34 63 6F 6D 2E 74 69 6D 2E...
Обычно лучше использовать Spring Data или сериализовать их самостоятельно. Например, вы можете изменить свой код так:
package com.aerospike.play;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Bin;
import com.aerospike.client.Key;
import com.aerospike.client.Record;
public class StackOverflowQuestion {
public static class Widgets implements Serializable{
private static final String VIEW_FIELD = "view";
private static final String CLICK_FIELD = "click";
private int viewCount;
private int clickCount;
public Widgets(int viewCount, int clickCount) {
this.viewCount = viewCount;
this.clickCount = clickCount;
}
public int getViewCount() {
return viewCount;
}
public int getClickCount() {
return clickCount;
}
@Override
public String toString() {
return String.format("{view: %d, count: %s}", viewCount, clickCount);
}
public Map<String, Object> asMap() {
Map<String, Object> values = new HashMap<>();
values.put(VIEW_FIELD, this.viewCount);
values.put(CLICK_FIELD, this.clickCount);
return values;
}
public static Widgets fromMap(Map<String, Object> map) {
return new Widgets((int)(long)map.get(VIEW_FIELD), (int)(long)map.get(CLICK_FIELD));
}
}
public static void main(String[] args) {
AerospikeClient client = new AerospikeClient("172.28.128.4", 3000);
Key key = new Key( "test", "users", 2 );
Map<Integer, Map<String, Object>> map = new HashMap<>();
map.put(1, new Widgets(2, 2).asMap());
map.put(2, new Widgets(3, 0).asMap());
Bin bin = new Bin("widgets", map);
client.put( null, key, bin );
Record record = client.get(null, key); // using same key for testing
Map<Long, Map<String, Object>> map2 = (Map<Long, Map<String, Object>>) record.getMap("widgets");
map2.forEach( (k,v) -> {
Widgets w = Widgets.fromMap(v);
System.out.printf("%d -> %s\n", k, w);
});
client.close();
}
}
В этом случае вы получите ожидаемый результат:
1 -> {view: 2, count: 2}
2 -> {view: 3, count: 0}
Но данные, хранящиеся в базе данных, используют собственные Типы:
aql> select * from test.users
*************************** 1. row ***************************
widgets: MAP('{1:{"view":2, "click":2}, 2:{"view":3, "click":0}}')
В качестве примечания, существуют более эффективные способы хранения этих данных, таких как список, но это представление более наглядно.