All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.spikeify.BigMap Maven / Gradle / Ivy

There is a newer version: 0.2.35
Show newest version
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.annotations.AsJson;
import com.spikeify.commands.InfoFetcher;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * A type-safe wrapper for a native {@link LargeList}, exposed as a Map.
 *
 * @param  the type of keys in this map
 * @param  the type of values in this map
 */
public class BigMap extends BigDatatypeWrapper {

	protected Type keyType;
	protected Type valueType;

	protected IAsyncClient asynClient;
	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.asynClient = client;
		this.key = key;
		this.keyType = TypeUtils.getBigMapKeyType(field);
		if (keyType != null) {
			Class keyClass = (Class) keyType;
			if (keyClass.isAnnotationPresent(AsJson.class)) {
				throw new SpikeifyError("Error: @AsJson is not supported on BigMap key type argument.");
			}
		}

		valueType = TypeUtils.getBigMapValueType(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

		// retards from AS
		inner = new LargeList((AerospikeClient) asynClient, 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;
			}
		}
	}

	/**
	 * Returns true if this map contains a mapping for the specified key.
	 *
	 * @param key key whose presence in this map is to be tested
	 * @return if this map contains a mapping for the specified key
	 */
	public boolean containsKey(K key) {

		try {
			return inner.exists(Value.get(key));
		} catch (AerospikeException ae) {
			if (ae.getResultCode() == 1417) {
				isEmpty = true;
			}
		}
		return false;
	}

	/**
	 * Inserts a value into the map and associate it with provided key. If the map previously contained a mapping for
	 * the key, the old value is replaced by the specified value.
	 *
	 * @param key   key with which the specified value is to be associated
	 * @param value value to be associated with the specified key
	 */
	@SuppressWarnings("unchecked")
	public void put(K key, V value) {
		if (value == null) {
			throw new IllegalArgumentException("Can not add 'null' to BigList.");
		}

		Map valMap = new HashMap<>(2);
		valMap.put("key", key);
		valMap.put("value", converter == null ? value : converter.fromField(value));
		inner.update(Value.get(valMap));
		isEmpty = false;
	}

	/**
	 * Copies all of the mappings from the specified map to this map.
	 * This is equivalent as calling {@link #put(Object, Object)} for each mapping of specified map.
	 *
	 * @param map mappings to be stored in this map
	 */
	@SuppressWarnings("unchecked")
	public void putAll(Map map) {
		if (map == null) {
			throw new IllegalArgumentException("Can not add 'null' to BigMap.");
		}

		if (map.isEmpty()) {
			return;
		}

		List values = new ArrayList<>(map.size());
		int inLoop = 0;

		for (Map.Entry entry : map.entrySet()) {
			K key = entry.getKey();
			V value = entry.getValue();
			inLoop++;
			Map valMap = new HashMap<>(2);
			valMap.put("key", key);
			valMap.put("value", converter == null ? value : converter.fromField(value));
			values.add(Value.get(valMap));
			if (inLoop % step == 0) {
				addTransactionally(values); // add in chunks
				values.clear();
			}
		}
		if (!values.isEmpty()) {
			addTransactionally(values);  // add remaining chunk
		}
	}

	/**
	 * Returns the value to which the specified key is mapped,
	 * or {@code null} if this map contains no mapping for the key.
	 *
	 * @param key the key whose associated value is to be returned
	 * @return the value to which the specified key is mapped, or
	 * {@code null} if this map contains no mapping for the key
	 */
	@SuppressWarnings("unchecked")
	public V get(K key) {
		List found = inner.find(Value.get(key));

		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 (V) converter.fromProperty(((Map) found.get(0)).get("value"));
		} else {
			return (V) ((Map) found.get(0)).get("value");
		}
	}

	/**
	 * Returns a map of all keys, values.
	 *
	 * @return A map of all keys, values.
	 */
	@SuppressWarnings("unchecked")
	public Map getAll() {

		List found = null;

		try {
			found = inner.scan();
		} catch (AerospikeException ae) {
			if (ae.getResultCode() == 1417) {
				return new HashMap<>(0);
			}
			throw ae;
		}

		return toTypedResults(found);
	}

	/**
	 * Returns a range of values between from an to positions.
	 *
	 * @param from Starting position
	 * @param to   Ending position
	 * @return A map of key, values between requested key positions.
	 */
	@SuppressWarnings("unchecked")
	public Map range(K from, K to) {

		Value fromValue = Value.get(from);
		Value toValue = Value.get(to);

		List found = null;
		try {
			found = inner.range(fromValue, toValue);
		} catch (AerospikeException ae) {
			if (ae.getResultCode() == 1417) {
				return new HashMap<>(0);
			}
			throw ae;
		}

		return toTypedResults(found);
	}

	/**
	 * Returns a count of key, values from the last position.
	 *
	 * @param count maximum number of values to return
	 * @return A map of key, values from the last key value
	 */
	@SuppressWarnings("unchecked")
	public Map findLast(int count) {


		List found = null;
		try {
			found = inner.findLast(count);
		} catch (AerospikeException ae) {
			if (ae.getResultCode() == 1417) {
				return new HashMap<>(0);
			}
			throw ae;
		}

		return toTypedResults(found);
	}

	/**
	 * Returns a count of key, values from the last position.
	 *
	 * @param count maximum number of values to return
	 * @return A map of key, values from the last key value
	 */
	@SuppressWarnings("unchecked")
	public Map findFirst(int count) {

		List found = null;
		try {
			found = inner.findFirst(count);
		} catch (AerospikeException ae) {
			if (ae.getResultCode() == 1417) {
				return new HashMap<>(0);
			}
			throw ae;
		}

		return toTypedResults(found);
	}

	private Map toTypedResults(List untyped) {
		if (untyped == null || untyped.isEmpty()) {
			return new HashMap<>(0);  // return empty list if no results
		}

		Map results = new HashMap<>(untyped.size());
		for (Object obj : untyped) {
			K key = (K) ((Map) obj).get("key");
			V val = (V) ((Map) obj).get("value");
			if (converter != null) {
				results.put(key, (V) converter.fromProperty(val));
			} else {
				results.put(key, val);
			}
		}
		return results;
	}

	/**
	 * Removes the mapping for a key from this map if it is present.
	 *
	 * @param key key whose mapping is to be removed from the map
	 */
	public void remove(K key) {
		inner.remove(Value.get(key));
	}

	/**
	 * Removes the mappings for a list keys from this map for each mapping that is present.
	 *
	 * @param keys a list of keys whose mapping is to be removed from the map
	 */
	public void remove(List keys) {
		// no need to use Value.get(keys) as this is already done by the underlying client
		inner.remove(keys);
	}

	/**
	 * Removes all values.
	 */
	public void removeAll() {
		// destroy LDT field...
		inner.destroy();
		// re-initialize
		inner = new LargeList((AerospikeClient) asynClient, wp, key, binName);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy