Close #114: refactored to_ordered_(map|list) of PR #108
Project: http://git-wip-us.apache.org/repos/asf/incubator-hivemall/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-hivemall/commit/688daa5f Tree: http://git-wip-us.apache.org/repos/asf/incubator-hivemall/tree/688daa5f Diff: http://git-wip-us.apache.org/repos/asf/incubator-hivemall/diff/688daa5f Branch: refs/heads/master Commit: 688daa5f8e6a87fad2abf3b47a8a8353bc1c792a Parents: 69730f6 Author: Makoto Yui <[email protected]> Authored: Wed Sep 13 21:12:41 2017 +0900 Committer: Makoto Yui <[email protected]> Committed: Wed Sep 13 21:12:41 2017 +0900 ---------------------------------------------------------------------- .../hivemall/tools/map/UDAFToOrderedMap.java | 240 ++++++++++++++++--- .../java/hivemall/utils/hadoop/HiveUtils.java | 12 + .../tools/map/UDAFToOrderedMapTest.java | 36 +-- docs/gitbook/misc/generic_funcs.md | 54 ++++- 4 files changed, 292 insertions(+), 50 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/688daa5f/core/src/main/java/hivemall/tools/map/UDAFToOrderedMap.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/hivemall/tools/map/UDAFToOrderedMap.java b/core/src/main/java/hivemall/tools/map/UDAFToOrderedMap.java index a6b547f..5cdac4d 100644 --- a/core/src/main/java/hivemall/tools/map/UDAFToOrderedMap.java +++ b/core/src/main/java/hivemall/tools/map/UDAFToOrderedMap.java @@ -20,11 +20,17 @@ package hivemall.tools.map; import hivemall.utils.collections.maps.BoundedSortedMap; import hivemall.utils.hadoop.HiveUtils; +import hivemall.utils.lang.Preconditions; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.TreeMap; import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.apache.hadoop.hive.ql.exec.Description; import org.apache.hadoop.hive.ql.exec.UDFArgumentException; @@ -33,8 +39,16 @@ import org.apache.hadoop.hive.ql.metadata.HiveException; import org.apache.hadoop.hive.ql.parse.SemanticException; import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator; import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFParameterInfo; +import org.apache.hadoop.hive.serde2.objectinspector.MapObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory; +import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorUtils; +import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.StructField; +import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo; +import org.apache.hadoop.io.IntWritable; /** * Convert two aggregated columns into a sorted key-value map. @@ -63,70 +77,232 @@ public final class UDAFToOrderedMap extends UDAFToMap { int size = 0; if (typeInfo.length == 3) { ObjectInspector[] argOIs = info.getParameterObjectInspectors(); - if (HiveUtils.isBooleanTypeInfo(typeInfo[2])) { - reverseOrder = HiveUtils.getConstBoolean(argOIs[2]); - } else if (HiveUtils.isIntegerTypeInfo(typeInfo[2])) { - size = HiveUtils.getConstInt(argOIs[2]); + ObjectInspector argOI2 = argOIs[2]; + if (HiveUtils.isConstBoolean(argOI2)) { + reverseOrder = HiveUtils.getConstBoolean(argOI2); + } else if (HiveUtils.isConstInteger(argOI2)) { + size = HiveUtils.getConstInt(argOI2); if (size == 0) { - throw new UDFArgumentException("Map size must be nonzero: " + size); + throw new UDFArgumentException("Map size must be non-zero value: " + size); } reverseOrder = (size > 0); // positive size => top-k - size = Math.abs(size); } else { throw new UDFArgumentTypeException(2, - "The third argument must be boolean or integer type: " - + typeInfo[2].getTypeName()); + "The third argument must be boolean or int type: " + typeInfo[2].getTypeName()); } } if (reverseOrder) { // descending - return new DescendingMapEvaluator(size); + if (size == 0) { + return new ReverseOrderedMapEvaluator(); + } else { + return new TopKOrderedMapEvaluator(); + } } else { // ascending - return new AscendingMapEvaluator(size); + if (size == 0) { + return new NaturalOrderedMapEvaluator(); + } else { + return new TailKOrderedMapEvaluator(); + } } } - public static final class AscendingMapEvaluator extends UDAFToMapEvaluator { - - private final int size; + public static class NaturalOrderedMapEvaluator extends UDAFToMapEvaluator { - AscendingMapEvaluator(@Nonnegative int size) { - super(); - this.size = size; + @Override + public void reset(@SuppressWarnings("deprecation") AggregationBuffer agg) + throws HiveException { + ((MapAggregationBuffer) agg).container = new TreeMap<Object, Object>(); } + } + + public static class ReverseOrderedMapEvaluator extends UDAFToMapEvaluator { + @Override public void reset(@SuppressWarnings("deprecation") AggregationBuffer agg) throws HiveException { - if (size == 0) { - ((MapAggregationBuffer) agg).container = new TreeMap<Object, Object>(); - } else { - ((MapAggregationBuffer) agg).container = new BoundedSortedMap<Object, Object>(size); - } + ((MapAggregationBuffer) agg).container = new TreeMap<Object, Object>( + Collections.reverseOrder()); } } - public static final class DescendingMapEvaluator extends UDAFToMapEvaluator { + public static class TopKOrderedMapEvaluator extends GenericUDAFEvaluator { + + protected PrimitiveObjectInspector inputKeyOI; + protected ObjectInspector inputValueOI; + protected MapObjectInspector partialMapOI; + protected PrimitiveObjectInspector sizeOI; + + protected StructObjectInspector internalMergeOI; + + protected StructField partialMapField; + protected StructField sizeField; - private final int size; + @Override + public ObjectInspector init(Mode mode, ObjectInspector[] argOIs) throws HiveException { + super.init(mode, argOIs); + + // initialize input + if (mode == Mode.PARTIAL1 || mode == Mode.COMPLETE) {// from original data + this.inputKeyOI = HiveUtils.asPrimitiveObjectInspector(argOIs[0]); + this.inputValueOI = argOIs[1]; + this.sizeOI = HiveUtils.asIntegerOI(argOIs[2]); + } else {// from partial aggregation + StructObjectInspector soi = (StructObjectInspector) argOIs[0]; + this.internalMergeOI = soi; + + this.partialMapField = soi.getStructFieldRef("partialMap"); + // re-extract input key/value OIs + MapObjectInspector partialMapOI = (MapObjectInspector) partialMapField.getFieldObjectInspector(); + this.inputKeyOI = HiveUtils.asPrimitiveObjectInspector(partialMapOI.getMapKeyObjectInspector()); + this.inputValueOI = partialMapOI.getMapValueObjectInspector(); + + this.partialMapOI = ObjectInspectorFactory.getStandardMapObjectInspector( + ObjectInspectorUtils.getStandardObjectInspector(inputKeyOI), + ObjectInspectorUtils.getStandardObjectInspector(inputValueOI)); + + this.sizeField = soi.getStructFieldRef("size"); + this.sizeOI = (PrimitiveObjectInspector) sizeField.getFieldObjectInspector(); + } - DescendingMapEvaluator(int size) { - super(); - this.size = size; + // initialize output + final ObjectInspector outputOI; + if (mode == Mode.PARTIAL1 || mode == Mode.PARTIAL2) {// terminatePartial + outputOI = internalMergeOI(inputKeyOI, inputValueOI); + } else {// terminate + outputOI = ObjectInspectorFactory.getStandardMapObjectInspector( + ObjectInspectorUtils.getStandardObjectInspector(inputKeyOI), + ObjectInspectorUtils.getStandardObjectInspector(inputValueOI)); + } + return outputOI; + } + + @Nonnull + private static StructObjectInspector internalMergeOI( + @Nonnull PrimitiveObjectInspector keyOI, @Nonnull ObjectInspector valueOI) { + List<String> fieldNames = new ArrayList<String>(); + List<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(); + + fieldNames.add("partialMap"); + fieldOIs.add(ObjectInspectorFactory.getStandardMapObjectInspector( + ObjectInspectorUtils.getStandardObjectInspector(keyOI), + ObjectInspectorUtils.getStandardObjectInspector(valueOI))); + + fieldNames.add("size"); + fieldOIs.add(PrimitiveObjectInspectorFactory.writableIntObjectInspector); + + return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs); + } + + static class MapAggregationBuffer extends AbstractAggregationBuffer { + @Nullable + Map<Object, Object> container; + int size; + + MapAggregationBuffer() { + super(); + } } @Override public void reset(@SuppressWarnings("deprecation") AggregationBuffer agg) throws HiveException { - if (size == 0) { - ((MapAggregationBuffer) agg).container = new TreeMap<Object, Object>( - Collections.reverseOrder()); - } else { - ((MapAggregationBuffer) agg).container = new BoundedSortedMap<Object, Object>(size, - true); + MapAggregationBuffer myagg = (MapAggregationBuffer) agg; + myagg.container = null; + myagg.size = 0; + } + + @Override + public MapAggregationBuffer getNewAggregationBuffer() throws HiveException { + MapAggregationBuffer myagg = new MapAggregationBuffer(); + reset(myagg); + return myagg; + } + + @Override + public void iterate(@SuppressWarnings("deprecation") AggregationBuffer agg, + Object[] parameters) throws HiveException { + assert (parameters.length == 3); + if (parameters[0] == null) { + return; + } + + Object key = ObjectInspectorUtils.copyToStandardObject(parameters[0], inputKeyOI); + Object value = ObjectInspectorUtils.copyToStandardObject(parameters[1], inputValueOI); + int size = Math.abs(HiveUtils.getInt(parameters[2], sizeOI)); // size could be negative for tail-k + + MapAggregationBuffer myagg = (MapAggregationBuffer) agg; + if (myagg.container == null) { + initBuffer(myagg, size); + } + myagg.container.put(key, value); + } + + void initBuffer(@Nonnull MapAggregationBuffer agg, @Nonnegative int size) { + Preconditions.checkArgument(size > 0, "size MUST be greather than zero: " + size); + + agg.container = new BoundedSortedMap<Object, Object>(size, true); + agg.size = size; + } + + @Override + public Object terminatePartial(@SuppressWarnings("deprecation") AggregationBuffer agg) + throws HiveException { + MapAggregationBuffer myagg = (MapAggregationBuffer) agg; + + Object[] partialResult = new Object[2]; + partialResult[0] = myagg.container; + partialResult[1] = new IntWritable(myagg.size); + + return partialResult; + } + + @Override + public void merge(@SuppressWarnings("deprecation") AggregationBuffer agg, Object partial) + throws HiveException { + if (partial == null) { + return; + } + + MapAggregationBuffer myagg = (MapAggregationBuffer) agg; + + Object partialMapObj = internalMergeOI.getStructFieldData(partial, partialMapField); + Map<?, ?> partialMap = partialMapOI.getMap(HiveUtils.castLazyBinaryObject(partialMapObj)); + if (partialMap == null) { + return; + } + + if (myagg.container == null) { + Object sizeObj = internalMergeOI.getStructFieldData(partial, sizeField); + int size = HiveUtils.getInt(sizeObj, sizeOI); + initBuffer(myagg, size); + } + for (Map.Entry<?, ?> e : partialMap.entrySet()) { + Object key = ObjectInspectorUtils.copyToStandardObject(e.getKey(), inputKeyOI); + Object value = ObjectInspectorUtils.copyToStandardObject(e.getValue(), inputValueOI); + myagg.container.put(key, value); } } + @Override + @Nullable + public Map<Object, Object> terminate(@SuppressWarnings("deprecation") AggregationBuffer agg) + throws HiveException { + MapAggregationBuffer myagg = (MapAggregationBuffer) agg; + return myagg.container; + } + } + + public static class TailKOrderedMapEvaluator extends TopKOrderedMapEvaluator { + + @Override + void initBuffer(MapAggregationBuffer agg, int size) { + agg.container = new BoundedSortedMap<Object, Object>(size); + agg.size = size; + } + } + } http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/688daa5f/core/src/main/java/hivemall/utils/hadoop/HiveUtils.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/hivemall/utils/hadoop/HiveUtils.java b/core/src/main/java/hivemall/utils/hadoop/HiveUtils.java index afa8a58..8fba349 100644 --- a/core/src/main/java/hivemall/utils/hadoop/HiveUtils.java +++ b/core/src/main/java/hivemall/utils/hadoop/HiveUtils.java @@ -326,6 +326,18 @@ public final class HiveUtils { return ObjectInspectorUtils.isConstantObjectInspector(oi) && isStringOI(oi); } + public static boolean isConstInt(@Nonnull final ObjectInspector oi) { + return ObjectInspectorUtils.isConstantObjectInspector(oi) && isIntOI(oi); + } + + public static boolean isConstInteger(@Nonnull final ObjectInspector oi) { + return ObjectInspectorUtils.isConstantObjectInspector(oi) && isIntegerOI(oi); + } + + public static boolean isConstBoolean(@Nonnull final ObjectInspector oi) { + return ObjectInspectorUtils.isConstantObjectInspector(oi) && isBooleanOI(oi); + } + public static boolean isPrimitiveTypeInfo(@Nonnull TypeInfo typeInfo) { return typeInfo.getCategory() == ObjectInspector.Category.PRIMITIVE; } http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/688daa5f/core/src/test/java/hivemall/tools/map/UDAFToOrderedMapTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/hivemall/tools/map/UDAFToOrderedMapTest.java b/core/src/test/java/hivemall/tools/map/UDAFToOrderedMapTest.java index 61642f1..38bc5ae 100644 --- a/core/src/test/java/hivemall/tools/map/UDAFToOrderedMapTest.java +++ b/core/src/test/java/hivemall/tools/map/UDAFToOrderedMapTest.java @@ -18,10 +18,12 @@ */ package hivemall.tools.map; -import hivemall.tools.map.UDAFToOrderedMap.AscendingMapEvaluator; -import hivemall.tools.map.UDAFToOrderedMap.DescendingMapEvaluator; +import hivemall.tools.map.UDAFToOrderedMap.NaturalOrderedMapEvaluator; +import hivemall.tools.map.UDAFToOrderedMap.ReverseOrderedMapEvaluator; +import hivemall.tools.map.UDAFToOrderedMap.TailKOrderedMapEvaluator; +import hivemall.tools.map.UDAFToOrderedMap.TopKOrderedMapEvaluator; -import java.util.SortedMap; +import java.util.Map; import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; @@ -33,8 +35,8 @@ public class UDAFToOrderedMapTest { @Test public void testNaturalOrder() throws Exception { - AscendingMapEvaluator evaluator = new AscendingMapEvaluator(0); - AscendingMapEvaluator.MapAggregationBuffer agg = (AscendingMapEvaluator.MapAggregationBuffer) evaluator.getNewAggregationBuffer(); + NaturalOrderedMapEvaluator evaluator = new NaturalOrderedMapEvaluator(); + NaturalOrderedMapEvaluator.MapAggregationBuffer agg = (NaturalOrderedMapEvaluator.MapAggregationBuffer) evaluator.getNewAggregationBuffer(); ObjectInspector[] inputOIs = new ObjectInspector[] { PrimitiveObjectInspectorFactory.javaDoubleObjectInspector, @@ -50,7 +52,7 @@ public class UDAFToOrderedMapTest { evaluator.iterate(agg, new Object[] {keys[i], values[i]}); } - SortedMap<Object, Object> res = (SortedMap<Object, Object>) evaluator.terminate(agg); + Map<Object, Object> res = evaluator.terminate(agg); Object[] sortedValues = res.values().toArray(); Assert.assertEquals(3, sortedValues.length); @@ -63,8 +65,8 @@ public class UDAFToOrderedMapTest { @Test public void testReverseOrder() throws Exception { - DescendingMapEvaluator evaluator = new DescendingMapEvaluator(0); - DescendingMapEvaluator.MapAggregationBuffer agg = (DescendingMapEvaluator.MapAggregationBuffer) evaluator.getNewAggregationBuffer(); + ReverseOrderedMapEvaluator evaluator = new ReverseOrderedMapEvaluator(); + ReverseOrderedMapEvaluator.MapAggregationBuffer agg = (ReverseOrderedMapEvaluator.MapAggregationBuffer) evaluator.getNewAggregationBuffer(); ObjectInspector[] inputOIs = new ObjectInspector[] { PrimitiveObjectInspectorFactory.javaDoubleObjectInspector, @@ -81,7 +83,7 @@ public class UDAFToOrderedMapTest { evaluator.iterate(agg, new Object[] {keys[i], values[i]}); } - SortedMap<Object, Object> res = (SortedMap<Object, Object>) evaluator.terminate(agg); + Map<Object, Object> res = evaluator.terminate(agg); Object[] sortedValues = res.values().toArray(); Assert.assertEquals(3, sortedValues.length); @@ -94,9 +96,8 @@ public class UDAFToOrderedMapTest { @Test public void testTopK() throws Exception { - int size = 2; - DescendingMapEvaluator evaluator = new DescendingMapEvaluator(size); - DescendingMapEvaluator.MapAggregationBuffer agg = (DescendingMapEvaluator.MapAggregationBuffer) evaluator.getNewAggregationBuffer(); + TopKOrderedMapEvaluator evaluator = new TopKOrderedMapEvaluator(); + TopKOrderedMapEvaluator.MapAggregationBuffer agg = (TopKOrderedMapEvaluator.MapAggregationBuffer) evaluator.getNewAggregationBuffer(); ObjectInspector[] inputOIs = new ObjectInspector[] { PrimitiveObjectInspectorFactory.javaDoubleObjectInspector, @@ -105,6 +106,7 @@ public class UDAFToOrderedMapTest { final double[] keys = new double[] {0.7, 0.5, 0.8}; final String[] values = new String[] {"banana", "apple", "candy"}; + int size = 2; evaluator.init(GenericUDAFEvaluator.Mode.PARTIAL1, inputOIs); evaluator.reset(agg); @@ -113,7 +115,7 @@ public class UDAFToOrderedMapTest { evaluator.iterate(agg, new Object[] {keys[i], values[i], size}); } - SortedMap<Object, Object> res = (SortedMap<Object, Object>) evaluator.terminate(agg); + Map<Object, Object> res = evaluator.terminate(agg); Object[] sortedValues = res.values().toArray(); Assert.assertEquals(size, sortedValues.length); @@ -125,9 +127,8 @@ public class UDAFToOrderedMapTest { @Test public void testTailK() throws Exception { - int size = -2; - AscendingMapEvaluator evaluator = new AscendingMapEvaluator(Math.abs(size)); - AscendingMapEvaluator.MapAggregationBuffer agg = (AscendingMapEvaluator.MapAggregationBuffer) evaluator.getNewAggregationBuffer(); + TailKOrderedMapEvaluator evaluator = new TailKOrderedMapEvaluator(); + TailKOrderedMapEvaluator.MapAggregationBuffer agg = (TailKOrderedMapEvaluator.MapAggregationBuffer) evaluator.getNewAggregationBuffer(); ObjectInspector[] inputOIs = new ObjectInspector[] { PrimitiveObjectInspectorFactory.javaDoubleObjectInspector, @@ -136,6 +137,7 @@ public class UDAFToOrderedMapTest { final double[] keys = new double[] {0.7, 0.5, 0.8}; final String[] values = new String[] {"banana", "apple", "candy"}; + int size = -2; evaluator.init(GenericUDAFEvaluator.Mode.PARTIAL1, inputOIs); evaluator.reset(agg); @@ -144,7 +146,7 @@ public class UDAFToOrderedMapTest { evaluator.iterate(agg, new Object[] {keys[i], values[i], size}); } - SortedMap<Object, Object> res = (SortedMap<Object, Object>) evaluator.terminate(agg); + Map<Object, Object> res = evaluator.terminate(agg); Object[] sortedValues = res.values().toArray(); Assert.assertEquals(Math.abs(size), sortedValues.length); http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/688daa5f/docs/gitbook/misc/generic_funcs.md ---------------------------------------------------------------------- diff --git a/docs/gitbook/misc/generic_funcs.md b/docs/gitbook/misc/generic_funcs.md index 66b30e2..03e1ef3 100644 --- a/docs/gitbook/misc/generic_funcs.md +++ b/docs/gitbook/misc/generic_funcs.md @@ -85,7 +85,36 @@ This page describes a list of useful Hivemall generic functions. ## List UDAF -- `to_ordered_list(value [, const string options])` or `to_ordered_list(value, key [, const string options])` - Return list of values sorted by value itself or specific key +- `to_ordered_list(PRIMITIVE value [, PRIMITIVE key, const string options])` or `to_ordered_list(value, key [, const string options])` - Return list of values sorted by value itself or specific key + + ```sql + with t as ( + select 5 as key, 'apple' as value + union all + select 3 as key, 'banana' as value + union all + select 4 as key, 'candy' as value + union all + select 2 as key, 'donut' as value + union all + select 3 as key, 'egg' as value + ) + select -- expected output + to_ordered_list(value, key, '-reverse'), -- [apple, candy, (banana, egg | egg, banana), donut] (reverse order) + to_ordered_list(value, key, '-k 2'), -- [apple, candy] (top-k) + to_ordered_list(value, key, '-k 100'), -- [apple, candy, (banana, egg | egg, banana), dunut] + to_ordered_list(value, key, '-k 2 -reverse'), -- [donut, (banana | egg)] (reverse top-k = tail-k) + to_ordered_list(value, key), -- [donut, (banana, egg | egg, banana), candy, apple] (natural order) + to_ordered_list(value, key, '-k -2'), -- [donut, (banana | egg)] (tail-k) + to_ordered_list(value, key, '-k -100'), -- [donut, (banana, egg | egg, banana), candy, apple] + to_ordered_list(value, key, '-k -2 -reverse'), -- [apple, candy] (reverse tail-k = top-k) + to_ordered_list(value, '-k 2'), -- [egg, donut] (alphabetically) + to_ordered_list(key, '-k -2 -reverse'), -- [5, 4] (top-2 keys) + to_ordered_list(key) -- [2, 3, 3, 4, 5] (natural ordered keys) + from + t + ; + ``` # Bitset functions @@ -147,6 +176,29 @@ The compression level must be in range [-1,9] - `to_ordered_map(key, value [, const int k|const boolean reverseOrder=false])` - Convert two aggregated columns into an ordered key-value map + ```sql + with t as ( + select 10 as key, 'apple' as value + union all + select 3 as key, 'banana' as value + union all + select 4 as key, 'candy' as value + ) + select + to_ordered_map(key, value, true), -- {10:"apple",4:"candy",3:"banana"} (reverse) + to_ordered_map(key, value, 1), -- {10:"apple"} (top-1) + to_ordered_map(key, value, 2), -- {10:"apple",4:"candy"} (top-2) + to_ordered_map(key, value, 3), -- {10:"apple",4:"candy",3:"banana"} (top-3) + to_ordered_map(key, value, 100), -- {10:"apple",4:"candy",3:"banana"} (top-100) + to_ordered_map(key, value), -- {3:"banana",4:"candy",10:"apple"} (natural) + to_ordered_map(key, value, -1), -- {3:"banana"} (tail-1) + to_ordered_map(key, value, -2), -- {3:"banana",4:"candy"} (tail-2) + to_ordered_map(key, value, -3), -- {3:"banana",4:"candy",10:"apple"} (tail-3) + to_ordered_map(key, value, -100) -- {3:"banana",4:"candy",10:"apple"} (tail-100) + from t + ; + ``` + # MapReduce functions - `rowid()` - Returns a generated row id of a form {TASK_ID}-{SEQUENCE_NUMBER}
