3636import com .aerospike .mapper .annotations .AerospikeEmbed ;
3737import com .aerospike .mapper .annotations .AerospikeEmbed .EmbedType ;
3838import 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 ;
3942import com .aerospike .mapper .tools .TypeUtils .AnnotatedType ;
4043import com .aerospike .mapper .tools .configuration .ClassConfig ;
4144import 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
0 commit comments