|
3 | 3 | import java.util.*; |
4 | 4 |
|
5 | 5 | public class InMemoryDB { |
6 | | - Map<String, Record> records = new HashMap<>(); |
7 | | - TreeMap<Long, Map<String, Record>> backups = new TreeMap<>(); |
8 | 6 |
|
9 | | - void set(long timestamp, String key, String field, int value) { |
10 | | - records.computeIfAbsent(key, k -> new Record()) |
11 | | - .data.put(field, new Value(value, timestamp, null)); |
12 | | - } |
| 7 | + private Map<String,Map<String,Value>> store; |
13 | 8 |
|
14 | | - void setAt(String key, String field, int value, long timestampToSet) { |
15 | | - records.computeIfAbsent(key, k -> new Record()) |
16 | | - .data.put(field, new Value(value, timestampToSet, null)); |
| 9 | + public InMemoryDB() { |
| 10 | + this.store = new HashMap<>(); |
17 | 11 | } |
18 | 12 |
|
19 | | - void setWithTTL(long timestamp, String key, String field, int value, int ttl) { |
20 | | - records.computeIfAbsent(key, k -> new Record()) |
21 | | - .data.put(field, new Value(value, timestamp, ttl)); |
| 13 | + static class Value { |
| 14 | + String value; |
| 15 | + int expiry; |
| 16 | + |
| 17 | + Value(String value, int expiry) { |
| 18 | + this.value = value; |
| 19 | + this.expiry = expiry; |
| 20 | + } |
22 | 21 | } |
23 | 22 |
|
24 | | - void setAtWithTTL(String key, String field, int value, long timestampToSet, int ttl) { |
25 | | - records.computeIfAbsent(key, k -> new Record()) |
26 | | - .data.put(field, new Value(value, timestampToSet, ttl)); |
| 23 | + private boolean isKeyPresent(String key) { |
| 24 | + return store.containsKey(key); |
27 | 25 | } |
28 | 26 |
|
29 | | - boolean compareAndSet(long timestamp, String key, String field, int expected, int newValue) { |
30 | | - Record record = records.get(key); |
31 | | - if (record != null) { |
32 | | - Value val = record.data.get(field); |
33 | | - if (val != null && val.value == expected) { |
34 | | - record.data.put(field, new Value(newValue, timestamp, null)); |
35 | | - return true; |
36 | | - } |
37 | | - } |
38 | | - return false; |
| 27 | + private boolean isFieldPresent(String key,String field){ |
| 28 | + return isKeyPresent(key) && store.get(key).containsKey(field); |
39 | 29 | } |
40 | 30 |
|
41 | | - boolean compareAndSetWithTTL(long timestamp, String key, String field, int expected, int newValue, int ttl) { |
42 | | - Record record = records.get(key); |
43 | | - if (record != null) { |
44 | | - Value val = record.data.get(field); |
45 | | - if (val != null && val.value == expected) { |
46 | | - record.data.put(field, new Value(newValue, timestamp, ttl)); |
47 | | - return true; |
48 | | - } |
49 | | - } |
50 | | - return false; |
| 31 | +// Level 1 |
| 32 | +// Set(key, field, value string) - Should insert a field-value pair to the record associated with key. If the field in the record already exists, replace the existing value with the specified value. If record doesn't exist, create a new one. |
| 33 | +// |
| 34 | +// Get(key, field string) *string - Should return the value contained within field of record associated with key. If record or field doesn't exist, should return nil |
| 35 | +// |
| 36 | +// Delete(key, field string) bool - Should remove the field from the record associated with key. Returns true if the field was successfully deleted, and false if the key or the field do not exist in the database |
| 37 | + |
| 38 | + public void set(String key,String field, String value){ |
| 39 | + store.putIfAbsent(key,new HashMap<>()); |
| 40 | + store.get(key).put(field,new Value(value,Integer.MAX_VALUE)); |
51 | 41 | } |
52 | 42 |
|
53 | | - boolean compareAndDelete(long timestamp, String key, String field, int expected) { |
54 | | - Record record = records.get(key); |
55 | | - if (record != null) { |
56 | | - Value val = record.data.get(field); |
57 | | - if (val != null && val.value == expected) { |
58 | | - record.data.put(field, new Value(expected, timestamp, 0)); |
59 | | - return true; |
60 | | - } |
| 43 | + public String get(String key,String field){ |
| 44 | + if(!isFieldPresent(key,field)){ |
| 45 | + return null; |
61 | 46 | } |
62 | | - return false; |
| 47 | + return store.get(key).get(field).value; |
63 | 48 | } |
64 | 49 |
|
65 | | - boolean deleteAt(String key, String field, long timestampToDelete) { |
66 | | - Record record = records.get(key); |
67 | | - if (record != null && record.data.containsKey(field)) { |
68 | | - Value val = record.data.get(field); |
69 | | - val.lastSetAt = timestampToDelete; |
70 | | - val.ttl = 0; |
71 | | - return true; |
| 50 | + public boolean delete(String key, String field){ |
| 51 | + if(!isFieldPresent(key,field)){ |
| 52 | + return false; |
72 | 53 | } |
73 | | - return false; |
| 54 | + store.get(key).remove(field); |
| 55 | + return true; |
74 | 56 | } |
75 | 57 |
|
76 | | - Integer get(long timestamp, String key, String field) { |
77 | | - Record record = records.get(key); |
78 | | - if (record == null) return null; |
79 | | - Value val = record.data.get(field); |
80 | | - if (val == null || val.isExpired(timestamp)) return null; |
81 | | - return val.value; |
82 | | - } |
83 | 58 |
|
84 | | - List<String> scan(long timestamp, String key) { |
85 | | - Record record = records.get(key); |
86 | | - if (record == null) return new ArrayList<>(); |
| 59 | +// Level 2 |
| 60 | +// Scan(key string) []string - Should return a list of strings representing the fields of a record associated with the key. The returned list should be in the following format ["<field1>(<value1>)" , "<field2>(<value2>)", ...] where the fields are lexicographically sorted. If specified record doesn't exist, return empty list. |
| 61 | +// |
| 62 | +//ScanByPrefix(key, prefix string) []string - Should return a list of strings representing some fields of a records associated with the key. Specifically, only fields that starts with the prefix should be included. The returned list should be the same format as the Scan operation with the fields sorted in lexicographical order. |
| 63 | + |
| 64 | + public List<String> scan(String key) { |
| 65 | + if (!isKeyPresent(key)) return new ArrayList<>(); |
87 | 66 | List<String> res = new ArrayList<>(); |
88 | | - for (Map.Entry<String, Value> e : record.data.entrySet()) { |
89 | | - if (!e.getValue().isExpired(timestamp)) { |
90 | | - res.add(e.getKey() + "(" + e.getValue().value + ")"); |
91 | | - } |
| 67 | + for (Map.Entry<String, Value> entry : store.get(key).entrySet()) { |
| 68 | + res.add(entry.getKey() + "(" + entry.getValue().value + ")"); |
92 | 69 | } |
| 70 | + Collections.sort(res); |
93 | 71 | return res; |
94 | 72 | } |
95 | 73 |
|
96 | | - List<String> scanAt(String key, long timestamp) { |
97 | | - return scan(timestamp, key); |
98 | | - } |
99 | | - |
100 | | - List<String> scanByPrefix(long timestamp, String key, String prefix) { |
| 74 | + public List<String> scanByPrefix(String key, String prefix) { |
| 75 | + if (!isKeyPresent(key)) return new ArrayList<>(); |
101 | 76 | List<String> res = new ArrayList<>(); |
102 | | - Record record = records.get(key); |
103 | | - if (record == null) return res; |
104 | | - for (Map.Entry<String, Value> e : record.data.entrySet()) { |
105 | | - if (e.getKey().startsWith(prefix)) { |
106 | | - if (!e.getValue().isExpired(timestamp)) { |
107 | | - res.add(e.getKey() + "(" + e.getValue().value + ")"); |
108 | | - } |
109 | | - } else if (e.getKey().compareTo(prefix) > 0) { |
110 | | - break; |
| 77 | + for (Map.Entry<String, Value> entry : store.get(key).entrySet()) { |
| 78 | + if (entry.getKey().startsWith(prefix)) { |
| 79 | + res.add(entry.getKey() + "(" + entry.getValue().value + ")"); |
111 | 80 | } |
112 | 81 | } |
| 82 | + Collections.sort(res); |
113 | 83 | return res; |
114 | 84 | } |
115 | 85 |
|
116 | | - int backup(long timestamp) { |
117 | | - int count = 0; |
118 | | - Map<String, Record> deepCopy = new HashMap<>(); |
119 | | - for (Map.Entry<String, Record> e : records.entrySet()) { |
120 | | - Record newRecord = new Record(); |
121 | | - for (Map.Entry<String, Value> fv : e.getValue().data.entrySet()) { |
122 | | - Value v = fv.getValue(); |
123 | | - if (!v.isExpired(timestamp)) { |
124 | | - Integer newTTL = v.ttl == null ? null : v.ttl - (int)(timestamp - v.lastSetAt); |
125 | | - newRecord.data.put(fv.getKey(), new Value(v.value, timestamp, newTTL)); |
126 | | - } |
127 | | - } |
128 | | - if (!newRecord.data.isEmpty()) { |
129 | | - deepCopy.put(e.getKey(), newRecord); |
130 | | - count++; |
131 | | - } |
132 | | - } |
133 | | - backups.put(timestamp, deepCopy); |
134 | | - return count; |
| 86 | +// Level 3 |
| 87 | + /*SetAt(key, field, value string, timestamp int) []string - Should insert a field-value pair or update the value of the field in the record associated with key |
| 88 | +
|
| 89 | +SetAtWithTtl(key, field, value string, timestamp, ttl int) []string - Should insert a field-value pair or update the value of the field in the record associated with key. Also sets its Time-to-Live starting at timestamp to be ttl. The ttl is the amount of time that this field-value pair should exist in the database, meaning it will be avaialble during the interval: [timestamp, timestamp + ttl] |
| 90 | +
|
| 91 | +DeleteAt(key, field string, timestamp int) bool The same as Delete, but with timestamp of the operation specified. Should return true if the field existed and was successfully deleted and false if the key didn't exist. |
| 92 | +
|
| 93 | +GetAt(key, field string, timestamp int) *string The same as Get, but with timestamp of the operation specified |
| 94 | +
|
| 95 | +ScanAt(key string, timestamp int) []string The same Scan but with the timestamp of the operation specified |
| 96 | +
|
| 97 | +ScanPrefixAt(key, prefix string, timestamp int) []string The same as ScanPrefix but with the timestamp of the operation specified.*/ |
| 98 | + |
| 99 | + public void setAt(String key, String field, String value, int timestamp) { |
| 100 | + store.putIfAbsent(key, new HashMap<>()); |
| 101 | + store.get(key).put(field, new Value(value, Integer.MAX_VALUE)); |
| 102 | + } |
| 103 | + |
| 104 | + public void setAtWithTtl(String key, String field, String value, int timestamp, int ttl) { |
| 105 | + store.putIfAbsent(key, new HashMap<>()); |
| 106 | + store.get(key).put(field, new Value(value, timestamp + ttl)); |
135 | 107 | } |
136 | 108 |
|
137 | | - void restore(long timestamp, long timestampToRestore) { |
138 | | - Map.Entry<Long, Map<String, Record>> entry = backups.floorEntry(timestampToRestore); |
139 | | - if (entry != null) { |
140 | | - Map<String, Record> snapshot = deepCopyRecords(entry.getValue(), timestamp); |
141 | | - records = snapshot; |
| 109 | + public boolean deleteAt(String key, String field, int timestamp) { |
| 110 | + if (!isFieldPresent(key, field)) return false; |
| 111 | + Value val = store.get(key).get(field); |
| 112 | + store.get(key).remove(field); |
| 113 | + return timestamp <= val.expiry; |
| 114 | + } |
| 115 | + |
| 116 | + public String getAt(String key, String field, int timestamp) { |
| 117 | + if (!isFieldPresent(key, field)) return null; |
| 118 | + Value val = store.get(key).get(field); |
| 119 | + return (timestamp <= val.expiry) ? val.value : null; |
| 120 | + } |
| 121 | + |
| 122 | + public List<String> scanAt(String key, int timestamp) { |
| 123 | + if (!isKeyPresent(key)) return new ArrayList<>(); |
| 124 | + List<String> res = new ArrayList<>(); |
| 125 | + for (Map.Entry<String, Value> entry : store.get(key).entrySet()) { |
| 126 | + if (timestamp <= entry.getValue().expiry) { |
| 127 | + res.add(entry.getKey() + "(" + entry.getValue().value + ")"); |
| 128 | + } |
142 | 129 | } |
| 130 | + Collections.sort(res); |
| 131 | + return res; |
143 | 132 | } |
144 | 133 |
|
145 | | - private Map<String, Record> deepCopyRecords(Map<String, Record> original, long newTimestamp) { |
146 | | - Map<String, Record> copy = new HashMap<>(); |
147 | | - for (Map.Entry<String, Record> e : original.entrySet()) { |
148 | | - Record newRecord = new Record(); |
149 | | - for (Map.Entry<String, Value> fv : e.getValue().data.entrySet()) { |
150 | | - Value v = fv.getValue(); |
151 | | - newRecord.data.put(fv.getKey(), new Value(v.value, newTimestamp, v.ttl)); |
| 134 | + public List<String> scanPrefixAt(String key, String prefix, int timestamp) { |
| 135 | + if (!isKeyPresent(key)) return new ArrayList<>(); |
| 136 | + List<String> res = new ArrayList<>(); |
| 137 | + for (Map.Entry<String, Value> entry : store.get(key).entrySet()) { |
| 138 | + if (entry.getKey().startsWith(prefix) && timestamp <= entry.getValue().expiry) { |
| 139 | + res.add(entry.getKey() + "(" + entry.getValue().value + ")"); |
152 | 140 | } |
153 | | - copy.put(e.getKey(), newRecord); |
154 | 141 | } |
155 | | - return copy; |
| 142 | + Collections.sort(res); |
| 143 | + return res; |
156 | 144 | } |
157 | 145 | } |
0 commit comments