-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* MongoDB & MariaDB Stateless Field Filtering Storage impl
- Loading branch information
Showing
13 changed files
with
1,540 additions
and
270 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package wtf.casper.storageapi; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public record Filter(String key, Object value, FilterType filterType, SortingType sortingType, Type type) { | ||
public static Filter of(String key, Object value, FilterType filterType, SortingType sortingType, Type type) { | ||
return new Filter(key, value, filterType, sortingType, type); | ||
} | ||
|
||
public static Filter of(String key, Object value, FilterType filterType, SortingType sortingType) { | ||
return new Filter(key, value, filterType, sortingType, Type.AND); | ||
} | ||
|
||
public static Filter of(String key, Object value, FilterType filterType) { | ||
return new Filter(key, value, filterType, SortingType.NONE, Type.AND); | ||
} | ||
|
||
public enum Type { | ||
AND, | ||
OR | ||
} | ||
|
||
/** | ||
* Groups filters into a list of lists, where each list is a group of filters that are connected by OR | ||
* [AND, OR, AND, AND, OR, AND] -> [[AND] OR [AND, AND] OR [AND]] | ||
* @param filters the filters to group | ||
* @return the grouped filters | ||
*/ | ||
public static List<List<Filter>> group(Filter... filters) { | ||
List<List<Filter>> groups = new ArrayList<>(); | ||
groups.add(new ArrayList<>()); | ||
for (Filter filter : filters) { | ||
List<Filter> lastGroup = groups.get(groups.size() - 1); | ||
if (filter.type() == Type.AND) { | ||
lastGroup.add(filter); | ||
continue; | ||
} | ||
|
||
if (!lastGroup.isEmpty()) { | ||
groups.add(new ArrayList<>()); | ||
lastGroup = groups.get(groups.size() - 1); | ||
} | ||
lastGroup.add(filter); | ||
} | ||
|
||
return groups; | ||
} | ||
} |
252 changes: 252 additions & 0 deletions
252
src/main/java/wtf/casper/storageapi/StatelessFieldStorage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
package wtf.casper.storageapi; | ||
|
||
import dev.dejvokep.boostedyaml.YamlDocument; | ||
import dev.dejvokep.boostedyaml.block.implementation.Section; | ||
import wtf.casper.storageapi.misc.ConstructableValue; | ||
import wtf.casper.storageapi.misc.KeyValue; | ||
import wtf.casper.storageapi.utils.ReflectionUtil; | ||
import wtf.casper.storageapi.utils.StorageAPIConstants; | ||
|
||
import java.io.IOException; | ||
import java.lang.reflect.ParameterizedType; | ||
import java.lang.reflect.Type; | ||
import java.util.*; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.function.Supplier; | ||
|
||
public interface StatelessFieldStorage<K, V> { | ||
|
||
/** | ||
* @param field the field to search for. | ||
* @param value the value to search for. | ||
* @return a future that will complete with a collection of all values that match the given field and value. | ||
*/ | ||
default CompletableFuture<Collection<V>> get(final String field, final Object value) { | ||
return get(field, value, FilterType.EQUALS, SortingType.NONE); | ||
} | ||
|
||
/** | ||
* @param field the field to search for. | ||
* @param value the value to search for. | ||
* @param filterType the filter type to use. | ||
* @param sortingType the sorting type to use. | ||
* @return a future that will complete with a collection of all values that match the given field and value. | ||
*/ | ||
default CompletableFuture<Collection<V>> get(final String field, final Object value, final FilterType filterType, final SortingType sortingType) { | ||
return get(Filter.of(field, value, filterType, sortingType)); | ||
}; | ||
|
||
/** | ||
* @param filters the filters to use. | ||
* @return a future that will complete with a collection of all value that match the given filters. | ||
*/ | ||
default CompletableFuture<Collection<V>> get(Filter... filters) { | ||
return get(-1, filters); | ||
}; | ||
|
||
/** | ||
* @param limit the limit of values to return. | ||
* @param filters the filters to use. | ||
* @return a future that will complete with a collection of all value that match the given filters. | ||
*/ | ||
CompletableFuture<Collection<V>> get(int limit, Filter... filters); | ||
|
||
/** | ||
* @param key the key to search for. | ||
* @return a future that will complete with the value that matches the given key. | ||
* The value may be null if the key is not found. | ||
*/ | ||
CompletableFuture<V> get(final K key); | ||
|
||
/** | ||
* @param key the key to search for. | ||
* @return a future that will complete with the value that matches the given key or a generated value if not found. | ||
*/ | ||
default CompletableFuture<V> getOrDefault(final K key) { | ||
return get(key).thenApply((v) -> { | ||
|
||
if (v != null) { | ||
return v; | ||
} | ||
|
||
if (this instanceof ConstructableValue<?, ?>) { | ||
v = ((ConstructableValue<K, V>) this).constructValue(key); | ||
if (v == null) { | ||
throw new RuntimeException("Failed to create default value for V with key " + key + ". " + | ||
"Please create a constructor in V for only the key"); | ||
} | ||
return v; | ||
} | ||
|
||
if (this instanceof KeyValue<?, ?>) { | ||
KeyValue<K, V> keyValueGetter = (KeyValue<K, V>) this; | ||
try { | ||
return ReflectionUtil.createInstance(keyValueGetter.value(), key); | ||
} catch (final Exception e) { | ||
e.printStackTrace(); | ||
throw new RuntimeException("Failed to create default value for V with key " + key + ". " + | ||
"Please create a constructor in V for only the key.", e); | ||
} | ||
} | ||
|
||
try { | ||
if (getClass().getGenericSuperclass() instanceof ParameterizedType parameterizedType) { | ||
Type type = parameterizedType.getActualTypeArguments()[1]; | ||
Class<V> aClass = (Class<V>) Class.forName(type.getTypeName()); | ||
return ReflectionUtil.createInstance(aClass, key); | ||
} | ||
|
||
throw new RuntimeException("Failed to create default value for V with key " + key + ". " + | ||
"Please create a constructor in V for only the key."); | ||
|
||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
throw new RuntimeException("Failed to create default value for V with key " + key + ". " + | ||
"Please create a constructor in V for only the key."); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* @param field the field to search for. | ||
* @param value the value to search for. | ||
* @return a future that will complete with the first value that matches the given field and value. | ||
*/ | ||
default CompletableFuture<V> getFirst(final String field, final Object value) { | ||
return getFirst(field, value, FilterType.EQUALS); | ||
} | ||
|
||
/** | ||
* @param field the field to search for. | ||
* @param value the value to search for. | ||
* @param filterType the filter type to use. | ||
* @return a future that will complete with the first value that matches the given field and value. | ||
*/ | ||
default CompletableFuture<V> getFirst(final String field, final Object value, FilterType filterType) { | ||
return get(1, Filter.of(field, value, filterType, SortingType.NONE)).thenApply((values) -> { | ||
if (values.isEmpty()) { | ||
return null; | ||
} | ||
|
||
return values.iterator().next(); | ||
}); | ||
}; | ||
|
||
|
||
/** | ||
* @param value the value to save. | ||
*/ | ||
CompletableFuture<Void> save(final V value); | ||
|
||
/** | ||
* @param values the values to save. | ||
*/ | ||
default CompletableFuture<Void> saveAll(final Collection<V> values) { | ||
return CompletableFuture.runAsync(() -> values.forEach(v -> save(v).join()), StorageAPIConstants.DB_THREAD_POOL); | ||
} | ||
|
||
/** | ||
* @param key the key to remove. | ||
*/ | ||
CompletableFuture<Void> remove(final V key); | ||
|
||
/** | ||
* Writes the storage to disk. | ||
*/ | ||
CompletableFuture<Void> write(); | ||
|
||
/** | ||
* Deletes the storage from disk. | ||
*/ | ||
CompletableFuture<Void> deleteAll(); | ||
|
||
/** | ||
* Closes the storage/storage connection. | ||
*/ | ||
default CompletableFuture<Void> close() { | ||
return CompletableFuture.completedFuture(null); | ||
} | ||
|
||
/** | ||
* @param field the field to search for. | ||
* @param value the value to search for. | ||
* @return a future that will complete with a boolean that represents whether the storage contains a value that matches the given field and value. | ||
*/ | ||
CompletableFuture<Boolean> contains(final String field, final Object value); | ||
|
||
/** | ||
* @param storage the storage to migrate from. The data will be copied from the given storage to this storage. | ||
* @return a future that will complete with a boolean that represents whether the migration was successful. | ||
*/ | ||
default CompletableFuture<Boolean> migrate(final StatelessFieldStorage<K, V> storage) { | ||
return CompletableFuture.supplyAsync(() -> { | ||
storage.allValues().thenAccept((values) -> values.forEach(v -> save(v).join())).join(); | ||
return true; | ||
}, StorageAPIConstants.DB_THREAD_POOL); | ||
} | ||
|
||
/** | ||
* @param oldStorageSupplier supplier to provide the old storage | ||
* @param config the config | ||
* @param path the path to the storage | ||
* @return a future that will complete with a boolean that represents whether the migration was successful. | ||
*/ | ||
default CompletableFuture<Boolean> migrateFrom(Supplier<StatelessFieldStorage<K, V>> oldStorageSupplier, YamlDocument config, String path) { | ||
return CompletableFuture.supplyAsync(() -> { | ||
if (config == null) return false; | ||
Section section = config.getSection(path); | ||
if (section == null) return false; | ||
if (!section.getBoolean("migrate", false)) return false; | ||
section.set("migrate", false); | ||
try { | ||
config.save(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
// storage that we are migrating to the new storage | ||
StatelessFieldStorage<K, V> oldStorage = oldStorageSupplier.get(); | ||
try { | ||
this.migrate(oldStorage).join(); | ||
return true; | ||
} catch (Exception e) { | ||
return false; | ||
} | ||
}, StorageAPIConstants.DB_THREAD_POOL); | ||
} | ||
|
||
/** | ||
* @return a future that will complete with a collection of all values in the storage. | ||
*/ | ||
CompletableFuture<Collection<V>> allValues(); | ||
|
||
/** | ||
* @param field the field to search for. | ||
* @param sortingType the sorting type to use. | ||
* @return a future that will complete with a collection of all values in the storage that match the given field and value. | ||
*/ | ||
default CompletableFuture<Collection<V>> allValues(String field, SortingType sortingType) { | ||
return CompletableFuture.supplyAsync(() -> { | ||
Collection<V> values = allValues().join(); | ||
if (values.isEmpty()) { | ||
return values; | ||
} | ||
|
||
// Sort the values. | ||
return sortingType.sort(values, field); | ||
}, StorageAPIConstants.DB_THREAD_POOL); | ||
} | ||
|
||
/** | ||
* Adds an index to the storage. | ||
* @param field the field to add an index for. | ||
* @return a future that will complete when the index has been added. | ||
*/ | ||
CompletableFuture<Void> addIndex(String field); | ||
|
||
/** | ||
* Removes an index from the storage. | ||
* @param field the field to remove the index for. | ||
* @return a future that will complete when the index has been removed. | ||
*/ | ||
CompletableFuture<Void> removeIndex(String field); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
...a/wtf/casper/storageapi/impl/direct/statelessfstorage/DirectStatelessMariaDBFStorage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package wtf.casper.storageapi.impl.direct.statelessfstorage; | ||
|
||
import wtf.casper.storageapi.Credentials; | ||
import wtf.casper.storageapi.impl.statelessfstorage.StatelessMariaDBFStorage; | ||
import wtf.casper.storageapi.impl.statelessfstorage.StatelessMongoFStorage; | ||
import wtf.casper.storageapi.misc.ConstructableValue; | ||
|
||
import java.util.function.Function; | ||
|
||
public class DirectStatelessMariaDBFStorage<K, V> extends StatelessMariaDBFStorage<K, V> implements ConstructableValue<K, V> { | ||
|
||
private final Function<K, V> function; | ||
|
||
public DirectStatelessMariaDBFStorage(Class<K> keyClass, Class<V> valueClass, Credentials credentials, Function<K, V> function) { | ||
super(keyClass, valueClass, credentials); | ||
this.function = function; | ||
} | ||
|
||
@Override | ||
public V constructValue(K key) { | ||
return function.apply(key); | ||
} | ||
} |
Oops, something went wrong.