[ASTERIXDB-2610][RT][*DB] Improve deep comparison runtime

- user model changes: no
- storage format changes: no
- interface changes: yes

Details:
Avoid data copies during comparison (no visitable pointables).
Allow comparison of non-tagged values to avoid copying the tag
when accessing an array element or record field.

- added TaggedValueReference.
- changed ILogicalBinaryComparator to use TaggedValueReference
	instead of IPointable.
- removed numberOfItems() from ListAccessorUtil and used
	AOrderedListSerializerDeserializer.getNumberOfItems().
- removed storage & IPointable pools from AbstractAGenericBinaryComparator
	& LogicalComplexBinaryComparator and used TaggedValueReference pool.
- removed compareNumbers() from ComparatorUtil since IPointable's
	have been replaced with TaggedValueReference.
- adapted record comparison logic in LogicalComplexBinaryComparator
	to match the logic in the physical comparator.

Change-Id: Id9ece93c704f566d7bdb7fd17b1ba92713c917d5
Reviewed-on: https://asterix-gerrit.ics.uci.edu/3466
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
Reviewed-by: Till Westmann <tillw@apache.org>
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ILogicalBinaryComparator.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ILogicalBinaryComparator.java
index 982a5a0..78ef12d 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ILogicalBinaryComparator.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ILogicalBinaryComparator.java
@@ -21,7 +21,6 @@
 import org.apache.asterix.om.base.IAObject;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.data.std.api.IPointable;
 
 public interface ILogicalBinaryComparator {
 
@@ -58,11 +57,11 @@
         }
     }
 
-    Result compare(IPointable left, IPointable right) throws HyracksDataException;
+    Result compare(TaggedValueReference left, TaggedValueReference right) throws HyracksDataException;
 
-    Result compare(IPointable left, IAObject rightConstant);
+    Result compare(TaggedValueReference left, IAObject rightConstant);
 
-    Result compare(IAObject leftConstant, IPointable right);
+    Result compare(IAObject leftConstant, TaggedValueReference right);
 
     Result compare(IAObject leftConstant, IAObject rightConstant);
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ListAccessorUtil.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ListAccessorUtil.java
index ba114c0..5350c0c 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ListAccessorUtil.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/ListAccessorUtil.java
@@ -19,10 +19,13 @@
 
 package org.apache.asterix.dataflow.data.common;
 
+import static org.apache.asterix.om.types.ATypeTag.VALUE_TYPE_MAPPING;
+
 import java.io.IOException;
 
 import org.apache.asterix.dataflow.data.nontagged.serde.AOrderedListSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.AUnorderedListSerializerDeserializer;
+import org.apache.asterix.dataflow.data.nontagged.serde.SerializerDeserializerUtil;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.EnumDeserializer;
 import org.apache.asterix.om.utils.NonTaggedFormatUtil;
@@ -52,6 +55,7 @@
      */
     public static boolean getItem(byte[] listBytes, int start, int itemIndex, ATypeTag listTag, ATypeTag listItemTag,
             IPointable pointable, ArrayBackedValueStorage storage) throws IOException {
+        // TODO(ali): this method should be removed if hashing is fixed to avoid copying the tag
         int itemOffset;
         if (listTag == ATypeTag.MULTISET) {
             itemOffset = AUnorderedListSerializerDeserializer.getItemOffset(listBytes, start, itemIndex);
@@ -78,13 +82,24 @@
         }
     }
 
-    public static int numberOfItems(byte[] listBytes, int start) {
-        if (listBytes[start] == ATypeTag.SERIALIZED_UNORDEREDLIST_TYPE_TAG) {
-            return AUnorderedListSerializerDeserializer.getNumberOfItems(listBytes, start);
-        } else if (listBytes[start] == ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG) {
-            return AOrderedListSerializerDeserializer.getNumberOfItems(listBytes, start);
+    public static void getItemFromList(TaggedValueReference listValue, int itemIndex, TaggedValueReference item,
+            ATypeTag arrayItemTag, boolean itemHasTag) throws IOException {
+        int itemOffset = SerializerDeserializerUtil.getItemOffsetNonTagged(listValue, itemIndex);
+        getItem(listValue, itemOffset, item, arrayItemTag, itemHasTag);
+    }
+
+    private static void getItem(TaggedValueReference listValue, int itemOffset, TaggedValueReference item,
+            ATypeTag listItemTag, boolean itemHasTag) throws IOException {
+        byte[] listBytes = listValue.getByteArray();
+        ATypeTag itemTag;
+        int itemValueOffset = itemOffset;
+        if (itemHasTag) {
+            itemTag = VALUE_TYPE_MAPPING[listBytes[itemOffset]];
+            itemValueOffset = itemOffset + 1;
         } else {
-            throw new IllegalStateException();
+            itemTag = listItemTag;
         }
+        int itemValueLength = NonTaggedFormatUtil.getFieldValueLength(listBytes, itemOffset, itemTag, itemHasTag);
+        item.set(listBytes, itemValueOffset, itemValueLength, itemTag);
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TaggedValueReference.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TaggedValueReference.java
new file mode 100644
index 0000000..db50e5e
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/common/TaggedValueReference.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.asterix.dataflow.data.common;
+
+import java.util.Objects;
+
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.hyracks.data.std.api.IValueReference;
+
+public final class TaggedValueReference implements IValueReference {
+
+    private byte[] bytes;
+    private int valueStart;
+    private int valueLength;
+    private ATypeTag tag;
+
+    public void set(byte[] bytes, int valueStart, int valueLength, ATypeTag tag) {
+        Objects.requireNonNull(tag);
+        this.bytes = bytes;
+        this.valueStart = valueStart;
+        this.valueLength = valueLength;
+        this.tag = tag;
+    }
+
+    @Override
+    public byte[] getByteArray() {
+        return bytes;
+    }
+
+    @Override
+    public int getStartOffset() {
+        return valueStart;
+    }
+
+    @Override
+    public int getLength() {
+        return valueLength;
+    }
+
+    public ATypeTag getTag() {
+        return tag;
+    }
+}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AGenericAscBinaryComparator.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AGenericAscBinaryComparator.java
index f78c4b3..3d2e1d1 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AGenericAscBinaryComparator.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AGenericAscBinaryComparator.java
@@ -18,17 +18,25 @@
  */
 package org.apache.asterix.dataflow.data.nontagged.comparators;
 
+import static org.apache.asterix.om.types.ATypeTag.VALUE_TYPE_MAPPING;
+
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.om.types.IAType;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 
 final class AGenericAscBinaryComparator extends AbstractAGenericBinaryComparator {
 
+    private final TaggedValueReference leftValue = new TaggedValueReference();
+    private final TaggedValueReference rightValue = new TaggedValueReference();
+
     AGenericAscBinaryComparator(IAType leftType, IAType rightType) {
         super(leftType, rightType);
     }
 
     @Override
     public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) throws HyracksDataException {
-        return compare(leftType, b1, s1, l1, rightType, b2, s2, l2);
+        leftValue.set(b1, s1 + 1, l1 - 1, VALUE_TYPE_MAPPING[b1[s1]]);
+        rightValue.set(b2, s2 + 1, l2 - 1, VALUE_TYPE_MAPPING[b2[s2]]);
+        return compare(leftType, leftValue, rightType, rightValue);
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AGenericDescBinaryComparator.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AGenericDescBinaryComparator.java
index 3f958bf..199fdc6 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AGenericDescBinaryComparator.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AGenericDescBinaryComparator.java
@@ -18,11 +18,17 @@
  */
 package org.apache.asterix.dataflow.data.nontagged.comparators;
 
+import static org.apache.asterix.om.types.ATypeTag.VALUE_TYPE_MAPPING;
+
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.om.types.IAType;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 
 final class AGenericDescBinaryComparator extends AbstractAGenericBinaryComparator {
 
+    private final TaggedValueReference leftValue = new TaggedValueReference();
+    private final TaggedValueReference rightValue = new TaggedValueReference();
+
     // interval asc and desc comparators are not the inverse of each other.
     // thus, we need to specify the interval desc comparator factory for descending comparisons.
     AGenericDescBinaryComparator(IAType leftType, IAType rightType) {
@@ -31,11 +37,13 @@
 
     @Override
     public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) throws HyracksDataException {
-        return -compare(leftType, b1, s1, l1, rightType, b2, s2, l2);
+        leftValue.set(b1, s1 + 1, l1 - 1, VALUE_TYPE_MAPPING[b1[s1]]);
+        rightValue.set(b2, s2 + 1, l2 - 1, VALUE_TYPE_MAPPING[b2[s2]]);
+        return -compare(leftType, leftValue, rightType, rightValue);
     }
 
     @Override
     protected int compareInterval(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
-        return -AIntervalDescPartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+        return -AIntervalDescPartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AbstractAGenericBinaryComparator.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AbstractAGenericBinaryComparator.java
index b3cd642..2e58cd4 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AbstractAGenericBinaryComparator.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/AbstractAGenericBinaryComparator.java
@@ -19,24 +19,24 @@
 package org.apache.asterix.dataflow.data.nontagged.comparators;
 
 import static org.apache.asterix.om.util.container.ObjectFactories.RECORD_FACTORY;
-import static org.apache.asterix.om.util.container.ObjectFactories.STORAGE_FACTORY;
-import static org.apache.asterix.om.util.container.ObjectFactories.VOID_FACTORY;
+import static org.apache.asterix.om.util.container.ObjectFactories.VALUE_FACTORY;
 
 import java.io.IOException;
 
 import org.apache.asterix.dataflow.data.common.ListAccessorUtil;
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADateSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADateTimeSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADayTimeDurationSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.ATimeSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.AYearMonthDurationSerializerDeserializer;
+import org.apache.asterix.dataflow.data.nontagged.serde.SerializerDeserializerUtil;
 import org.apache.asterix.om.pointables.nonvisitor.RecordField;
 import org.apache.asterix.om.pointables.nonvisitor.SortedRecord;
 import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.AbstractCollectionType;
-import org.apache.asterix.om.types.EnumDeserializer;
 import org.apache.asterix.om.types.IAType;
 import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
 import org.apache.asterix.om.types.hierachy.ATypeHierarchy.Domain;
@@ -44,12 +44,9 @@
 import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.accessors.RawBinaryComparatorFactory;
-import org.apache.hyracks.data.std.api.IMutableValueStorage;
-import org.apache.hyracks.data.std.api.IPointable;
 import org.apache.hyracks.data.std.primitive.BooleanPointable;
 import org.apache.hyracks.data.std.primitive.ByteArrayPointable;
 import org.apache.hyracks.data.std.primitive.UTF8StringPointable;
-import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
 
 /**
  * This comparator is an ordering comparator. It deals with MISSING, NULL, and incompatible types different than the
@@ -59,9 +56,8 @@
 
     protected final IAType leftType;
     protected final IAType rightType;
-    private final ListObjectPool<IMutableValueStorage, Void> storageAllocator = new ListObjectPool<>(STORAGE_FACTORY);
-    private final ListObjectPool<IPointable, Void> voidPointableAllocator = new ListObjectPool<>(VOID_FACTORY);
     private final ListObjectPool<SortedRecord, ARecordType> recordPool = new ListObjectPool<>(RECORD_FACTORY);
+    private final ListObjectPool<TaggedValueReference, Void> taggedValueAllocator = new ListObjectPool<>(VALUE_FACTORY);
 
     AbstractAGenericBinaryComparator(IAType leftType, IAType rightType) {
         // factory should have already made sure to get the actual type (and no null types)
@@ -69,10 +65,10 @@
         this.rightType = rightType;
     }
 
-    protected final int compare(IAType leftType, byte[] b1, int s1, int l1, IAType rightType, byte[] b2, int s2, int l2)
-            throws HyracksDataException {
-        ATypeTag tag1 = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(b1[s1]);
-        ATypeTag tag2 = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(b2[s2]);
+    protected final int compare(IAType leftType, TaggedValueReference leftValue, IAType rightType,
+            TaggedValueReference rightValue) throws HyracksDataException {
+        ATypeTag tag1 = leftValue.getTag();
+        ATypeTag tag2 = rightValue.getTag();
         if (tag1 == null || tag2 == null) {
             throw new IllegalStateException("Could not recognize the type of data.");
         }
@@ -86,56 +82,63 @@
         } else if (tag2 == ATypeTag.NULL) {
             return 1;
         }
+        byte[] b1 = leftValue.getByteArray();
+        int s1 = leftValue.getStartOffset();
+        int l1 = leftValue.getLength();
+        byte[] b2 = rightValue.getByteArray();
+        int s2 = rightValue.getStartOffset();
+        int l2 = rightValue.getLength();
+
         if (ATypeHierarchy.isCompatible(tag1, tag2) && ATypeHierarchy.getTypeDomain(tag1) == Domain.NUMERIC) {
-            return ComparatorUtil.compareNumbers(tag1, b1, s1 + 1, tag2, b2, s2 + 1);
+            return ComparatorUtil.compareNumbers(tag1, b1, s1, tag2, b2, s2);
         }
         // currently only numbers are compatible. if two tags are not compatible, we compare the tags to generate order
         if (tag1 != tag2) {
-            return Byte.compare(b1[s1], b2[s2]);
+            return Byte.compare(tag1.serialize(), tag2.serialize());
         }
 
         switch (tag1) {
             case STRING:
-                return UTF8StringPointable.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return UTF8StringPointable.compare(b1, s1, l1, b2, s2, l2);
             case UUID:
-                return AUUIDPartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return AUUIDPartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
             case BOOLEAN:
-                return BooleanPointable.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return BooleanPointable.compare(b1, s1, l1, b2, s2, l2);
             case TIME:
-                return Integer.compare(ATimeSerializerDeserializer.getChronon(b1, s1 + 1),
-                        ATimeSerializerDeserializer.getChronon(b2, s2 + 1));
+                return Integer.compare(ATimeSerializerDeserializer.getChronon(b1, s1),
+                        ATimeSerializerDeserializer.getChronon(b2, s2));
             case DATE:
-                return Integer.compare(ADateSerializerDeserializer.getChronon(b1, s1 + 1),
-                        ADateSerializerDeserializer.getChronon(b2, s2 + 1));
+                return Integer.compare(ADateSerializerDeserializer.getChronon(b1, s1),
+                        ADateSerializerDeserializer.getChronon(b2, s2));
             case YEARMONTHDURATION:
-                return Integer.compare(AYearMonthDurationSerializerDeserializer.getYearMonth(b1, s1 + 1),
-                        AYearMonthDurationSerializerDeserializer.getYearMonth(b2, s2 + 1));
+                return Integer.compare(AYearMonthDurationSerializerDeserializer.getYearMonth(b1, s1),
+                        AYearMonthDurationSerializerDeserializer.getYearMonth(b2, s2));
             case DATETIME:
-                return Long.compare(ADateTimeSerializerDeserializer.getChronon(b1, s1 + 1),
-                        ADateTimeSerializerDeserializer.getChronon(b2, s2 + 1));
+                return Long.compare(ADateTimeSerializerDeserializer.getChronon(b1, s1),
+                        ADateTimeSerializerDeserializer.getChronon(b2, s2));
             case DAYTIMEDURATION:
-                return Long.compare(ADayTimeDurationSerializerDeserializer.getDayTime(b1, s1 + 1),
-                        ADayTimeDurationSerializerDeserializer.getDayTime(b2, s2 + 1));
+                return Long.compare(ADayTimeDurationSerializerDeserializer.getDayTime(b1, s1),
+                        ADayTimeDurationSerializerDeserializer.getDayTime(b2, s2));
             case RECTANGLE:
-                return ARectanglePartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return ARectanglePartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
             case CIRCLE:
-                return ACirclePartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return ACirclePartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
             case POINT:
-                return APointPartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return APointPartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
             case POINT3D:
-                return APoint3DPartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return APoint3DPartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
             case LINE:
-                return ALinePartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return ALinePartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
             case POLYGON:
-                return APolygonPartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return APolygonPartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
             case DURATION:
-                return ADurationPartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return ADurationPartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
             case INTERVAL:
                 return compareInterval(b1, s1, l1, b2, s2, l2);
             case BINARY:
-                return ByteArrayPointable.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+                return ByteArrayPointable.compare(b1, s1, l1, b2, s2, l2);
             case ARRAY:
-                return compareArrays(leftType, b1, s1, rightType, b2, s2);
+                return compareArrays(leftType, leftValue, rightType, rightValue);
             case OBJECT:
                 return compareRecords(leftType, b1, s1, rightType, b2, s2);
             default:
@@ -144,32 +147,31 @@
     }
 
     protected int compareInterval(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
-        return AIntervalAscPartialBinaryComparatorFactory.compare(b1, s1 + 1, l1 - 1, b2, s2 + 1, l2 - 1);
+        return AIntervalAscPartialBinaryComparatorFactory.compare(b1, s1, l1, b2, s2, l2);
     }
 
-    private int compareArrays(IAType leftType, byte[] b1, int s1, IAType rightType, byte[] b2, int s2)
-            throws HyracksDataException {
-        int leftNumItems = ListAccessorUtil.numberOfItems(b1, s1);
-        int rightNumItems = ListAccessorUtil.numberOfItems(b2, s2);
-        IAType leftArrayType = TypeComputeUtils.getActualTypeOrOpen(leftType, ATypeTag.ARRAY);
-        IAType rightArrayType = TypeComputeUtils.getActualTypeOrOpen(rightType, ATypeTag.ARRAY);
-        IAType leftItemType = ((AbstractCollectionType) leftArrayType).getItemType();
-        IAType rightItemType = ((AbstractCollectionType) rightArrayType).getItemType();
-        ATypeTag leftItemTag = leftItemType.getTypeTag();
-        ATypeTag rightItemTag = rightItemType.getTypeTag();
-        // TODO(ali): could be optimized to not need pointable when changing comparator to be non-tagged & no visitable
-        IPointable leftItem = voidPointableAllocator.allocate(null);
-        IPointable rightItem = voidPointableAllocator.allocate(null);
-        // TODO(ali): optimize to not need this storage, will require optimizing records comparison to not use visitable
-        ArrayBackedValueStorage leftStorage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
-        ArrayBackedValueStorage rightStorage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
+    private int compareArrays(IAType leftArrayType, TaggedValueReference leftArray, IAType rightArrayType,
+            TaggedValueReference rightArray) throws HyracksDataException {
+        int leftNumItems = SerializerDeserializerUtil.getNumberOfItemsNonTagged(leftArray);
+        int rightNumItems = SerializerDeserializerUtil.getNumberOfItemsNonTagged(rightArray);
+        IAType leftItemType =
+                ((AbstractCollectionType) TypeComputeUtils.getActualTypeOrOpen(leftArrayType, ATypeTag.ARRAY))
+                        .getItemType();
+        IAType rightItemType =
+                ((AbstractCollectionType) TypeComputeUtils.getActualTypeOrOpen(rightArrayType, ATypeTag.ARRAY))
+                        .getItemType();
+        ATypeTag leftArrayItemTag = leftItemType.getTypeTag();
+        ATypeTag rightArrayItemTag = rightItemType.getTypeTag();
+        TaggedValueReference leftItem = taggedValueAllocator.allocate(null);
+        TaggedValueReference rightItem = taggedValueAllocator.allocate(null);
+        boolean leftItemHasTag = leftArrayItemTag == ATypeTag.ANY;
+        boolean rightItemHasTag = rightArrayItemTag == ATypeTag.ANY;
         int result;
         try {
             for (int i = 0; i < leftNumItems && i < rightNumItems; i++) {
-                ListAccessorUtil.getItem(b1, s1, i, ATypeTag.ARRAY, leftItemTag, leftItem, leftStorage);
-                ListAccessorUtil.getItem(b2, s2, i, ATypeTag.ARRAY, rightItemTag, rightItem, rightStorage);
-                result = compare(leftItemType, leftItem.getByteArray(), leftItem.getStartOffset(), leftItem.getLength(),
-                        rightItemType, rightItem.getByteArray(), rightItem.getStartOffset(), rightItem.getLength());
+                ListAccessorUtil.getItemFromList(leftArray, i, leftItem, leftArrayItemTag, leftItemHasTag);
+                ListAccessorUtil.getItemFromList(rightArray, i, rightItem, rightArrayItemTag, rightItemHasTag);
+                result = compare(leftItemType, leftItem, rightItemType, rightItem);
                 if (result != 0) {
                     return result;
                 }
@@ -178,10 +180,8 @@
         } catch (IOException e) {
             throw HyracksDataException.create(e);
         } finally {
-            storageAllocator.free(rightStorage);
-            storageAllocator.free(leftStorage);
-            voidPointableAllocator.free(rightItem);
-            voidPointableAllocator.free(leftItem);
+            taggedValueAllocator.free(rightItem);
+            taggedValueAllocator.free(leftItem);
         }
     }
 
@@ -191,35 +191,25 @@
         ARecordType rightRecordType = (ARecordType) TypeComputeUtils.getActualTypeOrOpen(rightType, ATypeTag.OBJECT);
         SortedRecord leftRecord = recordPool.allocate(leftRecordType);
         SortedRecord rightRecord = recordPool.allocate(rightRecordType);
-        IPointable leftFieldValue = voidPointableAllocator.allocate(null);
-        IPointable rightFieldValue = voidPointableAllocator.allocate(null);
-        // TODO(ali): this is not ideal. should be removed when tagged pointables are introduced
-        ArrayBackedValueStorage leftStorage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
-        ArrayBackedValueStorage rightStorage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
+        TaggedValueReference leftFieldValue = taggedValueAllocator.allocate(null);
+        TaggedValueReference rightFieldValue = taggedValueAllocator.allocate(null);
         try {
-            leftRecord.reset(b1, s1);
-            rightRecord.reset(b2, s2);
-            IAType leftFieldType, rightFieldType;
-            RecordField leftField, rightField;
+            leftRecord.resetNonTagged(b1, s1);
+            rightRecord.resetNonTagged(b2, s2);
             int result;
             while (!leftRecord.isEmpty() && !rightRecord.isEmpty()) {
-                leftField = leftRecord.poll();
-                rightField = rightRecord.poll();
+                RecordField leftField = leftRecord.poll();
+                RecordField rightField = rightRecord.poll();
                 // compare the names first
                 result = RecordField.FIELD_NAME_COMP.compare(leftField, rightField);
                 if (result != 0) {
                     return result;
                 }
                 // then compare the values if the names are equal
-                leftStorage.reset();
-                rightStorage.reset();
-                leftRecord.getFieldValue(leftField, leftFieldValue, leftStorage);
-                rightRecord.getFieldValue(rightField, rightFieldValue, rightStorage);
-                leftFieldType = leftRecord.getFieldType(leftField);
-                rightFieldType = rightRecord.getFieldType(rightField);
-                result = compare(leftFieldType, leftFieldValue.getByteArray(), leftFieldValue.getStartOffset(),
-                        leftFieldValue.getLength(), rightFieldType, rightFieldValue.getByteArray(),
-                        rightFieldValue.getStartOffset(), rightFieldValue.getLength());
+                leftRecord.getFieldValue(leftField, leftFieldValue);
+                rightRecord.getFieldValue(rightField, rightFieldValue);
+                result = compare(leftRecord.getFieldType(leftField), leftFieldValue,
+                        rightRecord.getFieldType(rightField), rightFieldValue);
                 if (result != 0) {
                     return result;
                 }
@@ -230,10 +220,8 @@
         } finally {
             recordPool.free(rightRecord);
             recordPool.free(leftRecord);
-            voidPointableAllocator.free(rightFieldValue);
-            voidPointableAllocator.free(leftFieldValue);
-            storageAllocator.free(rightStorage);
-            storageAllocator.free(leftStorage);
+            taggedValueAllocator.free(rightFieldValue);
+            taggedValueAllocator.free(leftFieldValue);
         }
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/ComparatorUtil.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/ComparatorUtil.java
index 6f08b37..4db23b2 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/ComparatorUtil.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/ComparatorUtil.java
@@ -30,6 +30,7 @@
 
 import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator;
 import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator.Result;
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADoubleSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.AFloatSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.AInt16SerializerDeserializer;
@@ -47,7 +48,6 @@
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.IAType;
 import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
-import org.apache.hyracks.data.std.api.IPointable;
 
 // TODO(ali): refactor some functionality with ATypeHierarchy and others
 public class ComparatorUtil {
@@ -81,13 +81,7 @@
         return null;
     }
 
-    // checking that left and right are compatible and are numbers has to be done before calling this
-    static Result compareNumbers(ATypeTag leftTag, IPointable left, ATypeTag rightTag, IPointable right) {
-        return asResult(compareNumbers(leftTag, left.getByteArray(), left.getStartOffset() + 1, rightTag,
-                right.getByteArray(), right.getStartOffset() + 1));
-    }
-
-    // start args point to the value
+    // start points to the value; checking left and right are compatible and numbers has to be done before calling this
     static int compareNumbers(ATypeTag lTag, byte[] l, int lStart, ATypeTag rTag, byte[] r, int rStart) {
         if (lTag == DOUBLE || rTag == DOUBLE) {
             return Double.compare(getDoubleValue(lTag, l, lStart), getDoubleValue(rTag, r, rStart));
@@ -103,10 +97,11 @@
     }
 
     // checking that left and right are compatible has to be done before calling this
-    static Result compareNumWithConstant(ATypeTag leftTag, IPointable left, IAObject right) {
+    static Result compareNumWithConstant(TaggedValueReference left, IAObject right) {
+        ATypeTag leftTag = left.getTag();
         ATypeTag rightTag = right.getType().getTypeTag();
         byte[] leftBytes = left.getByteArray();
-        int start = left.getStartOffset() + 1;
+        int start = left.getStartOffset();
         if (leftTag == DOUBLE || rightTag == DOUBLE) {
             return asResult(Double.compare(getDoubleValue(leftTag, leftBytes, start), getConstantDouble(right)));
         } else if (leftTag == FLOAT || rightTag == FLOAT) {
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalComplexBinaryComparator.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalComplexBinaryComparator.java
index a4fce6d..93ccaa3 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalComplexBinaryComparator.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalComplexBinaryComparator.java
@@ -18,38 +18,27 @@
  */
 package org.apache.asterix.dataflow.data.nontagged.comparators;
 
-import static org.apache.asterix.om.types.ATypeTag.SERIALIZED_MISSING_TYPE_TAG;
-import static org.apache.asterix.om.types.ATypeTag.VALUE_TYPE_MAPPING;
-import static org.apache.asterix.om.util.container.ObjectFactories.BIT_SET_FACTORY;
-import static org.apache.asterix.om.util.container.ObjectFactories.STORAGE_FACTORY;
-import static org.apache.asterix.om.util.container.ObjectFactories.VOID_FACTORY;
+import static org.apache.asterix.om.util.container.ObjectFactories.RECORD_FACTORY;
+import static org.apache.asterix.om.util.container.ObjectFactories.VALUE_FACTORY;
 
 import java.io.IOException;
-import java.util.BitSet;
-import java.util.List;
 
 import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator;
 import org.apache.asterix.dataflow.data.common.ListAccessorUtil;
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
+import org.apache.asterix.dataflow.data.nontagged.serde.SerializerDeserializerUtil;
 import org.apache.asterix.om.base.IAObject;
-import org.apache.asterix.om.pointables.ARecordVisitablePointable;
-import org.apache.asterix.om.pointables.PointableAllocator;
-import org.apache.asterix.om.pointables.base.IVisitablePointable;
+import org.apache.asterix.om.pointables.nonvisitor.RecordField;
+import org.apache.asterix.om.pointables.nonvisitor.SortedRecord;
 import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.AbstractCollectionType;
-import org.apache.asterix.om.types.EnumDeserializer;
 import org.apache.asterix.om.types.IAType;
-import org.apache.asterix.om.util.container.IObjectPool;
 import org.apache.asterix.om.util.container.ListObjectPool;
 import org.apache.asterix.om.utils.RecordUtil;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.accessors.RawBinaryComparatorFactory;
-import org.apache.hyracks.data.std.api.IMutableValueStorage;
-import org.apache.hyracks.data.std.api.IPointable;
-import org.apache.hyracks.data.std.api.IValueReference;
-import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
-import org.apache.hyracks.util.string.UTF8StringUtil;
 
 public final class LogicalComplexBinaryComparator implements ILogicalBinaryComparator {
 
@@ -57,26 +46,20 @@
     private final IAType rightType;
     private final boolean isEquality;
     private final LogicalScalarBinaryComparator scalarComparator;
-    private final IObjectPool<IMutableValueStorage, Void> storageAllocator;
-    private final IObjectPool<IPointable, Void> voidPointableAllocator;
-    private final IObjectPool<BitSet, Void> bitSetAllocator;
-    private final PointableAllocator pointableAllocator;
+    private final ListObjectPool<TaggedValueReference, Void> taggedValuePool = new ListObjectPool<>(VALUE_FACTORY);
+    private final ListObjectPool<SortedRecord, ARecordType> recordPool = new ListObjectPool<>(RECORD_FACTORY);
 
     LogicalComplexBinaryComparator(IAType leftType, IAType rightType, boolean isEquality) {
         this.leftType = leftType;
         this.rightType = rightType;
         this.isEquality = isEquality;
         this.scalarComparator = LogicalScalarBinaryComparator.of(isEquality);
-        storageAllocator = new ListObjectPool<>(STORAGE_FACTORY);
-        voidPointableAllocator = new ListObjectPool<>(VOID_FACTORY);
-        bitSetAllocator = new ListObjectPool<>(BIT_SET_FACTORY);
-        pointableAllocator = new PointableAllocator();
     }
 
     @Override
-    public Result compare(IPointable left, IPointable right) throws HyracksDataException {
-        ATypeTag leftRuntimeTag = VALUE_TYPE_MAPPING[left.getByteArray()[left.getStartOffset()]];
-        ATypeTag rightRuntimeTag = VALUE_TYPE_MAPPING[right.getByteArray()[right.getStartOffset()]];
+    public Result compare(TaggedValueReference left, TaggedValueReference right) throws HyracksDataException {
+        ATypeTag leftRuntimeTag = left.getTag();
+        ATypeTag rightRuntimeTag = right.getTag();
         Result comparisonResult = ComparatorUtil.returnMissingOrNullOrMismatch(leftRuntimeTag, rightRuntimeTag);
         if (comparisonResult != null) {
             return comparisonResult;
@@ -89,9 +72,9 @@
     }
 
     @Override
-    public Result compare(IPointable left, IAObject rightConstant) {
+    public Result compare(TaggedValueReference left, IAObject rightConstant) {
         // TODO(ali): not defined currently for constant complex types
-        ATypeTag leftTag = VALUE_TYPE_MAPPING[left.getByteArray()[left.getStartOffset()]];
+        ATypeTag leftTag = left.getTag();
         ATypeTag rightTag = rightConstant.getType().getTypeTag();
         Result comparisonResult = ComparatorUtil.returnMissingOrNullOrMismatch(leftTag, rightTag);
         if (comparisonResult != null) {
@@ -102,7 +85,7 @@
     }
 
     @Override
-    public Result compare(IAObject leftConstant, IPointable right) {
+    public Result compare(IAObject leftConstant, TaggedValueReference right) {
         // TODO(ali): not defined currently for constant complex types
         Result result = compare(right, leftConstant);
         switch (result) {
@@ -128,18 +111,19 @@
         return Result.NULL;
     }
 
-    private Result compareComplex(IAType leftType, ATypeTag leftTag, IPointable left, IAType rightType,
-            ATypeTag rightTag, IPointable right) throws HyracksDataException {
-        if (leftTag != rightTag) {
+    private Result compareComplex(IAType leftType, ATypeTag leftRuntimeTag, TaggedValueReference left, IAType rightType,
+            ATypeTag rightRuntimeTag, TaggedValueReference right) throws HyracksDataException {
+        if (leftRuntimeTag != rightRuntimeTag) {
             return Result.INCOMPARABLE;
         }
-        IAType leftCompileType = TypeComputeUtils.getActualTypeOrOpen(leftType, leftTag);
-        IAType rightCompileType = TypeComputeUtils.getActualTypeOrOpen(rightType, rightTag);
-        switch (leftTag) {
+        IAType leftCompileType = TypeComputeUtils.getActualTypeOrOpen(leftType, leftRuntimeTag);
+        IAType rightCompileType = TypeComputeUtils.getActualTypeOrOpen(rightType, rightRuntimeTag);
+        switch (leftRuntimeTag) {
             case MULTISET:
-                return compareMultisets(leftCompileType, leftTag, left, rightCompileType, rightTag, right);
+                return compareMultisets(leftCompileType, leftRuntimeTag, left, rightCompileType, rightRuntimeTag,
+                        right);
             case ARRAY:
-                return compareArrays(leftCompileType, leftTag, left, rightCompileType, rightTag, right);
+                return compareArrays(leftCompileType, left, rightCompileType, right);
             case OBJECT:
                 return compareRecords(leftCompileType, left, rightCompileType, right);
             default:
@@ -147,41 +131,28 @@
         }
     }
 
-    private Result compareArrays(IAType leftType, ATypeTag leftListTag, IPointable left, IAType rightType,
-            ATypeTag rightListTag, IPointable right) throws HyracksDataException {
-        // reaching here, both left and right have to be arrays (should be enforced)
-        byte[] leftBytes = left.getByteArray();
-        byte[] rightBytes = right.getByteArray();
-        int leftStart = left.getStartOffset();
-        int rightStart = right.getStartOffset();
-        int leftNumItems = ListAccessorUtil.numberOfItems(leftBytes, leftStart);
-        int rightNumItems = ListAccessorUtil.numberOfItems(rightBytes, rightStart);
+    private Result compareArrays(IAType leftType, TaggedValueReference leftArray, IAType rightType,
+            TaggedValueReference rightArray) throws HyracksDataException {
+        // reaching here, both leftArray and rightArray have to be arrays (should be enforced)
+        int leftNumItems = SerializerDeserializerUtil.getNumberOfItemsNonTagged(leftArray);
+        int rightNumItems = SerializerDeserializerUtil.getNumberOfItemsNonTagged(rightArray);
         IAType leftItemCompileType = ((AbstractCollectionType) leftType).getItemType();
         IAType rightItemCompileType = ((AbstractCollectionType) rightType).getItemType();
-        ATypeTag leftItemTag = leftItemCompileType.getTypeTag();
-        ATypeTag rightItemTag = rightItemCompileType.getTypeTag();
-
-        // TODO(ali): could be optimized to not need pointable when changing comparator to be non-tagged & no visitable
-        IPointable leftItem = voidPointableAllocator.allocate(null);
-        IPointable rightItem = voidPointableAllocator.allocate(null);
-        // TODO(ali): optimize to not need this storage, will require optimizing records comparison to not use visitable
-        ArrayBackedValueStorage leftStorage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
-        ArrayBackedValueStorage rightStorage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
+        ATypeTag leftArrayItemTag = leftItemCompileType.getTypeTag();
+        ATypeTag rightArrayItemTag = rightItemCompileType.getTypeTag();
+        boolean leftItemHasTag = leftArrayItemTag == ATypeTag.ANY;
+        boolean rightItemHasTag = rightArrayItemTag == ATypeTag.ANY;
+        TaggedValueReference leftItem = taggedValuePool.allocate(null);
+        TaggedValueReference rightItem = taggedValuePool.allocate(null);
         Result determiningResult = null;
         Result tempResult;
-        byte leftItemTagByte, rightItemTagByte;
-        ATypeTag leftItemRuntimeTag, rightItemRuntimeTag;
         try {
             for (int i = 0; i < leftNumItems && i < rightNumItems; i++) {
-                ListAccessorUtil.getItem(leftBytes, leftStart, i, leftListTag, leftItemTag, leftItem, leftStorage);
-                ListAccessorUtil.getItem(rightBytes, rightStart, i, rightListTag, rightItemTag, rightItem,
-                        rightStorage);
-                leftItemTagByte = leftItem.getByteArray()[leftItem.getStartOffset()];
-                rightItemTagByte = rightItem.getByteArray()[rightItem.getStartOffset()];
-
+                ListAccessorUtil.getItemFromList(leftArray, i, leftItem, leftArrayItemTag, leftItemHasTag);
+                ListAccessorUtil.getItemFromList(rightArray, i, rightItem, rightArrayItemTag, rightItemHasTag);
                 // if both tags are derived, get item type or default to open item if array is open, then call complex
-                leftItemRuntimeTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(leftItemTagByte);
-                rightItemRuntimeTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(rightItemTagByte);
+                ATypeTag leftItemRuntimeTag = leftItem.getTag();
+                ATypeTag rightItemRuntimeTag = rightItem.getTag();
                 if (leftItemRuntimeTag.isDerivedType() && rightItemRuntimeTag.isDerivedType()) {
                     tempResult = compareComplex(leftItemCompileType, leftItemRuntimeTag, leftItem, rightItemCompileType,
                             rightItemRuntimeTag, rightItem);
@@ -206,15 +177,13 @@
         } catch (IOException e) {
             throw HyracksDataException.create(e);
         } finally {
-            storageAllocator.free(rightStorage);
-            storageAllocator.free(leftStorage);
-            voidPointableAllocator.free(rightItem);
-            voidPointableAllocator.free(leftItem);
+            taggedValuePool.free(rightItem);
+            taggedValuePool.free(leftItem);
         }
     }
 
-    private Result compareMultisets(IAType leftType, ATypeTag leftListTag, IPointable left, IAType rightType,
-            ATypeTag rightListTag, IPointable right) throws HyracksDataException {
+    private Result compareMultisets(IAType leftType, ATypeTag leftListTag, TaggedValueReference left, IAType rightType,
+            ATypeTag rightListTag, TaggedValueReference right) throws HyracksDataException {
         // TODO(ali): multiset comparison logic here
         // equality is the only operation defined for multiset
         if (!isEquality) {
@@ -225,107 +194,78 @@
                         left.getLength(), right.getByteArray(), right.getStartOffset(), right.getLength()));
     }
 
-    private Result compareRecords(IAType leftType, IPointable left, IAType rightType, IPointable right)
-            throws HyracksDataException {
+    private Result compareRecords(IAType leftType, TaggedValueReference left, IAType rightType,
+            TaggedValueReference right) throws HyracksDataException {
         // equality is the only operation defined for records
         if (!isEquality) {
             return Result.INCOMPARABLE;
         }
-        ARecordType leftRecordType = (ARecordType) leftType;
-        ARecordType rightRecordType = (ARecordType) rightType;
-        ARecordVisitablePointable leftRecord = pointableAllocator.allocateRecordValue(leftRecordType);
-        ARecordVisitablePointable rightRecord = pointableAllocator.allocateRecordValue(rightRecordType);
-        // keeps track of the fields in the right record that have not been matched
-        BitSet notMatched = bitSetAllocator.allocate(null);
+        ARecordType leftRecordType = (ARecordType) TypeComputeUtils.getActualTypeOrOpen(leftType, ATypeTag.OBJECT);
+        ARecordType rightRecordType = (ARecordType) TypeComputeUtils.getActualTypeOrOpen(rightType, ATypeTag.OBJECT);
+        SortedRecord leftRecord = recordPool.allocate(leftRecordType);
+        SortedRecord rightRecord = recordPool.allocate(rightRecordType);
+        TaggedValueReference leftFieldValue = taggedValuePool.allocate(null);
+        TaggedValueReference rightFieldValue = taggedValuePool.allocate(null);
         try {
-            leftRecord.set(left);
-            rightRecord.set(right);
-            List<IVisitablePointable> leftFieldValues = leftRecord.getFieldValues();
-            List<IVisitablePointable> leftFieldNames = leftRecord.getFieldNames();
-            List<IVisitablePointable> rightFieldValues = rightRecord.getFieldValues();
-            List<IVisitablePointable> rightFieldNames = rightRecord.getFieldNames();
-            IVisitablePointable leftFieldValue, leftFieldName, rightFieldValue, rightFieldName;
-            int leftNumFields = leftFieldNames.size();
-            int rightNumFields = rightFieldNames.size();
-            IAType leftFieldType, rightFieldType;
-            ATypeTag leftFTag, rightFTag;
-            Result tempCompResult;
-            boolean foundFieldInRight;
+            leftRecord.resetNonTagged(left.getByteArray(), left.getStartOffset());
+            rightRecord.resetNonTagged(right.getByteArray(), right.getStartOffset());
             boolean notEqual = false;
-            notMatched.set(0, rightNumFields);
-            for (int i = 0; i < leftNumFields; i++) {
-                leftFieldValue = leftFieldValues.get(i);
-                leftFTag = VALUE_TYPE_MAPPING[leftFieldValue.getByteArray()[leftFieldValue.getStartOffset()]];
-
-                // ignore if the field value is missing
-                if (leftFTag != ATypeTag.MISSING) {
-                    // start looking for the field in the right record
-                    foundFieldInRight = false;
-                    leftFieldName = leftFieldNames.get(i);
-                    for (int k = 0; k < rightNumFields; k++) {
-                        rightFieldName = rightFieldNames.get(k);
-                        if (notMatched.get(k) && equalNames(leftFieldName, rightFieldName)) {
-                            notMatched.clear(k);
-                            rightFieldValue = rightFieldValues.get(k);
-                            rightFTag = VALUE_TYPE_MAPPING[rightFieldValue.getByteArray()[rightFieldValue
-                                    .getStartOffset()]];
-                            // if right field has a missing value, ignore and flag the two records as not equal
-                            if (rightFTag != ATypeTag.MISSING) {
-                                foundFieldInRight = true;
-                                if (leftFTag == ATypeTag.NULL || rightFTag == ATypeTag.NULL) {
-                                    tempCompResult = Result.NULL;
-                                } else if (leftFTag.isDerivedType() && rightFTag.isDerivedType()) {
-                                    leftFieldType = RecordUtil.getType(leftRecordType, i, leftFTag);
-                                    rightFieldType = RecordUtil.getType(rightRecordType, k, rightFTag);
-                                    tempCompResult = compareComplex(leftFieldType, leftFTag, leftFieldValue,
-                                            rightFieldType, rightFTag, rightFieldValue);
-                                } else {
-                                    tempCompResult = scalarComparator.compare(leftFieldValue, rightFieldValue);
-                                }
-
-                                if (tempCompResult == Result.INCOMPARABLE || tempCompResult == Result.MISSING
-                                        || tempCompResult == Result.NULL) {
-                                    return Result.INCOMPARABLE;
-                                }
-                                if (tempCompResult != Result.EQ) {
-                                    notEqual = true;
-                                }
-                            }
-                            break;
-                        }
+            RecordField leftField = null, rightField = null;
+            int previousNamesComparisonResult = 0;
+            while (!leftRecord.isEmpty() && !rightRecord.isEmpty()) {
+                if (previousNamesComparisonResult == 0) {
+                    // previous field names were equal or first time to enter the loop
+                    leftField = leftRecord.poll();
+                    rightField = rightRecord.poll();
+                } else if (previousNamesComparisonResult > 0) {
+                    // right field name was less than left field name. get next field from right
+                    rightField = rightRecord.poll();
+                } else {
+                    leftField = leftRecord.poll();
+                }
+                Result tempCompResult;
+                previousNamesComparisonResult = RecordField.FIELD_NAME_COMP.compare(leftField, rightField);
+                if (previousNamesComparisonResult == 0) {
+                    // filed names are equal
+                    leftRecord.getFieldValue(leftField, leftFieldValue);
+                    rightRecord.getFieldValue(rightField, rightFieldValue);
+                    ATypeTag leftFTag = leftFieldValue.getTag();
+                    ATypeTag rightFTag = rightFieldValue.getTag();
+                    if (leftFTag == ATypeTag.NULL || rightFTag == ATypeTag.NULL) {
+                        tempCompResult = Result.NULL;
+                    } else if (leftFTag.isDerivedType() && rightFTag.isDerivedType()) {
+                        IAType leftFieldType = RecordUtil.getType(leftRecordType, leftField.getIndex(), leftFTag);
+                        IAType rightFieldType = RecordUtil.getType(rightRecordType, rightField.getIndex(), rightFTag);
+                        tempCompResult = compareComplex(leftFieldType, leftFTag, leftFieldValue, rightFieldType,
+                                rightFTag, rightFieldValue);
+                    } else {
+                        tempCompResult = scalarComparator.compare(leftFieldValue, rightFieldValue);
                     }
-                    if (!foundFieldInRight) {
+
+                    if (tempCompResult == Result.INCOMPARABLE || tempCompResult == Result.MISSING
+                            || tempCompResult == Result.NULL) {
+                        return Result.INCOMPARABLE;
+                    }
+                    if (tempCompResult != Result.EQ) {
                         notEqual = true;
                     }
+                } else {
+                    notEqual = true;
                 }
             }
-
-            if (notEqual) {
+            if (notEqual || leftRecord.size() != rightRecord.size()) {
                 // LT or GT does not make a difference since this is an answer to equality
                 return Result.LT;
             }
-            // check if there is a field in the right record that does not exist in left record
-            byte rightFieldTag;
-            for (int i = notMatched.nextSetBit(0); i >= 0 && i < rightNumFields; i = notMatched.nextSetBit(i + 1)) {
-                rightFieldValue = rightFieldValues.get(i);
-                rightFieldTag = rightFieldValue.getByteArray()[rightFieldValue.getStartOffset()];
-                if (rightFieldTag != SERIALIZED_MISSING_TYPE_TAG) {
-                    // LT or GT does not make a difference since this is an answer to equality
-                    return Result.LT;
-                }
-            }
-
             // reaching here means every field in the left record exists in the right and vice versa
             return Result.EQ;
+        } catch (IOException e) {
+            throw HyracksDataException.create(e);
         } finally {
-            pointableAllocator.freeRecord(rightRecord);
-            pointableAllocator.freeRecord(leftRecord);
-            bitSetAllocator.free(notMatched);
+            recordPool.free(rightRecord);
+            recordPool.free(leftRecord);
+            taggedValuePool.free(rightFieldValue);
+            taggedValuePool.free(leftFieldValue);
         }
     }
-
-    private boolean equalNames(IValueReference fieldName1, IValueReference fieldName2) {
-        return UTF8StringUtil.compareTo(fieldName1.getByteArray(), fieldName1.getStartOffset() + 1,
-                fieldName2.getByteArray(), fieldName2.getStartOffset() + 1) == 0;
-    }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalGenericBinaryComparator.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalGenericBinaryComparator.java
index 57dbb33..d3c4183 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalGenericBinaryComparator.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalGenericBinaryComparator.java
@@ -18,14 +18,11 @@
  */
 package org.apache.asterix.dataflow.data.nontagged.comparators;
 
-import static org.apache.asterix.om.types.ATypeTag.VALUE_TYPE_MAPPING;
-
 import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator;
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.om.base.IAObject;
-import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.IAType;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.data.std.api.IPointable;
 
 public final class LogicalGenericBinaryComparator implements ILogicalBinaryComparator {
 
@@ -38,27 +35,23 @@
     }
 
     @Override
-    public Result compare(IPointable left, IPointable right) throws HyracksDataException {
-        ATypeTag leftTag = VALUE_TYPE_MAPPING[left.getByteArray()[left.getStartOffset()]];
-        ATypeTag rightTag = VALUE_TYPE_MAPPING[right.getByteArray()[right.getStartOffset()]];
-        if (leftTag.isDerivedType() && rightTag.isDerivedType()) {
+    public Result compare(TaggedValueReference left, TaggedValueReference right) throws HyracksDataException {
+        if (left.getTag().isDerivedType() && right.getTag().isDerivedType()) {
             return complexComparator.compare(left, right);
         }
         return scalarComparator.compare(left, right);
     }
 
     @Override
-    public Result compare(IPointable left, IAObject rightConstant) {
-        ATypeTag leftTag = VALUE_TYPE_MAPPING[left.getByteArray()[left.getStartOffset()]];
-        ATypeTag rightTag = rightConstant.getType().getTypeTag();
-        if (leftTag.isDerivedType() && rightTag.isDerivedType()) {
+    public Result compare(TaggedValueReference left, IAObject rightConstant) {
+        if (left.getTag().isDerivedType() && rightConstant.getType().getTypeTag().isDerivedType()) {
             return complexComparator.compare(left, rightConstant);
         }
         return scalarComparator.compare(left, rightConstant);
     }
 
     @Override
-    public Result compare(IAObject leftConstant, IPointable right) {
+    public Result compare(IAObject leftConstant, TaggedValueReference right) {
         Result result = compare(right, leftConstant);
         if (result == Result.LT) {
             return Result.GT;
@@ -69,12 +62,10 @@
     }
 
     @Override
-    public Result compare(IAObject leftConstant, IAObject rightConstant) {
-        ATypeTag leftTag = leftConstant.getType().getTypeTag();
-        ATypeTag rightTag = rightConstant.getType().getTypeTag();
-        if (leftTag.isDerivedType() && rightTag.isDerivedType()) {
-            return complexComparator.compare(leftConstant, rightConstant);
+    public Result compare(IAObject leftConst, IAObject rightConst) {
+        if (leftConst.getType().getTypeTag().isDerivedType() && rightConst.getType().getTypeTag().isDerivedType()) {
+            return complexComparator.compare(leftConst, rightConst);
         }
-        return scalarComparator.compare(leftConstant, rightConstant);
+        return scalarComparator.compare(leftConst, rightConst);
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalScalarBinaryComparator.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalScalarBinaryComparator.java
index f2f637a..c2ff8d7 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalScalarBinaryComparator.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/comparators/LogicalScalarBinaryComparator.java
@@ -19,9 +19,9 @@
 package org.apache.asterix.dataflow.data.nontagged.comparators;
 
 import static org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator.inequalityUndefined;
-import static org.apache.asterix.om.types.ATypeTag.VALUE_TYPE_MAPPING;
 
 import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator;
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADateSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADateTimeSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADayTimeDurationSerializerDeserializer;
@@ -31,7 +31,6 @@
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.data.std.api.IPointable;
 import org.apache.hyracks.data.std.primitive.BooleanPointable;
 import org.apache.hyracks.data.std.primitive.ByteArrayPointable;
 import org.apache.hyracks.data.std.primitive.UTF8StringPointable;
@@ -51,9 +50,9 @@
     }
 
     @Override
-    public Result compare(IPointable left, IPointable right) throws HyracksDataException {
-        ATypeTag leftTag = VALUE_TYPE_MAPPING[left.getByteArray()[left.getStartOffset()]];
-        ATypeTag rightTag = VALUE_TYPE_MAPPING[right.getByteArray()[right.getStartOffset()]];
+    public Result compare(TaggedValueReference left, TaggedValueReference right) throws HyracksDataException {
+        ATypeTag leftTag = left.getTag();
+        ATypeTag rightTag = right.getTag();
         Result comparisonResult = ComparatorUtil.returnMissingOrNullOrMismatch(leftTag, rightTag);
         if (comparisonResult != null) {
             return comparisonResult;
@@ -61,9 +60,14 @@
         if (comparisonUndefined(leftTag, rightTag, isEquality)) {
             return Result.INCOMPARABLE;
         }
+        byte[] leftBytes = left.getByteArray();
+        byte[] rightBytes = right.getByteArray();
+        int leftStart = left.getStartOffset();
+        int rightStart = right.getStartOffset();
         // compare number if one of args is number since compatibility has already been checked above
         if (ATypeHierarchy.getTypeDomain(leftTag) == ATypeHierarchy.Domain.NUMERIC) {
-            return ComparatorUtil.compareNumbers(leftTag, left, rightTag, right);
+            return ILogicalBinaryComparator.asResult(
+                    ComparatorUtil.compareNumbers(leftTag, leftBytes, leftStart, rightTag, rightBytes, rightStart));
         }
 
         // comparing non-numeric
@@ -72,13 +76,8 @@
             throw new IllegalStateException("Two different non-numeric tags but they are compatible");
         }
 
-        byte[] leftBytes = left.getByteArray();
-        byte[] rightBytes = right.getByteArray();
-        int leftStart = left.getStartOffset() + 1;
-        int rightStart = right.getStartOffset() + 1;
-        int leftLen = left.getLength() - 1;
-        int rightLen = right.getLength() - 1;
-
+        int leftLen = left.getLength();
+        int rightLen = right.getLength();
         int result;
         switch (leftTag) {
             case BOOLEAN:
@@ -153,9 +152,9 @@
     }
 
     @Override
-    public Result compare(IPointable left, IAObject rightConstant) {
+    public Result compare(TaggedValueReference left, IAObject rightConstant) {
         // TODO(ali): currently defined for numbers only
-        ATypeTag leftTag = VALUE_TYPE_MAPPING[left.getByteArray()[left.getStartOffset()]];
+        ATypeTag leftTag = left.getTag();
         ATypeTag rightTag = rightConstant.getType().getTypeTag();
         Result comparisonResult = ComparatorUtil.returnMissingOrNullOrMismatch(leftTag, rightTag);
         if (comparisonResult != null) {
@@ -165,13 +164,13 @@
             return Result.NULL;
         }
         if (ATypeHierarchy.getTypeDomain(leftTag) == ATypeHierarchy.Domain.NUMERIC) {
-            return ComparatorUtil.compareNumWithConstant(leftTag, left, rightConstant);
+            return ComparatorUtil.compareNumWithConstant(left, rightConstant);
         }
         return Result.NULL;
     }
 
     @Override
-    public Result compare(IAObject leftConstant, IPointable right) {
+    public Result compare(IAObject leftConstant, TaggedValueReference right) {
         // TODO(ali): currently defined for numbers only
         Result result = compare(right, leftConstant);
         if (result == Result.LT) {
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/hash/AMurmurHash3BinaryHashFunctionFamily.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/hash/AMurmurHash3BinaryHashFunctionFamily.java
index f02c764..caab273 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/hash/AMurmurHash3BinaryHashFunctionFamily.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/hash/AMurmurHash3BinaryHashFunctionFamily.java
@@ -26,6 +26,7 @@
 import java.io.IOException;
 
 import org.apache.asterix.dataflow.data.common.ListAccessorUtil;
+import org.apache.asterix.dataflow.data.nontagged.serde.AOrderedListSerializerDeserializer;
 import org.apache.asterix.om.pointables.nonvisitor.RecordField;
 import org.apache.asterix.om.pointables.nonvisitor.SortedRecord;
 import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils;
@@ -138,7 +139,7 @@
             IAType arrayType = TypeComputeUtils.getActualTypeOrOpen(type, ATypeTag.ARRAY);
             IAType itemType = ((AbstractCollectionType) arrayType).getItemType();
             ATypeTag itemTag = itemType.getTypeTag();
-            int numItems = ListAccessorUtil.numberOfItems(bytes, offset);
+            int numItems = AOrderedListSerializerDeserializer.getNumberOfItems(bytes, offset);
             int hash = seed;
             IPointable item = voidPointableAllocator.allocate(null);
             ArrayBackedValueStorage storage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/AOrderedListSerializerDeserializer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/AOrderedListSerializerDeserializer.java
index 9307627..bffc3b2 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/AOrderedListSerializerDeserializer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/AOrderedListSerializerDeserializer.java
@@ -116,32 +116,16 @@
         listBuilder.write(out, false);
     }
 
-    public static final int getOrderedListLength(byte[] serOrderedList, int offset) {
+    public static int getOrderedListLength(byte[] serOrderedList, int offset) {
         return AInt32SerializerDeserializer.getInt(serOrderedList, offset + 1);
     }
 
     public static int getNumberOfItems(byte[] serOrderedList, int offset) {
-        if (serOrderedList[offset] == ATypeTag.ARRAY.serialize()) {
-            // 6 = tag (1) + itemTag (1) + list size (4)
-            return AInt32SerializerDeserializer.getInt(serOrderedList, offset + 6);
-        } else {
-            return -1;
-        }
+        // 6 = tag (1) + itemTag (1) + list size (4)
+        return AInt32SerializerDeserializer.getInt(serOrderedList, offset + 6);
     }
 
     public static int getItemOffset(byte[] serOrderedList, int offset, int itemIndex) throws HyracksDataException {
-        if (serOrderedList[offset] == ATypeTag.ARRAY.serialize()) {
-            ATypeTag typeTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(serOrderedList[offset + 1]);
-            if (NonTaggedFormatUtil.isFixedSizedCollection(typeTag)) {
-                int length = NonTaggedFormatUtil.getFieldValueLength(serOrderedList, offset + 1, typeTag, true);
-                return offset + 10 + (length * itemIndex);
-            } else {
-                return offset + AInt32SerializerDeserializer.getInt(serOrderedList, offset + 10 + (4 * itemIndex));
-            }
-            // 10 = tag (1) + itemTag (1) + list size (4) + number of items (4)
-        } else {
-            return -1;
-        }
+        return SerializerDeserializerUtil.getItemOffset(serOrderedList, offset, itemIndex);
     }
-
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/AUnorderedListSerializerDeserializer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/AUnorderedListSerializerDeserializer.java
index 751146a..b5165d2 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/AUnorderedListSerializerDeserializer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/AUnorderedListSerializerDeserializer.java
@@ -118,31 +118,16 @@
         listBuilder.write(out, false);
     }
 
-    public static final int getUnorderedListLength(byte[] serOrderedList, int offset) {
+    public static int getUnorderedListLength(byte[] serOrderedList, int offset) {
         return AInt32SerializerDeserializer.getInt(serOrderedList, offset + 1);
     }
 
     public static int getNumberOfItems(byte[] serOrderedList, int offset) {
-        if (serOrderedList[offset] == ATypeTag.MULTISET.serialize()) {
-            // 6 = tag (1) + itemTag (1) + list size (4)
-            return AInt32SerializerDeserializer.getInt(serOrderedList, offset + 6);
-        } else {
-            return -1;
-        }
+        // 6 = tag (1) + itemTag (1) + list size (4)
+        return AInt32SerializerDeserializer.getInt(serOrderedList, offset + 6);
     }
 
     public static int getItemOffset(byte[] serOrderedList, int offset, int itemIndex) throws HyracksDataException {
-        if (serOrderedList[offset] == ATypeTag.MULTISET.serialize()) {
-            ATypeTag typeTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(serOrderedList[offset + 1]);
-            if (NonTaggedFormatUtil.isFixedSizedCollection(typeTag)) {
-                int length = NonTaggedFormatUtil.getFieldValueLength(serOrderedList, offset + 1, typeTag, true);
-                return offset + 10 + (length * itemIndex);
-            } else {
-                return offset + AInt32SerializerDeserializer.getInt(serOrderedList, offset + 10 + (4 * itemIndex));
-            }
-            // 10 = tag (1) + itemTag (1) + list size (4) + number of items (4)
-        } else {
-            return -1;
-        }
+        return SerializerDeserializerUtil.getItemOffset(serOrderedList, offset, itemIndex);
     }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/SerializerDeserializerUtil.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/SerializerDeserializerUtil.java
index 4ea2c13..952ffb4 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/SerializerDeserializerUtil.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/dataflow/data/nontagged/serde/SerializerDeserializerUtil.java
@@ -24,11 +24,13 @@
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.om.base.AInt32;
 import org.apache.asterix.om.base.IAObject;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.EnumDeserializer;
 import org.apache.asterix.om.types.IAType;
+import org.apache.asterix.om.utils.NonTaggedFormatUtil;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
@@ -98,4 +100,33 @@
         return castBuffer.getByteArray();
     }
 
+    public static int getNumberOfItemsNonTagged(TaggedValueReference list) {
+        // 5 = itemTag (1) + list size (4)
+        return AInt32SerializerDeserializer.getInt(list.getByteArray(), list.getStartOffset() + 5);
+    }
+
+    public static int getItemOffset(byte[] listBytes, int offset, int itemIndex) throws HyracksDataException {
+        ATypeTag itemTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(listBytes[offset + 1]);
+        // 10 = tag (1) + itemTag (1) + list size (4) + number of items (4)
+        if (NonTaggedFormatUtil.isFixedSizedCollection(itemTag)) {
+            int valueLength = NonTaggedFormatUtil.getFieldValueLength(listBytes, offset + 1, itemTag, true);
+            return offset + 10 + (valueLength * itemIndex);
+        } else {
+            return offset + AInt32SerializerDeserializer.getInt(listBytes, offset + 10 + (4 * itemIndex));
+        }
+    }
+
+    public static int getItemOffsetNonTagged(TaggedValueReference list, int itemIndex) throws HyracksDataException {
+        byte[] listValueBytes = list.getByteArray();
+        int offset = list.getStartOffset();
+        ATypeTag itemTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(listValueBytes[offset]);
+        // 9 = itemTag (1) + list size (4) + number of items (4)
+        if (NonTaggedFormatUtil.isFixedSizedCollection(itemTag)) {
+            int valueLength = NonTaggedFormatUtil.getFieldValueLength(listValueBytes, offset, itemTag, true);
+            return offset + 9 + (valueLength * itemIndex);
+        } else {
+            // the -1 is due to the fact that the item encoded offset is measured from a tagged list
+            return offset + AInt32SerializerDeserializer.getInt(listValueBytes, offset + 9 + (4 * itemIndex)) - 1;
+        }
+    }
 }
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/ARecordPointable.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/ARecordPointable.java
index 928c71a..af8d5e6 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/ARecordPointable.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/ARecordPointable.java
@@ -53,13 +53,13 @@
  *   int length;
  *   byte isExpanded?;
  *   int openOffset?;
- *   int numberOfClosedFields;
- *   byte[ceil (numberOfFields / 4)] nullBitMap; // 2 bits per field, "1" means field is null, "2" field is missing
- *   int[numberOfClosedFields] closedFieldOffset;
- *   IPointable[numberOfClosedFields] fieldValue;
+ *   int numberOfClosedFields?;
+ *   byte[ceil (numberOfFields / 4)] nullBitMap?; // 2 bits per field, "1" means field is null, "2" field is missing
+ *   int[numberOfClosedFields] closedFieldOffset?;
+ *   IPointable[numberOfClosedFields] fieldValues?;
  *   int numberOfOpenFields?;
- *   OpenFieldLookup[numberOfOpenFields] lookup;
- *   OpenField[numberOfOpenFields] openFields;
+ *   OpenFieldLookup[numberOfOpenFields] lookup?;
+ *   OpenField[numberOfOpenFields] openFields?;
  * }
  *
  * OpenFieldLookup {
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/RecordField.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/RecordField.java
index 0cd16dc..3c33f49 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/RecordField.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/RecordField.java
@@ -59,7 +59,7 @@
         return namePointable;
     }
 
-    final int getIndex() {
+    public final int getIndex() {
         return index;
     }
 
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/SortedRecord.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/SortedRecord.java
index 1d9ac6c..be81a70 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/SortedRecord.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/pointables/nonvisitor/SortedRecord.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import java.util.PriorityQueue;
 
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.dataflow.data.nontagged.serde.AInt32SerializerDeserializer;
 import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils;
 import org.apache.asterix.om.types.ARecordType;
@@ -97,48 +98,58 @@
      * closed and open. It populates the utf8 filed names for open.
      */
     public final void reset(byte[] data, int start) throws HyracksDataException {
+        resetRecord(data, start, ARecordPointable.TAG_SIZE, 0);
+    }
+
+    public final void resetNonTagged(byte[] data, int start) throws HyracksDataException {
+        resetRecord(data, start, 0, 1);
+    }
+
+    private void resetRecord(byte[] data, int start, int skipTag, int fixOffset) throws HyracksDataException {
         bytes = data;
         reset();
         boolean isExpanded = false;
         // advance to expanded byte if present
-        int cursor = start + ARecordPointable.TAG_SIZE + ARecordPointable.RECORD_LENGTH_SIZE;
+        int pointer = start + skipTag + ARecordPointable.RECORD_LENGTH_SIZE;
         if (recordType.isOpen()) {
-            isExpanded = bytes[cursor] == 1;
+            isExpanded = bytes[pointer] == 1;
             // advance to either open part offset or number of closed fields
-            cursor += ARecordPointable.EXPANDED_SIZE;
+            pointer += ARecordPointable.EXPANDED_SIZE;
         }
         int openPartOffset = 0;
         if (isExpanded) {
-            openPartOffset = start + AInt32SerializerDeserializer.getInt(bytes, cursor);
+            openPartOffset = start + AInt32SerializerDeserializer.getInt(bytes, pointer) - fixOffset;
             // advance to number of closed fields
-            cursor += ARecordPointable.OPEN_OFFSET_SIZE;
+            pointer += ARecordPointable.OPEN_OFFSET_SIZE;
         }
         int fieldOffset;
         int length;
         int fieldIndex = 0;
         ATypeTag tag;
         // advance to where fields offsets are (or to null bit map if the schema has optional fields)
-        cursor += ARecordPointable.CLOSED_COUNT_SIZE;
-        int nullBitMapOffset = cursor;
-        int fieldsOffsets = cursor + nullBitMapSize;
-        // compute the offsets of each closed field value and whether it's missing or null
-        for (int i = 0; i < numSchemaFields; i++, fieldIndex++) {
-            fieldOffset = AInt32SerializerDeserializer.getInt(bytes, fieldsOffsets) + start;
-            tag = TypeComputeUtils.getActualType(fieldTypes[i]).getTypeTag();
-            if (hasOptionalFields) {
-                byte nullBits = bytes[nullBitMapOffset + i / 4];
-                if (RecordUtil.isNull(nullBits, i)) {
-                    tag = ATypeTag.NULL;
-                } else if (RecordUtil.isMissing(nullBits, i)) {
-                    tag = ATypeTag.MISSING;
+        if (numSchemaFields > 0) {
+            pointer += ARecordPointable.CLOSED_COUNT_SIZE;
+            int nullBitMapOffset = pointer;
+            int fieldsOffsets = nullBitMapOffset + nullBitMapSize;
+            // compute the offsets of each closed field value and whether it's missing or null
+            for (int i = 0; i < numSchemaFields; i++, fieldIndex++) {
+                fieldOffset = AInt32SerializerDeserializer.getInt(bytes, fieldsOffsets) + start - fixOffset;
+                tag = TypeComputeUtils.getActualType(fieldTypes[i]).getTypeTag();
+                if (hasOptionalFields) {
+                    byte nullBits = bytes[nullBitMapOffset + i / 4];
+                    if (RecordUtil.isNull(nullBits, i)) {
+                        tag = ATypeTag.NULL;
+                    } else if (RecordUtil.isMissing(nullBits, i)) {
+                        tag = ATypeTag.MISSING;
+                    }
                 }
+                length = NonTaggedFormatUtil.getFieldValueLength(bytes, fieldOffset, tag, false);
+                closedFields[i].set(fieldIndex, fieldOffset, length, tag);
+                if (tag != ATypeTag.MISSING) {
+                    sortedFields.add(closedFields[i]);
+                }
+                fieldsOffsets += ARecordPointable.FIELD_OFFSET_SIZE;
             }
-            length = NonTaggedFormatUtil.getFieldValueLength(bytes, fieldOffset, tag, false);
-            closedFields[i].set(fieldIndex, fieldOffset, length, tag);
-            if (tag != ATypeTag.MISSING) {
-                sortedFields.add(closedFields[i]);
-            }
-            fieldsOffsets += ARecordPointable.FIELD_OFFSET_SIZE;
         }
         // then populate open fields info second, an open field has name + value (tagged)
         if (isExpanded) {
@@ -185,6 +196,21 @@
         return RecordUtil.getType(recordType, field.getIndex(), field.getValueTag());
     }
 
+    public final void getFieldValue(RecordField field, TaggedValueReference fieldValueRef) {
+        if (field.getIndex() >= numSchemaFields) {
+            fieldValueRef.set(bytes, field.getValueOffset() + 1, field.getValueLength() - 1, field.getValueTag());
+        } else {
+            if (field.getValueTag() == ATypeTag.MISSING) {
+                fieldValueRef.set(MISSING_BYTES, 0, 0, ATypeTag.MISSING);
+            } else if (field.getValueTag() == ATypeTag.NULL) {
+                fieldValueRef.set(NULL_BYTES, 0, 0, ATypeTag.NULL);
+            } else {
+                fieldValueRef.set(bytes, field.getValueOffset(), field.getValueLength(), field.getValueTag());
+            }
+        }
+    }
+
+    // TODO(ali): remove this method once hashing does not need the tag to be adjacent to the value
     public final void getFieldValue(RecordField field, IPointable pointable, ArrayBackedValueStorage storage)
             throws IOException {
         int fieldIdx = field.getIndex();
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/util/container/ObjectFactories.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/util/container/ObjectFactories.java
index 2c6e408..ff76ea3 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/util/container/ObjectFactories.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/util/container/ObjectFactories.java
@@ -20,6 +20,7 @@
 
 import java.util.BitSet;
 
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.om.pointables.nonvisitor.SortedRecord;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.hyracks.data.std.api.IMutableValueStorage;
@@ -45,4 +46,5 @@
     public static final IObjectFactory<BitSet, Void> BIT_SET_FACTORY = type -> new BitSet();
     public static final IObjectFactory<UTF8StringPointable, Void> UTF8_FACTORY = type -> new UTF8StringPointable();
     public static final IObjectFactory<SortedRecord, ARecordType> RECORD_FACTORY = SortedRecord::new;
+    public static final IObjectFactory<TaggedValueReference, Void> VALUE_FACTORY = type -> new TaggedValueReference();
 }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java
index aa45922..2c49f71 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java
@@ -18,11 +18,14 @@
  */
 package org.apache.asterix.runtime.aggregates.std;
 
+import static org.apache.asterix.om.types.ATypeTag.VALUE_TYPE_MAPPING;
+
 import java.io.IOException;
 
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.exceptions.WarningUtil;
 import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator;
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.dataflow.data.nontagged.comparators.ComparatorUtil;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.EnumDeserializer;
@@ -45,6 +48,8 @@
     private final IPointable inputVal = new VoidPointable();
     private final ArrayBackedValueStorage outputVal = new ArrayBackedValueStorage();
     private final ArrayBackedValueStorage tempValForCasting = new ArrayBackedValueStorage();
+    private final TaggedValueReference value1 = new TaggedValueReference();
+    private final TaggedValueReference value2 = new TaggedValueReference();
     private final IScalarEvaluator eval;
     private final boolean isMin;
     private final IAType aggFieldType;
@@ -170,7 +175,15 @@
     private void compareAndUpdate(ILogicalBinaryComparator c, IPointable newVal, ArrayBackedValueStorage currentVal,
             ATypeTag typeTag) throws HyracksDataException {
         // newVal is never NULL/MISSING here. it's already checked up. current value is the first encountered non-null.
-        ILogicalBinaryComparator.Result result = c.compare(newVal, currentVal);
+        byte[] newValByteArray = newVal.getByteArray();
+        int newValStartOffset = newVal.getStartOffset();
+        byte[] currentValByteArray = currentVal.getByteArray();
+        int currentValStartOffset = currentVal.getStartOffset();
+        value1.set(newValByteArray, newValStartOffset + 1, newVal.getLength() - 1,
+                VALUE_TYPE_MAPPING[newValByteArray[newValStartOffset]]);
+        value2.set(currentValByteArray, currentValStartOffset + 1, currentVal.getLength() - 1,
+                VALUE_TYPE_MAPPING[newValByteArray[newValStartOffset]]);
+        ILogicalBinaryComparator.Result result = c.compare(value1, value2);
         switch (result) {
             case LT:
                 if (isMin) {
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/comparisons/AbstractComparisonEvaluator.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/comparisons/AbstractComparisonEvaluator.java
index 216259d..244ffc3 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/comparisons/AbstractComparisonEvaluator.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/comparisons/AbstractComparisonEvaluator.java
@@ -18,10 +18,13 @@
  */
 package org.apache.asterix.runtime.evaluators.comparisons;
 
+import static org.apache.asterix.om.types.ATypeTag.VALUE_TYPE_MAPPING;
+
 import java.io.DataOutput;
 
 import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator;
 import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator.Result;
+import org.apache.asterix.dataflow.data.common.TaggedValueReference;
 import org.apache.asterix.dataflow.data.nontagged.comparators.ComparatorUtil;
 import org.apache.asterix.dataflow.data.nontagged.serde.ADoubleSerializerDeserializer;
 import org.apache.asterix.dataflow.data.nontagged.serde.AFloatSerializerDeserializer;
@@ -68,6 +71,8 @@
     protected final DataOutput out = resultStorage.getDataOutput();
     protected final TaggedValuePointable argLeft = TaggedValuePointable.FACTORY.createPointable();
     private final TaggedValuePointable argRight = TaggedValuePointable.FACTORY.createPointable();
+    private final TaggedValueReference leftVal = new TaggedValueReference();
+    private final TaggedValueReference rightVal = new TaggedValueReference();
     private final IScalarEvaluator evalLeft;
     private final IScalarEvaluator evalRight;
     protected final SourceLocation sourceLoc;
@@ -102,38 +107,41 @@
         if (PointableHelper.checkAndSetMissingOrNull(result, argLeft, argRight)) {
             return;
         }
-
+        leftVal.set(argLeft.getByteArray(), argLeft.getStartOffset() + 1, argLeft.getLength() - 1,
+                VALUE_TYPE_MAPPING[argLeft.getTag()]);
+        rightVal.set(argRight.getByteArray(), argRight.getStartOffset() + 1, argRight.getLength() - 1,
+                VALUE_TYPE_MAPPING[argRight.getTag()]);
         evaluateImpl(result);
     }
 
     protected abstract void evaluateImpl(IPointable result) throws HyracksDataException;
 
-    Result compare() throws HyracksDataException {
+    final Result compare() throws HyracksDataException {
         if (leftConstant != null) {
             if (rightConstant != null) {
                 // both are constants
                 return logicalComparator.compare(leftConstant, rightConstant);
             } else {
                 // left is constant, right isn't
-                return logicalComparator.compare(leftConstant, argRight);
+                return logicalComparator.compare(leftConstant, rightVal);
             }
         } else {
             if (rightConstant != null) {
                 // right is constant, left isn't
-                return logicalComparator.compare(argLeft, rightConstant);
+                return logicalComparator.compare(leftVal, rightConstant);
             } else {
-                return logicalComparator.compare(argLeft, argRight);
+                return logicalComparator.compare(leftVal, rightVal);
             }
         }
     }
 
-    void writeMissing(IPointable result) throws HyracksDataException {
+    final void writeMissing(IPointable result) throws HyracksDataException {
         resultStorage.reset();
         missingSerde.serialize(AMissing.MISSING, out);
         result.set(resultStorage);
     }
 
-    void writeNull(IPointable result) throws HyracksDataException {
+    final void writeNull(IPointable result) throws HyracksDataException {
         resultStorage.reset();
         nullSerde.serialize(ANull.NULL, out);
         result.set(resultStorage);