Skip to content

Commit 84609ab

Browse files
committed
Mulit-operational statements now functional, although there is cleanup
to be done.
1 parent 4dc09b5 commit 84609ab

File tree

4 files changed

+316
-22
lines changed

4 files changed

+316
-22
lines changed

src/main/java/com/aerospike/mapper/tools/AeroMapper.java

Lines changed: 189 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
import com.aerospike.mapper.annotations.AerospikeEmbed;
3737
import com.aerospike.mapper.annotations.AerospikeEmbed.EmbedType;
3838
import com.aerospike.mapper.tools.ClassCache.PolicyType;
39+
import com.aerospike.mapper.tools.ResultsUnpacker.ArrayUnpacker;
40+
import com.aerospike.mapper.tools.ResultsUnpacker.ElementUnpacker;
41+
import com.aerospike.mapper.tools.ResultsUnpacker.ListUnpacker;
3942
import com.aerospike.mapper.tools.TypeUtils.AnnotatedType;
4043
import com.aerospike.mapper.tools.configuration.ClassConfig;
4144
import com.aerospike.mapper.tools.configuration.Configuration;
@@ -362,6 +365,8 @@ public static class VirtualList<E> {
362365
private final Key key;
363366
private final EmbedType listType;
364367
private final EmbedType elementType;
368+
private final Function<Object, Object> instanceMapper;
369+
365370

366371
public VirtualList(@NotNull AeroMapper mapper, @NotNull Object object, @NotNull String binName, @NotNull Class<E> clazz) {
367372
Class<?> owningClazz = object.getClass();
@@ -399,13 +404,157 @@ public VirtualList(@NotNull AeroMapper mapper, @NotNull Object object, @NotNull
399404
else {
400405
throw new AerospikeException(String.format("Bin %s on class %s is not mapped via a listMapper. This is unexpected", binName, clazz.getSimpleName()));
401406
}
407+
this.instanceMapper = src -> listMapper.fromAerospikeInstanceFormat(src);
402408
}
403409

404410
public VirtualList<E> keptInSync(boolean inSync) {
405411
return this;
406412
}
407413

414+
public class MultiOperation<E> {
415+
private VirtualList<E> virtualList;
416+
private List<Interactor> interactions;
417+
private int indexToReturn = -1;
418+
private WritePolicy writePolicy;
419+
420+
private MultiOperation(@NotNull VirtualList<E> virtualList, @NotNull WritePolicy writePolicy) {
421+
this.virtualList = virtualList;
422+
this.interactions = new ArrayList<>();
423+
this.writePolicy = writePolicy;
424+
}
425+
426+
public MultiOperation<E> append(E item) {
427+
Object aerospikeItem = listMapper.toAerospikeInstanceFormat(item);
428+
this.interactions.add(new Interactor(virtualList.getAppendOperation(aerospikeItem)));
429+
return this;
430+
}
431+
public MultiOperation<E> removeByKeyRange(Object startKey, Object endKey) {
432+
// TODO: Be able to change the return type based on the asResult() function call
433+
this.interactions.add(getRemoveRangeInteractor(startKey, endKey, true));
434+
return this;
435+
}
436+
437+
public MultiOperation<E> get(int index) {
438+
this.interactions.add(getIndexInteractor(index));
439+
return this;
440+
}
441+
442+
public MultiOperation<E> size() {
443+
this.interactions.add(getSizeInteractor());
444+
return this;
445+
}
446+
447+
public MultiOperation<E> asResult() {
448+
if (interactions.isEmpty()) {
449+
throw new AerospikeException("asResult() cannot mark an item as the function result if there are no items to process");
450+
}
451+
else if (this.indexToReturn >= 0) {
452+
throw new AerospikeException("asResult() can only be called once in a multi operation");
453+
}
454+
else {
455+
this.indexToReturn = this.interactions.size() - 1;
456+
}
457+
return this;
458+
}
459+
460+
public Object end() {
461+
if (this.interactions.isEmpty()) {
462+
return null;
463+
}
464+
this.writePolicy.respondAllOps = true;
465+
Operation[] operations = new Operation[this.interactions.size()];
466+
int count = 0;
467+
for (Interactor thisInteractor : this.interactions) {
468+
operations[count++] = thisInteractor.getOperation();
469+
}
470+
471+
Record record = this.virtualList.mapper.mClient.operate(writePolicy, key, operations);
472+
473+
if (count == 1) {
474+
Object resultObj = record.getValue(binName);
475+
return this.interactions.get(0).getResult(resultObj);
476+
}
477+
else {
478+
List<?> resultList = record.getList(binName);
479+
if (indexToReturn < 0) {
480+
int listSize = this.interactions.size();
481+
indexToReturn = listSize-1;
482+
// Determine the last GET operation
483+
for (int i = listSize-1; i >= 0; i--) {
484+
if (!this.interactions.get(i).isWriteOperation()) {
485+
indexToReturn = i;
486+
break;
487+
}
488+
}
489+
}
490+
return this.interactions.get(indexToReturn).getResult(resultList.get(indexToReturn));
491+
}
492+
}
493+
}
494+
495+
public MultiOperation<E> beginMulti() {
496+
return this.beginMulti(null);
497+
}
498+
499+
public MultiOperation<E> beginMulti(WritePolicy writePolicy) {
500+
if (writePolicy == null) {
501+
writePolicy = new WritePolicy(owningEntry.getWritePolicy());
502+
writePolicy.recordExistsAction = RecordExistsAction.UPDATE;
503+
}
504+
return new MultiOperation<E>(this, writePolicy);
505+
}
506+
408507

508+
/**
509+
* Remove items from the list matching the specified key. If the list is mapped to a MAP in Aerospike, the start key and end key will dictate the range of keys to be removed,
510+
* inclusive of the start, exclusive of the end.
511+
* <p/>
512+
* If the list is mapped to a LIST in Aerospike however, the start and end range represent values to be removed from the list.
513+
* <p/>
514+
* The result of the method is a list of the records which have been removed from the database if returnResults is true, null otherwise.
515+
* @param startKey
516+
* @param endKey
517+
* @param returnResults
518+
* @return
519+
*/
520+
public List<E> removeByKeyRange(Object startKey, Object endKey, boolean returnResults) {
521+
return this.removeByKeyRange(null, startKey, endKey, returnResults);
522+
}
523+
524+
public List<E> removeByKeyRange(WritePolicy writePolicy, Object startKey, Object endKey, boolean returnResults) {
525+
if (writePolicy == null) {
526+
writePolicy = new WritePolicy(owningEntry.getWritePolicy());
527+
writePolicy.recordExistsAction = RecordExistsAction.UPDATE;
528+
}
529+
Interactor interactor = getRemoveRangeInteractor(startKey, endKey, returnResults);
530+
Record record = this.mapper.mClient.operate(writePolicy, key, interactor.getOperation());
531+
532+
return (List<E>)interactor.getResult(record.getList(binName));
533+
}
534+
535+
private Interactor getRemoveRangeInteractor(Object startKey, Object endKey, boolean returnResults) {
536+
Object aerospikeStartKey = elementEntry.translateKeyToAerospikeKey(startKey);
537+
Object aerospikeEndKey = elementEntry.translateKeyToAerospikeKey(endKey);
538+
Interactor interactor;
539+
if (listType == EmbedType.LIST) {
540+
if (returnResults) {
541+
interactor = new Interactor(ListOperation.removeByValueRange(binName, Value.get(aerospikeStartKey), Value.get(aerospikeEndKey), ListReturnType.VALUE), new ArrayUnpacker(instanceMapper));
542+
}
543+
else {
544+
interactor = new Interactor(ListOperation.removeByValueRange(binName, Value.get(aerospikeStartKey), Value.get(aerospikeEndKey), ListReturnType.NONE));
545+
}
546+
}
547+
else {
548+
if (returnResults) {
549+
interactor = new Interactor( MapOperation.removeByKeyRange(binName, Value.get(aerospikeStartKey), Value.get(aerospikeEndKey), MapReturnType.KEY_VALUE), new ArrayUnpacker(instanceMapper));
550+
}
551+
else {
552+
interactor = new Interactor( MapOperation.removeByKeyRange(binName, Value.get(aerospikeStartKey), Value.get(aerospikeEndKey), MapReturnType.NONE));
553+
}
554+
}
555+
return interactor;
556+
}
557+
409558
private Operation getAppendOperation(Object aerospikeObject) {
410559
if (aerospikeObject instanceof Entry) {
411560
Entry<Object, Object> entry = (Entry) aerospikeObject;
@@ -415,35 +564,61 @@ private Operation getAppendOperation(Object aerospikeObject) {
415564
return ListOperation.append(binName, Value.get(aerospikeObject));
416565
}
417566
}
418-
public VirtualList<E> append(E element) {
567+
public long append(E element) {
419568
return this.append(null, element);
420569
}
421570

422-
public VirtualList<E> append(WritePolicy writePolicy, E element) {
571+
public long append(WritePolicy writePolicy, E element) {
423572
Object result = listMapper.toAerospikeInstanceFormat(element);
424573
if (writePolicy == null) {
425574
writePolicy = new WritePolicy(owningEntry.getWritePolicy());
426575
writePolicy.recordExistsAction = RecordExistsAction.UPDATE;
427576
}
428-
this.mapper.mClient.operate(writePolicy, key, getAppendOperation(result));
429-
return this;
577+
Record record = this.mapper.mClient.operate(writePolicy, key, getAppendOperation(result));
578+
return record.getLong(binName);
430579
}
431580

581+
582+
432583
public E get(int index) {
433-
// TODO
434-
//Object aerospikeKey = elementEntry.translateKeyToAerospikeKey(key);
435-
Operation operation;
584+
return get(null, index);
585+
}
586+
587+
private Interactor getIndexInteractor(int index) {
436588
if (listType == EmbedType.LIST) {
437-
operation = ListOperation.getByIndex(binName, index, ListReturnType.VALUE);
589+
return new Interactor(ListOperation.getByIndex(binName, index, ListReturnType.VALUE), new ElementUnpacker(instanceMapper));
438590
}
439591
else {
440-
operation = MapOperation.getByIndex(binName, index, MapReturnType.KEY_VALUE);
592+
return new Interactor(MapOperation.getByIndex(binName, index, MapReturnType.KEY_VALUE), ListUnpacker.instance, new ElementUnpacker(instanceMapper));
441593
}
442-
Record record = this.mapper.mClient.operate(null, key, operation);
443-
List<Object> list = (List<Object>) record.getList(binName);
444-
Object result = listMapper.fromAerospikeInstanceFormat(list.get(0));
445-
446-
return (E) result;
594+
}
595+
596+
public E get(Policy policy, int index) {
597+
if (policy == null) {
598+
policy = new Policy(owningEntry.getReadPolicy());
599+
}
600+
601+
Interactor interactor = getIndexInteractor(index);
602+
Record record = this.mapper.mClient.operate(null, key, interactor.getOperation());
603+
return (E)interactor.getResult(record.getList(binName));
604+
}
605+
606+
private Interactor getSizeInteractor() {
607+
if (listType == EmbedType.LIST) {
608+
return new Interactor(ListOperation.size(binName));
609+
}
610+
else {
611+
return new Interactor(MapOperation.size(binName));
612+
}
613+
}
614+
615+
public long size(Policy policy) {
616+
if (policy == null) {
617+
policy = new Policy(owningEntry.getReadPolicy());
618+
}
619+
Interactor interactor = getSizeInteractor();
620+
Record record = this.mapper.mClient.operate(null, key, interactor.getOperation());
621+
return record.getLong(binName);
447622
}
448623
}
449624

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.aerospike.mapper.tools;
2+
3+
import javax.validation.constraints.NotNull;
4+
5+
import com.aerospike.client.Operation;
6+
7+
public class Interactor {
8+
private final Operation operation;
9+
private final ResultsUnpacker []resultsUnpackers;
10+
11+
public Interactor(@NotNull Operation operation, @NotNull ResultsUnpacker ... resultsUnpackers) {
12+
super();
13+
this.operation = operation;
14+
this.resultsUnpackers = resultsUnpackers;
15+
}
16+
public Operation getOperation() {
17+
return operation;
18+
}
19+
public Object getResult(Object rawResult) {
20+
Object result = rawResult;
21+
for (ResultsUnpacker thisUnpacker : resultsUnpackers) {
22+
result = thisUnpacker.unpack(result);
23+
}
24+
return result;
25+
}
26+
public boolean isWriteOperation() {
27+
switch (operation.type) {
28+
case ADD:
29+
case APPEND:
30+
case BIT_MODIFY:
31+
case CDT_MODIFY:
32+
case DELETE:
33+
case MAP_MODIFY:
34+
case PREPEND:
35+
case TOUCH:
36+
case WRITE:
37+
return true;
38+
default:
39+
return false;
40+
}
41+
}
42+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.aerospike.mapper.tools;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.function.Function;
6+
7+
public interface ResultsUnpacker {
8+
public Object unpack(Object object);
9+
10+
public static class ListUnpacker implements ResultsUnpacker {
11+
private ListUnpacker() {
12+
}
13+
@Override
14+
public Object unpack(Object object) {
15+
if (object == null) {
16+
return null;
17+
}
18+
else {
19+
List<?> list = (List<?>)object;
20+
return list.isEmpty() ? null : list.get(0);
21+
}
22+
}
23+
public final static ListUnpacker instance = new ListUnpacker();
24+
}
25+
26+
public static class IdentityUnpacker implements ResultsUnpacker {
27+
private IdentityUnpacker() {
28+
}
29+
@Override
30+
public Object unpack(Object object) {
31+
return object;
32+
}
33+
public final static IdentityUnpacker instance = new IdentityUnpacker();
34+
}
35+
36+
public static class ElementUnpacker implements ResultsUnpacker {
37+
Function<Object, Object> function;
38+
public ElementUnpacker(Function<Object, Object> itemMapper) {
39+
this.function = itemMapper;
40+
}
41+
@Override
42+
public Object unpack(Object object) {
43+
return function.apply(object);
44+
}
45+
}
46+
public static class ArrayUnpacker implements ResultsUnpacker {
47+
Function<Object, Object> function;
48+
public ArrayUnpacker(Function<Object, Object> itemMapper) {
49+
this.function = itemMapper;
50+
}
51+
@Override
52+
public Object unpack(Object object) {
53+
if (object == null) {
54+
return null;
55+
}
56+
57+
List<Object> source = (List<Object>)object;
58+
List<Object> results = new ArrayList<>(source.size());
59+
for (Object thisObject : source) {
60+
results.add(function.apply(thisObject));
61+
}
62+
return results;
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)