
com.spikeify.BigIndexedList Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core Show documentation
Show all versions of core Show documentation
Simple ORM for Aerospike
package com.spikeify;
import com.aerospike.client.AerospikeClient;
import com.aerospike.client.AerospikeException;
import com.aerospike.client.Key;
import com.aerospike.client.Value;
import com.aerospike.client.async.IAsyncClient;
import com.aerospike.client.large.LargeList;
import com.aerospike.client.policy.RecordExistsAction;
import com.aerospike.client.policy.WritePolicy;
import com.spikeify.commands.InfoFetcher;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.*;
/**
* A type-safe wrapper for a native {@link LargeList}, exposing its interface as a List.
*
* @param the type of elements in this list
*/
public class BigIndexedList extends BigDatatypeWrapper {
protected Type valueType;
protected IAsyncClient client;
protected WritePolicy wp;
protected Key key;
protected String binName;
/**
* Internal function - must be called before this LDT can be used.
* This function is called during a setup of mapping relation between this class and mapped field.
*
* @param client The underlying Aerospike client
* @param key The record key under which this list is saved in DB
* @param binName The bin name under which this list is saved in DB
* @param field The field in the object to which this list is assigned
*/
public void init(IAsyncClient client, Key key, String binName, Field field) {
this.binName = binName;
this.client = client;
this.key = key;
valueType = TypeUtils.getBigListValueType(field);
setConverterForValueType(field, valueType);
this.wp = new WritePolicy();
wp.recordExistsAction = RecordExistsAction.UPDATE;
// need to set timeout for operations with a lot of records, e.g. getAll();
wp.timeout = 10_000; // 10s
inner = new LargeList((AerospikeClient) client, wp, key, binName);
if (!(new InfoFetcher(client).isUDFEnabled(key.namespace))) {
throw new SpikeifyError("Error: LDT support not enabled on namespace '" + key.namespace + "'. Please add 'ldt-enabled true' to namespace section in your aerospike.conf file.");
}
try {
inner.size();
} catch (AerospikeException ae) {
if (ae.getResultCode() == 1417) {
isEmpty = true;
}
}
}
/**
* Add object to the end of list
*
* @param value Value
* @return last index
*/
@SuppressWarnings("unchecked")
public int add(T value) {
if (value == null) {
throw new IllegalArgumentException("Can not add 'null' to BigList.");
}
int lastIndex = isEmpty ? 0 : inner.size();
int retries = 10;
Map valMap = new HashMap<>(2);
valMap.put("key", lastIndex);
valMap.put("value", converter == null ? value : converter.fromField(value));
while (retries > 0) {
try {
inner.add(Value.get(valMap));
isEmpty = false;
return lastIndex;
} catch (AerospikeException ae) {
if (ae.getResultCode() == 1402) {
retries--;
} else {
throw ae; // re-throw the exception
}
}
}
throw new SpikeifyError("Concurrency error: could not add to LargeList due to too-high conncurent updates.");
}
/**
* Add a List of objects to the end of list
*
* @param collection A collection of object to be added to the end of list.
* @return last index
*/
@SuppressWarnings("unchecked")
public int addAll(Collection collection) {
if (collection == null) {
throw new IllegalArgumentException("Can not add 'null' to BigList.");
}
if (collection.isEmpty()) {
return 0;
}
List values = new ArrayList<>(collection.size());
int inLoop = 0;
int lastIndex = isEmpty ? 0 : inner.size();
for (T value : collection) {
inLoop++;
Map valMap = new HashMap<>(2);
valMap.put("key", lastIndex);
valMap.put("value", converter == null ? value : converter.fromField(value));
values.add(Value.get(valMap));
lastIndex++;
if (inLoop % step == 0) {
addTransactionally(values); // add in chunks
values.clear();
}
}
if (!values.isEmpty()) {
addTransactionally(values); // add remaining chunk
return lastIndex;
} else {
return lastIndex;
}
}
/**
* Update value at given position in the list.
*
* @param index Index
* @param value to be updated
*/
@SuppressWarnings("unchecked")
public void update(int index, T value) {
if (value == null) {
throw new IllegalArgumentException("Can not add 'null' to BigList.");
}
int lastIndex = isEmpty ? 0 : inner.size();
if (index > lastIndex) {
throw new IllegalArgumentException("Error: index out of bounds. Can not add value past the end of list.");
}
boolean success = false;
int retries = 10;
while (retries > 0) {
Map valMap = new HashMap<>(2);
valMap.put("key", index);
valMap.put("value", converter == null ? value : converter.fromField(value));
try {
inner.update(Value.get(valMap));
isEmpty = false;
return;
} catch (AerospikeException ae) {
if (ae.getResultCode() == 1402) {
retries--;
} else {
throw ae; // re-throw the exception
}
}
}
throw new SpikeifyError("Concurrency error: could not add to LargeList due to too-high concurrent updates.");
}
/**
* Returns a value at given position in the list.
*
* @param index Index
* @return A value at requested indexes.
*/
@SuppressWarnings("unchecked")
public T get(int index) {
try {
List found = inner.find(Value.get(index));
if (found == null || found.isEmpty()) {
return null;
}
if (found.size() > 1) {
throw new IllegalStateException("List consistency error: list should only contain one value for each index.");
}
if (converter != null) {
return (T) converter.fromProperty(((Map) found.get(0)).get("value"));
} else {
return (T) ((Map) found.get(0)).get("value");
}
} catch (AerospikeException ae) {
if (ae.getResultCode() == 1417) {
return null;
}
throw ae;
}
}
/**
* Returns a list of all values.
*
* @return A list of all values.
*/
@SuppressWarnings("unchecked")
public List getAll() {
List found = null;
try {
found = inner.scan();
} catch (AerospikeException ae) {
if (ae.getResultCode() == 1417) {
return new ArrayList<>(0);
}
throw ae;
}
if (found == null || found.isEmpty()) {
return new ArrayList<>(0); // return empty list if no results
}
List results = new ArrayList<>(found.size());
for (Object obj : found) {
Object val = ((Map) obj).get("value");
if (converter != null) {
results.add((T) converter.fromProperty(val));
} else {
results.add((T) val);
}
}
return results;
}
/**
* Returns a range of values between from an to positions.
*
* @param from Starting position
* @param to Ending position
* @return A list of values between requested positions.
*/
@SuppressWarnings("unchecked")
public List range(int from, int to) {
if (to < from) {
throw new IllegalArgumentException("Inverted range: 'to' is smaller then 'from'");
}
List found = null;
try {
found = inner.range(Value.get(from), Value.get(to));
} catch (AerospikeException ae) {
if (ae.getResultCode() == 1417) {
return new ArrayList<>();
}
throw ae;
}
if (found == null || found.isEmpty()) {
return new ArrayList<>(0); // return empty list if no results
}
List results = new ArrayList<>(found.size());
for (Object obj : found) {
Object val = ((Map) obj).get("value");
if (converter != null) {
results.add((T) converter.fromProperty(val));
} else {
results.add((T) val);
}
}
return results;
}
public List range(int from, int to, int count) {
if (to < from) {
throw new IllegalArgumentException("Inverted range: 'to' is smaller then 'from'");
}
List found = null;
try {
found = inner.range(Value.get(from), Value.get(to), count);
} catch (AerospikeException ae) {
if (ae.getResultCode() == 1417) {
return new ArrayList<>();
}
throw ae;
}
if (found == null || found.isEmpty()) {
return new ArrayList<>(0); // return empty list if no results
}
List results = new ArrayList<>(found.size());
for (Object obj : found) {
Object val = ((Map) obj).get("value");
if (converter != null) {
results.add((T) converter.fromProperty(val));
} else {
results.add((T) val);
}
}
return results;
}
/**
* Removes values from start position to the end of list.
*
* @param from Starting index of trim (inclusive)
* @return Number of items removed
*/
public int trim(int from) {
try {
int to = inner.size() - 1;
if (to < from) {
throw new IndexOutOfBoundsException("Parameter 'from' is out of range.");
}
return inner.remove(Value.get(from), Value.get(to));
} catch (AerospikeException ae) {
if (ae.getResultCode() == 1417) {
return 0;
}
throw ae;
}
}
/**
* Does value exist?
*
* @param index index of the value to check existance for
* @return True if value at given index exists
*/
public boolean exists(int index) throws AerospikeException {
try {
return index >= 0 && inner.exists(Value.get(index));
} catch (AerospikeException ae) {
if (ae.getResultCode() == 1417) {
return false;
}
throw ae;
}
}
/**
* Removes all values.
*/
public void removeAll() {
// destroy LDT field...
inner.destroy();
// re-initialize
inner = new LargeList((AerospikeClient) client, wp, key, binName);
isEmpty = true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy