use of com.cinchapi.ccl.type.Function in project concourse by cinchapi.
the class Stores method serialSelect.
/**
* Select the values stored for {@code key} in {@code record} at
* {@code timestamp} from the {@code store}.
* <p>
* If the {@code key} is primitive, the store retrieval is usually a simple
* {@link Store#select(long) select}. However, if the key is a navigation
* key, this method will process it via
* {@link Operations#traverseKeyRecordOptionalAtomic(String, long, long, AtomicOperation)}
* if the {@code store} is an {@link AtomicOperation} or
* {@link AtomicSupport supports} starting one.
* </p>
*
* @param store
* @param key
* @param record
* @param timestamp
*
* @return the values stored for {@code key} in {@code record} at
* {@code timestamp} according to the {@code store}
*/
protected static Set<TObject> serialSelect(Store store, String key, long record, long timestamp) {
Function evalFunc;
if (Keys.isNavigationKey(key)) {
if (store instanceof AtomicOperation || timestamp != Time.NONE) {
return Operations.traverseKeyRecordOptionalAtomic(key, record, timestamp, store);
} else if (store instanceof AtomicSupport) {
AtomicReference<Set<TObject>> value = new AtomicReference<>(ImmutableSet.of());
AtomicOperations.executeWithRetry((AtomicSupport) store, (atomic) -> {
value.set(Operations.traverseKeyRecordOptionalAtomic(key, record, timestamp, atomic));
});
return value.get();
} else {
throw new UnsupportedOperationException("Cannot fetch the current values of a navigation key using a Store that does not support atomic operations");
}
} else if ((evalFunc = Keys.tryParseFunction(key)) != null) {
String method = Calculations.alias(evalFunc.operation()) + "KeyRecordAtomic";
Number value = Reflection.callStatic(Operations.class, method, evalFunc.key(), record, timestamp, store);
return value != null ? ImmutableSet.of(Convert.javaToThrift(value)) : ImmutableSet.of();
} else if (key.equals(Constants.JSON_RESERVED_IDENTIFIER_NAME)) {
return ImmutableSet.of(Convert.javaToThrift(record));
} else {
Source source;
if (Command.isSet()) {
Strategy strategy = new Strategy(Command.current(), store);
source = strategy.source(key, record);
} else {
source = Source.FIELD;
}
Set<TObject> values;
if (source == Source.RECORD) {
// @formatter:off
Map<String, Set<TObject>> data = timestamp == Time.NONE ? store.select(record) : store.select(record, timestamp);
values = data.getOrDefault(key, ImmutableSet.of());
// @formatter:on
} else if (source == Source.FIELD) {
// @formatter:off
values = timestamp == Time.NONE ? store.select(key, record) : store.select(key, record, timestamp);
// @formatter:on
} else {
// source == Source.INDEX
// @formatter:off
values = timestamp == Time.NONE ? store.gather(key, record) : store.gather(key, record, timestamp);
// @formatter:on
}
return values;
}
}
use of com.cinchapi.ccl.type.Function in project concourse by cinchapi.
the class Stores method find.
/**
* Find the records that contain values that are stored for {@code key} and
* satisfy {@code operator} in relation to the specified {@code values} at
* {@code timestamp}.
* <p>
* If the {@code key} is primitive, the store lookup is usually a simple
* {@link Store#find(String, Operator, TObject[]) find}. However, if the key
* is a navigation key, this method will process it by
* {@link #browse(Store, String) browsing} the destination values and
* checking the operator validity of each if and only if the {@code store}
* is an {@link AtomicOperation} or {@link AtomicSupport supports} starting
* one.
* </p>
*
* @param store
* @param timestamp
* @param key
* @param operator
* @param values
* @return the records that satisfy the query
*/
public static Set<Long> find(Store store, long timestamp, String key, Operator operator, TObject... values) {
for (int i = 0; i < values.length; ++i) {
TObject value = values[i];
if (value.getType() == Type.FUNCTION) {
Function function = (Function) Convert.thriftToJava(value);
TemporalFunction func = (TemporalFunction) function;
String method = Calculations.alias(function.operation());
ArrayBuilder<Object> args = ArrayBuilder.builder();
method += "Key";
args.add(function.key());
if (function instanceof KeyRecordsFunction || function instanceof KeyConditionFunction) {
method += "Records";
Collection<Long> records = function instanceof KeyRecordsFunction ? ((KeyRecordsFunction) function).source() : Finder.instance().visit(((KeyConditionFunction) function).source(), store);
args.add(records);
} else if (!(function instanceof IndexFunction)) {
throw new IllegalStateException("Invalid function value");
}
method += "Atomic";
args.add(func.timestamp());
args.add(store);
values[i] = Convert.javaToThrift(Reflection.callStatic(Operations.class, method, args.build()));
}
}
if (Keys.isNavigationKey(key)) {
Map<TObject, Set<Long>> index = timestamp == Time.NONE ? browse(store, key) : browse(store, key, timestamp);
Set<Long> records = index.entrySet().stream().filter(e -> e.getKey().is(operator, values)).map(e -> e.getValue()).flatMap(Set::stream).collect(Collectors.toCollection(LinkedHashSet::new));
return records;
} else if (Keys.isFunctionKey(key)) {
Set<Long> records = Sets.newLinkedHashSet();
for (long record : store.getAllRecords()) {
Set<TObject> aggregate = select(store, key, record, timestamp);
for (TObject tobject : aggregate) {
if (tobject.is(operator, values)) {
records.add(record);
break;
}
}
}
return records;
} else {
return timestamp == Time.NONE ? store.find(key, operator, values) : store.find(timestamp, key, operator, values);
}
}
use of com.cinchapi.ccl.type.Function in project concourse by cinchapi.
the class Convert method javaToThrift.
/**
* Return the Thrift Object that represents {@code object}.
*
* @param object
* @return the TObject
*/
public static TObject javaToThrift(Object object) {
if (object == null) {
return TObject.NULL;
} else {
ByteBuffer bytes;
Type type = null;
if (object instanceof Boolean) {
bytes = ByteBuffer.allocate(1);
bytes.put((boolean) object ? (byte) 1 : (byte) 0);
type = Type.BOOLEAN;
} else if (object instanceof Double) {
bytes = ByteBuffer.allocate(8);
bytes.putDouble((double) object);
type = Type.DOUBLE;
} else if (object instanceof Float) {
bytes = ByteBuffer.allocate(4);
bytes.putFloat((float) object);
type = Type.FLOAT;
} else if (object instanceof Link) {
bytes = ByteBuffer.allocate(8);
bytes.putLong(((Link) object).longValue());
type = Type.LINK;
} else if (object instanceof Long) {
bytes = ByteBuffer.allocate(8);
bytes.putLong((long) object);
type = Type.LONG;
} else if (object instanceof Integer) {
bytes = ByteBuffer.allocate(4);
bytes.putInt((int) object);
type = Type.INTEGER;
} else if (object instanceof BigDecimal) {
bytes = ByteBuffer.allocate(8);
bytes.putDouble((double) ((BigDecimal) object).doubleValue());
type = Type.DOUBLE;
} else if (object instanceof Tag) {
bytes = ByteBuffer.wrap(object.toString().getBytes(StandardCharsets.UTF_8));
type = Type.TAG;
} else if (object instanceof Timestamp) {
try {
bytes = ByteBuffer.allocate(8);
bytes.putLong(((Timestamp) object).getMicros());
type = Type.TIMESTAMP;
} catch (IllegalStateException e) {
throw new UnsupportedOperationException("Cannot convert string based Timestamp to a TObject");
}
} else if (object instanceof Function) {
type = Type.FUNCTION;
Function function = (Function) object;
byte[] nameBytes = function.operation().getBytes(StandardCharsets.UTF_8);
byte[] keyBytes = function.key().getBytes(StandardCharsets.UTF_8);
if (function instanceof IndexFunction) {
/*
* Schema:
* | type (1) | timestamp(8) | nameLength (4) | name
* (nameLength) | key |
*/
bytes = ByteBuffer.allocate(1 + 8 + 4 + nameBytes.length + keyBytes.length);
bytes.put((byte) FunctionType.INDEX.ordinal());
bytes.putLong(((TemporalFunction) function).timestamp());
bytes.putInt(nameBytes.length);
bytes.put(nameBytes);
bytes.put(keyBytes);
} else if (function instanceof KeyRecordsFunction) {
/*
* Schema:
* | type (1) | timestamp(8) | nameLength (4) | name
* (nameLength) | keyLength (4) | key (keyLength) | records
* (8 each) |
*/
KeyRecordsFunction func = (KeyRecordsFunction) function;
bytes = ByteBuffer.allocate(1 + 8 + 4 + nameBytes.length + 4 + keyBytes.length + 8 * func.source().size());
bytes.put((byte) FunctionType.KEY_RECORDS.ordinal());
bytes.putLong(((TemporalFunction) function).timestamp());
bytes.putInt(nameBytes.length);
bytes.put(nameBytes);
bytes.putInt(keyBytes.length);
bytes.put(keyBytes);
for (long record : func.source()) {
bytes.putLong(record);
}
} else if (function instanceof KeyConditionFunction) {
/*
* Schema:
* | type (1) | timestamp(8) | nameLength (4) | name
* (nameLength) | keyLength (4) | key (keyLength) |
* condition |
*/
KeyConditionFunction func = (KeyConditionFunction) function;
String condition = ConcourseCompiler.get().tokenize(func.source()).stream().map(Symbol::toString).collect(Collectors.joining(" "));
bytes = ByteBuffer.allocate(1 + 9 + 4 + nameBytes.length + 4 + keyBytes.length + condition.length());
bytes.put((byte) FunctionType.KEY_CONDITION.ordinal());
bytes.putLong(((TemporalFunction) function).timestamp());
bytes.putInt(nameBytes.length);
bytes.put(nameBytes);
bytes.putInt(keyBytes.length);
bytes.put(keyBytes);
bytes.put(condition.getBytes(StandardCharsets.UTF_8));
} else {
throw new UnsupportedOperationException("Cannot convert the following function to a TObject: " + function);
}
} else {
bytes = ByteBuffer.wrap(object.toString().getBytes(StandardCharsets.UTF_8));
type = Type.STRING;
}
bytes.rewind();
return new TObject(bytes, type).setJavaFormat(object);
}
}
use of com.cinchapi.ccl.type.Function in project concourse by cinchapi.
the class Stores method select.
/**
* Select all of the {@code keys} from {@code record} at {@code timestamp}
* within {@code store}.
* <p>
* This method contains optimizations to efficiently select multiple keys
* from a record with as few lookups as possible; especially if there are
* multiple {@link KeyType#NAVIGATION_KEY navigation keys}.
* </p>
*
* @param store
* @param keys
* @param record
* @param timestamp
* @return a mapping from each of the {@code keys} to the data held for it
* in {@code record} at {@code timestamp} within {@code store}
*/
public static Map<String, Set<TObject>> select(Store store, Collection<String> keys, long record, long timestamp) {
if (keys.isEmpty()) {
return ImmutableMap.of();
} else {
Map<String, Set<TObject>> data = new HashMap<>(keys.size());
Map<String, Set<TObject>> stored = null;
Node root = null;
int count = 1;
for (String key : keys) {
Key metadata = Keys.parse(key);
KeyType type = metadata.type();
if (type == KeyType.NAVIGATION_KEY) {
// Generate a single Graph containing all of the stops in
// each of the navigation keys.
root = root == null ? Node.root(record) : root;
Node node = root;
String[] stops = metadata.data();
for (String stop : stops) {
node = node.next(stop);
++count;
}
node.end();
} else {
Set<TObject> values;
if (type == KeyType.WRITABLE_KEY && keys.size() == 1) {
// efficient lookup source.
return ImmutableMap.of(key, lookupWithStrategy(store, key, record, timestamp));
} else if (type == KeyType.WRITABLE_KEY) {
// @formatter:off
stored = stored == null ? (timestamp == Time.NONE ? store.select(record) : store.select(record, timestamp)) : stored;
// @formatter:on
values = stored.get(key);
if (values == null) {
values = ImmutableSet.of();
}
} else if (type == KeyType.IDENTIFIER_KEY) {
values = ImmutableSet.of(Convert.javaToThrift(record));
} else if (type == KeyType.FUNCTION_KEY) {
Function function = metadata.data();
String method = Calculations.alias(function.operation()) + "KeyRecordAtomic";
Number value = Reflection.callStatic(Operations.class, method, function.key(), record, timestamp, store);
values = value != null ? ImmutableSet.of(Convert.javaToThrift(value)) : ImmutableSet.of();
} else {
values = ImmutableSet.of();
}
data.put(key, values);
}
}
if (root != null) {
// Iterate through the graph, in a breadth-first manner, to
// perform bulk selection at each Junctions.
Queue<Node> queue = new ArrayDeque<>(count);
queue.add(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
Collection<Node> successors = node.successors();
if (successors.isEmpty()) {
data.put(node.path, node.values());
} else {
queue.addAll(successors);
Collection<Long> links = node.links();
for (long link : links) {
Map<String, Set<TObject>> intermediate = null;
if (successors.size() > 1) {
// Bypassing the Strategy framework is
// acceptable here because we know that there
// are multiple keys that need to be selected
// from each record, so it makes sense to select
// the entire record from the Engine, once
intermediate = timestamp == Time.NONE ? store.select(link) : store.select(link, timestamp);
}
for (Node successor : successors) {
String stop = successor.stop;
if (intermediate == null) {
// This means there is only 1 successor, so
// the lookup should defer to the Strategy
// framework
intermediate = ImmutableMap.of(stop, lookupWithStrategy(store, stop, link, timestamp));
}
Set<TObject> values = intermediate.get(stop);
if (values != null) {
successor.store(values);
} else if (stop.equals(Constants.JSON_RESERVED_IDENTIFIER_NAME)) {
successor.store(Convert.javaToThrift(link));
}
}
}
}
}
}
return data.size() > 1 ? new OrderImposingMap<>(keys, data) : data;
}
}
Aggregations