You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by al...@apache.org on 2019/05/15 18:15:15 UTC

[asterixdb] branch master updated: [ASTERIXDB-2555][RT][COMP] Make hash join use logical comparison

This is an automated email from the ASF dual-hosted git repository.

alsuliman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git


The following commit(s) were added to refs/heads/master by this push:
     new b09ec2d  [ASTERIXDB-2555][RT][COMP] Make hash join use logical comparison
b09ec2d is described below

commit b09ec2d99277c2fbce0e6d2961aa4ca483963edd
Author: Ali Alsuliman <al...@gmail.com>
AuthorDate: Thu May 9 17:02:50 2019 -0700

    [ASTERIXDB-2555][RT][COMP] Make hash join use logical comparison
    
    - user model changes: no
    - storage format changes: no
    - interface changes: no
    
    Details:
    This patch changes the hash join operator to use the join condition
    to evaluate if tuples are equal when joining. Binary physical comparators
    have been removed. The join condition evaluator is in TuplePairEvaluator.
    
    - extraced TuplePairEvaluatorFactory out of nested loop join class
    into a separate class so that it is shared among nested loop and
    hash join.
    - switched from FrameTuplePairComparator to ITuplePairComparator in
    in OptimizedHybridHashJoin and InMemoryHashJoin.
    - moved debugging code from OptimizedHybridHashJoin into a separate
    class, JoinUtil.
    - temporarily made the logical comparison of multisets use raw binary
    comparison instead of returning null until the logic is implemented.
    - made IBinaryBooleanInspector a functional interface and updated
    the implementations.
    - updated record and array test cases to reflect the new
    behaviour of hash join where logical comparison could produce null.
    Also, updated sorting, group by and distinct test cases since
    the input data has been modified.
    - added two new input files arrays1nulls.adm & arrays2nulls.adm
    to be used by the open dataset. previous arrays1.adm & arrays2.adm
    are used by the closed dataset since it cannot accept arrays with
    null values.
    
    Change-Id: If1834967fdd913fdc76003f09636b2450d07cd5e
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/3387
    Contrib: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Reviewed-by: Dmitry Lychagin <dm...@couchbase.com>
    Reviewed-by: Murtadha Hubail <mh...@apache.org>
---
 .../asterix-app/data/complex/arrays1nulls.adm      |  23 ++++
 .../asterix-app/data/complex/arrays2nulls.adm      |  18 +++
 .../distinct/array/array.2.update.sqlpp            |   2 +-
 .../group-by/gby-array/gby-array.2.update.sqlpp    |   2 +-
 .../hash_join_array/hash_join_array.2.update.sqlpp |   4 +-
 .../sorting/arrays/arrays.2.update.sqlpp           |   2 +-
 .../runtimets/results/distinct/array/array.3.adm   |   1 +
 .../results/group-by/gby-array/gby-array.3.adm     |   1 +
 .../results/group-by/gby-array/gby-array.5.adm     |   1 +
 .../join/hash_join_array/hash_join_array.7.adm     |   4 +-
 .../join/hash_join_array/hash_join_array.8.adm     |   4 +-
 .../join/hash_join_record/hash_join_record.03.adm  |   5 -
 .../join/hash_join_record/hash_join_record.04.adm  |   5 -
 .../join/hash_join_record/hash_join_record.05.adm  |   5 -
 .../join/hash_join_record/hash_join_record.06.adm  |   5 -
 .../join/hash_join_record/hash_join_record.07.adm  |   5 -
 .../join/hash_join_record/hash_join_record.08.adm  |   5 -
 .../join/hash_join_record/hash_join_record.09.adm  |   5 -
 .../join/hash_join_record/hash_join_record.10.adm  |   5 -
 .../join/hash_join_record/hash_join_record.11.adm  |   5 -
 .../join/hash_join_record/hash_join_record.12.adm  |   8 +-
 .../join/hash_join_record/hash_join_record.13.adm  |   8 +-
 .../join/hash_join_record/hash_join_record.14.adm  |   8 +-
 .../join/hash_join_record/hash_join_record.15.adm  |   8 +-
 .../join/hash_join_record/hash_join_record.16.adm  |   8 +-
 .../join/hash_join_record/hash_join_record.17.adm  |   8 +-
 .../join/hash_join_record/hash_join_record.18.adm  |   8 +-
 .../join/hash_join_record/hash_join_record.19.adm  |   8 +-
 .../join/hash_join_record/hash_join_record.20.adm  |   8 +-
 .../runtimets/results/sorting/arrays/arrays.3.adm  |   2 +
 .../runtimets/results/sorting/arrays/arrays.5.adm  |   2 +
 .../LogicalComplexBinaryComparator.java            |   5 +-
 .../formats/nontagged/BinaryBooleanInspector.java  |  14 +-
 .../algebricks/algebricks-core/pom.xml             |   5 -
 .../physical/HybridHashJoinPOperator.java          | 123 ++++-------------
 .../physical/InMemoryHashJoinPOperator.java        |  28 ++--
 .../physical/NestedLoopJoinPOperator.java          | 129 +-----------------
 .../algebricks/data/IBinaryBooleanInspector.java   |   4 +-
 .../data/impl/BinaryBooleanInspectorImpl.java      |  11 +-
 .../evaluators/TuplePairEvaluatorFactory.java      | 150 +++++++++++++++++++++
 .../api/dataflow/value/ITuplePairComparator.java   |   4 +-
 .../dataflow/std/join/HybridHashJoinUtil.java      |  99 ++++++++++++++
 .../dataflow/std/join/InMemoryHashJoin.java        |   8 +-
 .../join/InMemoryHashJoinOperatorDescriptor.java   |  30 ++---
 .../dataflow/std/join/OptimizedHybridHashJoin.java |  96 ++-----------
 .../OptimizedHybridHashJoinOperatorDescriptor.java |  89 +++++-------
 .../integration/TPCHCustomerOrderHashJoinTest.java |  23 +---
 .../TPCHCustomerOrderNestedLoopJoinTest.java       |  64 +--------
 .../apache/hyracks/examples/tpch/client/Join.java  |   4 +-
 49 files changed, 462 insertions(+), 607 deletions(-)

diff --git a/asterixdb/asterix-app/data/complex/arrays1nulls.adm b/asterixdb/asterix-app/data/complex/arrays1nulls.adm
new file mode 100644
index 0000000..acd7802
--- /dev/null
+++ b/asterixdb/asterix-app/data/complex/arrays1nulls.adm
@@ -0,0 +1,23 @@
+{"id":1, "name":"Margarita", "dept_ids": [3, 2, 8]}
+{"id":2, "name":"Isac", "dept_ids": [99, 12, 14, 15, 77]}
+{"id":3, "name":"Emory", "dept_ids": [33, 11, 3, 16]}
+{"id":4, "name":"Nicholas", "dept_ids": []}
+{"id":5, "name":"Von", "dept_ids": [4]}
+{"id":6, "name":"Willis", "dept_ids": [1, 5, 6]}
+{"id":7, "name":"Suzanna", "dept_ids": [9, 5, 2]}
+{"id":8, "name":"Nicole", "dept_ids": [33, 11, 3, 16]}
+{"id":9, "name":"Woodrow", "dept_ids": [3, 2, 8]}
+{"id":10, "name":"Bram", "dept_ids": []}
+{"id":11, "name":"Nicholas", "dept_ids": null}
+{"id":12, "name":"John", "dept_ids": [2]}
+{"id":13, "name":"Steve", "dept_ids": [8, 2]}
+{"id":14, "name":"Jay", "dept_ids": [4]}
+{"id":15, "name":"Jim", "dept_ids": null}
+{"id":16, "name":"Wail", "dept_ids": [9, 5, 2]}
+{"id":17, "name":"Jim"}
+{"id":18, "name":"Kayle", "dept_ids": [8, 2, 1, 7, 9]}
+{"id":19, "name":"Mart"}
+{"id":20, "name":"Mai", "dept_ids": [1, 5, 9]}
+{"id":21, "name":"Ken", "dept_ids": [3, 5, 8]}
+{"id":38, "name":"Kim", "dept_ids": [33, null, 3, 16]}
+{"id":45, "name":"Kim", "dept_ids": [33, null, 3, 16]}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/data/complex/arrays2nulls.adm b/asterixdb/asterix-app/data/complex/arrays2nulls.adm
new file mode 100644
index 0000000..64bd2dd
--- /dev/null
+++ b/asterixdb/asterix-app/data/complex/arrays2nulls.adm
@@ -0,0 +1,18 @@
+{"id":1, "name":"Margarita", "dept_ids": [4]}
+{"id":2, "name":"Isac", "dept_ids": [99, 12, 14, 15]}
+{"id":5, "name":"Von", "dept_ids": [3, 2, 8]}
+{"id":6, "name":"Willis", "dept_ids": []}
+{"id":8, "name":"Nicole", "dept_ids": [33, 11, 3, 16]}
+{"id":9, "name":"Woodrow", "dept_ids": null}
+{"id":11, "name":"Nicholas", "dept_ids":[3, 2, 8] }
+{"id":12, "name":"John", "dept_ids": [9, 5, 2]}
+{"id":13, "name":"Steve"}
+{"id":14, "name":"Jay", "dept_ids": [9, 5, 2]}
+{"id":15, "name":"Jim", "dept_ids": null}
+{"id":16, "name":"Wail", "dept_ids": [2]}
+{"id":18, "name":"Kayle"}
+{"id":19, "name":"Mart", "dept_ids": [8, 2, 1, 7, 9]}
+{"id":20, "name":"Mai", "dept_ids": [1, 5, 9]}
+{"id":21, "name":"Ken", "dept_ids": [3, 5, 8]}
+{"id":22, "name":"Son", "dept_ids": [33, null, 3, 16]}
+{"id":23, "name":"Young", "dept_ids": [33, null, 3, 16]}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/distinct/array/array.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/distinct/array/array.2.update.sqlpp
index 98b5875..4a5c210 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/distinct/array/array.2.update.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/distinct/array/array.2.update.sqlpp
@@ -20,4 +20,4 @@
 use test;
 
 load dataset closedDs using localfs (("path"="asterix_nc1://data/complex/arrays1.adm"),("format"="adm"));
-load dataset openDs using localfs (("path"="asterix_nc1://data/complex/arrays1.adm"),("format"="adm"));
\ No newline at end of file
+load dataset openDs using localfs (("path"="asterix_nc1://data/complex/arrays1nulls.adm"),("format"="adm"));
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/gby-array/gby-array.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/gby-array/gby-array.2.update.sqlpp
index 98b5875..4a5c210 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/gby-array/gby-array.2.update.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/gby-array/gby-array.2.update.sqlpp
@@ -20,4 +20,4 @@
 use test;
 
 load dataset closedDs using localfs (("path"="asterix_nc1://data/complex/arrays1.adm"),("format"="adm"));
-load dataset openDs using localfs (("path"="asterix_nc1://data/complex/arrays1.adm"),("format"="adm"));
\ No newline at end of file
+load dataset openDs using localfs (("path"="asterix_nc1://data/complex/arrays1nulls.adm"),("format"="adm"));
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_array/hash_join_array.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_array/hash_join_array.2.update.sqlpp
index b87bf16..62ef9e1 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_array/hash_join_array.2.update.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_array/hash_join_array.2.update.sqlpp
@@ -20,7 +20,7 @@
 use test;
 
 load dataset closedDs1 using localfs (("path"="asterix_nc1://data/complex/arrays1.adm"),("format"="adm"));
-load dataset openDs1 using localfs (("path"="asterix_nc1://data/complex/arrays1.adm"),("format"="adm"));
+load dataset openDs1 using localfs (("path"="asterix_nc1://data/complex/arrays1nulls.adm"),("format"="adm"));
 
 load dataset closedDs2 using localfs (("path"="asterix_nc1://data/complex/arrays2.adm"),("format"="adm"));
-load dataset openDs2 using localfs (("path"="asterix_nc1://data/complex/arrays2.adm"),("format"="adm"));
\ No newline at end of file
+load dataset openDs2 using localfs (("path"="asterix_nc1://data/complex/arrays2nulls.adm"),("format"="adm"));
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/sorting/arrays/arrays.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/sorting/arrays/arrays.2.update.sqlpp
index 98b5875..4a5c210 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/sorting/arrays/arrays.2.update.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/sorting/arrays/arrays.2.update.sqlpp
@@ -20,4 +20,4 @@
 use test;
 
 load dataset closedDs using localfs (("path"="asterix_nc1://data/complex/arrays1.adm"),("format"="adm"));
-load dataset openDs using localfs (("path"="asterix_nc1://data/complex/arrays1.adm"),("format"="adm"));
\ No newline at end of file
+load dataset openDs using localfs (("path"="asterix_nc1://data/complex/arrays1nulls.adm"),("format"="adm"));
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/distinct/array/array.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/distinct/array/array.3.adm
index 89ceb43..7ba49ca 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/distinct/array/array.3.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/distinct/array/array.3.adm
@@ -10,5 +10,6 @@
 { "dept_ids": [ 8, 2 ] }
 { "dept_ids": [ 8, 2, 1, 7, 9 ] }
 { "dept_ids": [ 9, 5, 2 ] }
+{ "dept_ids": [ 33, null, 3, 16 ] }
 { "dept_ids": [ 33, 11, 3, 16 ] }
 { "dept_ids": [ 99, 12, 14, 15, 77 ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/gby-array/gby-array.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/gby-array/gby-array.3.adm
index fe8e7c7..6ac498e 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/gby-array/gby-array.3.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/gby-array/gby-array.3.adm
@@ -10,5 +10,6 @@
 { "count": 1, "dept_ids": [ 8, 2 ] }
 { "count": 1, "dept_ids": [ 8, 2, 1, 7, 9 ] }
 { "count": 2, "dept_ids": [ 9, 5, 2 ] }
+{ "count": 2, "dept_ids": [ 33, null, 3, 16 ] }
 { "count": 2, "dept_ids": [ 33, 11, 3, 16 ] }
 { "count": 1, "dept_ids": [ 99, 12, 14, 15, 77 ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/gby-array/gby-array.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/gby-array/gby-array.5.adm
index fe8e7c7..6ac498e 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/gby-array/gby-array.5.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/gby-array/gby-array.5.adm
@@ -10,5 +10,6 @@
 { "count": 1, "dept_ids": [ 8, 2 ] }
 { "count": 1, "dept_ids": [ 8, 2, 1, 7, 9 ] }
 { "count": 2, "dept_ids": [ 9, 5, 2 ] }
+{ "count": 2, "dept_ids": [ 33, null, 3, 16 ] }
 { "count": 2, "dept_ids": [ 33, 11, 3, 16 ] }
 { "count": 1, "dept_ids": [ 99, 12, 14, 15, 77 ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_array/hash_join_array.7.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_array/hash_join_array.7.adm
index 8aabb36..21aa85b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_array/hash_join_array.7.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_array/hash_join_array.7.adm
@@ -22,4 +22,6 @@
 { "v1": { "id": 18, "name": "Kayle", "dept_ids": [ 8, 2, 1, 7, 9 ] }, "v2": { "id": 19, "name": "Mart", "dept_ids": [ 8, 2, 1, 7, 9 ] } }
 { "v1": { "id": 19, "name": "Mart" } }
 { "v1": { "id": 20, "name": "Mai", "dept_ids": [ 1, 5, 9 ] }, "v2": { "id": 20, "name": "Mai", "dept_ids": [ 1, 5, 9 ] } }
-{ "v1": { "id": 21, "name": "Ken", "dept_ids": [ 3, 5, 8 ] }, "v2": { "id": 21, "name": "Ken", "dept_ids": [ 3, 5, 8 ] } }
\ No newline at end of file
+{ "v1": { "id": 21, "name": "Ken", "dept_ids": [ 3, 5, 8 ] }, "v2": { "id": 21, "name": "Ken", "dept_ids": [ 3, 5, 8 ] } }
+{ "v1": { "id": 38, "name": "Kim", "dept_ids": [ 33, null, 3, 16 ] } }
+{ "v1": { "id": 45, "name": "Kim", "dept_ids": [ 33, null, 3, 16 ] } }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_array/hash_join_array.8.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_array/hash_join_array.8.adm
index 8aabb36..21aa85b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_array/hash_join_array.8.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_array/hash_join_array.8.adm
@@ -22,4 +22,6 @@
 { "v1": { "id": 18, "name": "Kayle", "dept_ids": [ 8, 2, 1, 7, 9 ] }, "v2": { "id": 19, "name": "Mart", "dept_ids": [ 8, 2, 1, 7, 9 ] } }
 { "v1": { "id": 19, "name": "Mart" } }
 { "v1": { "id": 20, "name": "Mai", "dept_ids": [ 1, 5, 9 ] }, "v2": { "id": 20, "name": "Mai", "dept_ids": [ 1, 5, 9 ] } }
-{ "v1": { "id": 21, "name": "Ken", "dept_ids": [ 3, 5, 8 ] }, "v2": { "id": 21, "name": "Ken", "dept_ids": [ 3, 5, 8 ] } }
\ No newline at end of file
+{ "v1": { "id": 21, "name": "Ken", "dept_ids": [ 3, 5, 8 ] }, "v2": { "id": 21, "name": "Ken", "dept_ids": [ 3, 5, 8 ] } }
+{ "v1": { "id": 38, "name": "Kim", "dept_ids": [ 33, null, 3, 16 ] } }
+{ "v1": { "id": 45, "name": "Kim", "dept_ids": [ 33, null, 3, 16 ] } }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.03.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.03.adm
index ed4cfc9..f96ac0e 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.03.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.03.adm
@@ -13,8 +13,6 @@
 { "id1": 13, "id2": 4, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 19, "id2": 24, "add1": {  }, "add2": {  } }
 { "id1": 21, "id2": 21, "add1": { "street": "15th", "apt": 6, "state": "CO", "zipcode": 44321, "country": "USA" }, "add2": { "street": "15th", "apt": 6, "state": "CO", "zipcode": 44321, "country": "USA" } }
 { "id1": 22, "id2": 22, "add1": { "street": "16th", "state": "BC", "country": "Canada" }, "add2": { "street": "16th", "state": "BC", "country": "Canada" } }
@@ -32,13 +30,10 @@
 { "id1": 30, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 30, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
-{ "id1": 34, "id2": 34, "add1": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" }, "add2": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
 { "id1": 35, "id2": 35, "add1": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" }, "add2": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.04.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.04.adm
index 3ecd74e..c31626c 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.04.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.04.adm
@@ -13,8 +13,6 @@
 { "id1": 13, "id2": 4, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 21, "id2": 21, "add2": { "state": "CO", "country": "USA", "apt": 6, "street": "15th", "zipcode": 44321 }, "add1": { "street": "15th", "apt": 6, "state": "CO", "zipcode": 44321, "country": "USA" } }
 { "id1": 22, "id2": 22, "add2": { "state": "BC", "country": "Canada", "street": "16th" }, "add1": { "street": "16th", "state": "BC", "country": "Canada" } }
 { "id1": 23, "id2": 23, "add2": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 }, "add1": { "street": "14th", "state": "MN", "zipcode": 78812, "country": "USA" } }
@@ -30,13 +28,10 @@
 { "id1": 30, "id2": 31, "add2": { "state": "BC", "country": "Canada" }, "add1": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 30, "add2": { "state": "BC", "country": "Canada" }, "add1": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 31, "add2": { "state": "BC", "country": "Canada" }, "add1": { "state": "BC", "country": "Canada" } }
-{ "id1": 34, "id2": 34, "add2": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 }, "add1": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
 { "id1": 35, "id2": 35, "add2": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 }, "add1": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 38, "id2": 28, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.05.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.05.adm
index c499cb9..58153e1 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.05.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.05.adm
@@ -13,8 +13,6 @@
 { "id1": 13, "id2": 4, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 21, "id2": 21, "add2": { "state": "CO", "country": "USA", "street": "15th", "apt": 6, "zipcode": 44321 }, "add1": { "street": "15th", "apt": 6, "state": "CO", "zipcode": 44321, "country": "USA" } }
 { "id1": 22, "id2": 22, "add2": { "state": "BC", "country": "Canada", "street": "16th" }, "add1": { "street": "16th", "state": "BC", "country": "Canada" } }
 { "id1": 23, "id2": 23, "add2": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 }, "add1": { "street": "14th", "state": "MN", "zipcode": 78812, "country": "USA" } }
@@ -30,13 +28,10 @@
 { "id1": 30, "id2": 31, "add2": { "state": "BC", "country": "Canada" }, "add1": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 30, "add2": { "state": "BC", "country": "Canada" }, "add1": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 31, "add2": { "state": "BC", "country": "Canada" }, "add1": { "state": "BC", "country": "Canada" } }
-{ "id1": 34, "id2": 34, "add2": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 }, "add1": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
 { "id1": 35, "id2": 35, "add2": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 }, "add1": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 38, "id2": 28, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.06.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.06.adm
index fd4ae68..5bc70ed 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.06.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.06.adm
@@ -13,8 +13,6 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 21, "id2": 21, "add1": { "state": "CO", "country": "USA", "apt": 6, "street": "15th", "zipcode": 44321 }, "add2": { "street": "15th", "apt": 6, "state": "CO", "zipcode": 44321, "country": "USA" } }
 { "id1": 22, "id2": 22, "add1": { "state": "BC", "country": "Canada", "street": "16th" }, "add2": { "street": "16th", "state": "BC", "country": "Canada" } }
 { "id1": 23, "id2": 23, "add1": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 }, "add2": { "street": "14th", "state": "MN", "zipcode": 78812, "country": "USA" } }
@@ -30,13 +28,10 @@
 { "id1": 30, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 30, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 }, "add2": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 }, "add2": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.07.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.07.adm
index c77e071..4a63674 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.07.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.07.adm
@@ -13,8 +13,6 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 21, "id2": 21, "add1": { "state": "CO", "country": "USA", "apt": 6, "street": "15th", "zipcode": 44321 }, "add2": { "state": "CO", "country": "USA", "apt": 6, "street": "15th", "zipcode": 44321 } }
 { "id1": 22, "id2": 22, "add1": { "state": "BC", "country": "Canada", "street": "16th" }, "add2": { "state": "BC", "country": "Canada", "street": "16th" } }
 { "id1": 23, "id2": 23, "add1": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 }, "add2": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 } }
@@ -30,13 +28,10 @@
 { "id1": 30, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 30, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 }, "add2": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 }, "add2": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.08.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.08.adm
index 5ca90d2..aaadff0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.08.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.08.adm
@@ -13,8 +13,6 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 21, "id2": 21, "add1": { "state": "CO", "country": "USA", "apt": 6, "street": "15th", "zipcode": 44321 }, "add2": { "state": "CO", "country": "USA", "street": "15th", "apt": 6, "zipcode": 44321 } }
 { "id1": 22, "id2": 22, "add1": { "state": "BC", "country": "Canada", "street": "16th" }, "add2": { "state": "BC", "country": "Canada", "street": "16th" } }
 { "id1": 23, "id2": 23, "add1": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 }, "add2": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 } }
@@ -30,13 +28,10 @@
 { "id1": 30, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 30, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 }, "add2": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 }, "add2": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.09.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.09.adm
index 80d7c58..f8f59c0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.09.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.09.adm
@@ -13,8 +13,6 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 21, "id2": 21, "add1": { "state": "CO", "country": "USA", "street": "15th", "apt": 6, "zipcode": 44321 }, "add2": { "street": "15th", "apt": 6, "state": "CO", "zipcode": 44321, "country": "USA" } }
 { "id1": 22, "id2": 22, "add1": { "state": "BC", "country": "Canada", "street": "16th" }, "add2": { "street": "16th", "state": "BC", "country": "Canada" } }
 { "id1": 23, "id2": 23, "add1": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 }, "add2": { "street": "14th", "state": "MN", "zipcode": 78812, "country": "USA" } }
@@ -30,13 +28,10 @@
 { "id1": 30, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 30, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 }, "add2": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 }, "add2": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.10.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.10.adm
index 71c2dd2..bc02ed2 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.10.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.10.adm
@@ -13,8 +13,6 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 21, "id2": 21, "add1": { "state": "CO", "country": "USA", "street": "15th", "apt": 6, "zipcode": 44321 }, "add2": { "state": "CO", "country": "USA", "apt": 6, "street": "15th", "zipcode": 44321 } }
 { "id1": 22, "id2": 22, "add1": { "state": "BC", "country": "Canada", "street": "16th" }, "add2": { "state": "BC", "country": "Canada", "street": "16th" } }
 { "id1": 23, "id2": 23, "add1": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 }, "add2": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 } }
@@ -30,13 +28,10 @@
 { "id1": 30, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 30, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 }, "add2": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 }, "add2": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.11.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.11.adm
index 8adc4bf..decb58b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.11.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.11.adm
@@ -13,8 +13,6 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 21, "id2": 21, "add1": { "state": "CO", "country": "USA", "street": "15th", "apt": 6, "zipcode": 44321 }, "add2": { "state": "CO", "country": "USA", "street": "15th", "apt": 6, "zipcode": 44321 } }
 { "id1": 22, "id2": 22, "add1": { "state": "BC", "country": "Canada", "street": "16th" }, "add2": { "state": "BC", "country": "Canada", "street": "16th" } }
 { "id1": 23, "id2": 23, "add1": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 }, "add2": { "state": "MN", "country": "USA", "street": "14th", "zipcode": 78812 } }
@@ -30,13 +28,10 @@
 { "id1": 30, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 30, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 }, "add2": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 }, "add2": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.12.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.12.adm
index bd37428..798d9f7 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.12.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.12.adm
@@ -16,8 +16,7 @@
 { "id1": 13, "id2": 4, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 14, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 15, "add1": null }
 { "id1": 16 }
 { "id1": 17 }
@@ -44,13 +43,12 @@
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 32, "add1": { "state": "ON", "country": "Canada" } }
 { "id1": 33, "add1": { "street": null, "apt": null, "state": "CO", "country": "USA" } }
-{ "id1": 34, "id2": 34, "add1": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" }, "add2": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
+{ "id1": 34, "add1": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
 { "id1": 35, "id2": 35, "add1": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" }, "add2": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 37, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.13.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.13.adm
index 65156dd..28ca49d 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.13.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.13.adm
@@ -16,8 +16,7 @@
 { "id1": 13, "id2": 4, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 14, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 15, "add1": null }
 { "id1": 16 }
 { "id1": 17 }
@@ -44,13 +43,12 @@
 { "id1": 31, "id2": 31, "add2": { "state": "BC", "country": "Canada" }, "add1": { "state": "BC", "country": "Canada" } }
 { "id1": 32, "add1": { "state": "ON", "country": "Canada" } }
 { "id1": 33, "add1": { "street": null, "apt": null, "state": "CO", "country": "USA" } }
-{ "id1": 34, "id2": 34, "add2": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 }, "add1": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
+{ "id1": 34, "add1": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
 { "id1": 35, "id2": 35, "add2": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 }, "add1": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 37, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 38, "id2": 28, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.14.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.14.adm
index 197eb1c..8476427 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.14.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.14.adm
@@ -16,8 +16,7 @@
 { "id1": 13, "id2": 4, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 14, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 15, "add1": null }
 { "id1": 16 }
 { "id1": 17 }
@@ -44,13 +43,12 @@
 { "id1": 31, "id2": 31, "add2": { "state": "BC", "country": "Canada" }, "add1": { "state": "BC", "country": "Canada" } }
 { "id1": 32, "add1": { "state": "ON", "country": "Canada" } }
 { "id1": 33, "add1": { "street": null, "apt": null, "state": "CO", "country": "USA" } }
-{ "id1": 34, "id2": 34, "add2": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 }, "add1": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
+{ "id1": 34, "add1": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
 { "id1": 35, "id2": 35, "add2": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 }, "add1": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add1": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 37, "add1": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
 { "id1": 38, "id2": 28, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add2": { "state": "AL", "country": "USA" }, "add1": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.15.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.15.adm
index a14ebdb..39563e3 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.15.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.15.adm
@@ -16,8 +16,7 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 15, "add1": null }
 { "id1": 16 }
 { "id1": 17 }
@@ -42,13 +41,12 @@
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 32, "add1": { "state": "ON", "country": "Canada" } }
 { "id1": 33, "add1": { "state": "CO", "country": "USA", "apt": null, "street": null } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 }, "add2": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
+{ "id1": 34, "add1": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 }, "add2": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.16.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.16.adm
index dcc7f0b..0373689 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.16.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.16.adm
@@ -16,8 +16,7 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
+{ "id1": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 15, "add1": null }
 { "id1": 16 }
 { "id1": 17 }
@@ -42,13 +41,12 @@
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 32, "add1": { "state": "ON", "country": "Canada" } }
 { "id1": 33, "add1": { "state": "CO", "country": "USA", "apt": null, "street": null } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 }, "add2": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 } }
+{ "id1": 34, "add1": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 }, "add2": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
+{ "id1": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.17.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.17.adm
index ff08a44..92db128 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.17.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.17.adm
@@ -16,8 +16,7 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
+{ "id1": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 15, "add1": null }
 { "id1": 16 }
 { "id1": 17 }
@@ -42,13 +41,12 @@
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 32, "add1": { "state": "ON", "country": "Canada" } }
 { "id1": 33, "add1": { "state": "CO", "country": "USA", "apt": null, "street": null } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 }, "add2": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 } }
+{ "id1": 34, "add1": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 }, "add2": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
+{ "id1": 37, "add1": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.18.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.18.adm
index dd0a0f9..b924e7b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.18.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.18.adm
@@ -16,8 +16,7 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 15, "add1": null }
 { "id1": 16 }
 { "id1": 17 }
@@ -42,13 +41,12 @@
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 32, "add1": { "state": "ON", "country": "Canada" } }
 { "id1": 33, "add1": { "state": "CO", "country": "USA", "street": null, "apt": null } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 }, "add2": { "street": null, "apt": 7, "state": "OR", "zipcode": 97402, "country": "USA" } }
+{ "id1": 34, "add1": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 }, "add2": { "street": "18th", "apt": 8, "state": "AL", "zipcode": 33212, "country": "USA" } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "street": "19th", "state": "IN", "zipcode": 88232, "country": "USA" } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "street": "10th", "apt": null, "state": "MO", "country": "USA" } }
+{ "id1": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.19.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.19.adm
index 7cac215..b97fece 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.19.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.19.adm
@@ -16,8 +16,7 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
+{ "id1": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 15, "add1": null }
 { "id1": 16 }
 { "id1": 17 }
@@ -42,13 +41,12 @@
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 32, "add1": { "state": "ON", "country": "Canada" } }
 { "id1": 33, "add1": { "state": "CO", "country": "USA", "street": null, "apt": null } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 }, "add2": { "state": "OR", "country": "USA", "apt": 7, "street": null, "zipcode": 97402 } }
+{ "id1": 34, "add1": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 }, "add2": { "state": "AL", "country": "USA", "apt": 8, "street": "18th", "zipcode": 33212 } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "apt": null, "street": "10th" } }
+{ "id1": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.20.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.20.adm
index 54f090e..85a61c5 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.20.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_record/hash_join_record.20.adm
@@ -16,8 +16,7 @@
 { "id1": 13, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 13, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 14, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
-{ "id1": 14, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
+{ "id1": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 15, "add1": null }
 { "id1": 16 }
 { "id1": 17 }
@@ -42,13 +41,12 @@
 { "id1": 31, "id2": 31, "add1": { "state": "BC", "country": "Canada" }, "add2": { "state": "BC", "country": "Canada" } }
 { "id1": 32, "add1": { "state": "ON", "country": "Canada" } }
 { "id1": 33, "add1": { "state": "CO", "country": "USA", "street": null, "apt": null } }
-{ "id1": 34, "id2": 34, "add1": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 }, "add2": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 } }
+{ "id1": 34, "add1": { "state": "OR", "country": "USA", "street": null, "apt": 7, "zipcode": 97402 } }
 { "id1": 35, "id2": 35, "add1": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 }, "add2": { "state": "AL", "country": "USA", "street": "18th", "apt": 8, "zipcode": 33212 } }
 { "id1": 36, "id2": 4, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 13, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
 { "id1": 36, "id2": 36, "add1": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 }, "add2": { "state": "IN", "country": "USA", "street": "19th", "zipcode": 88232 } }
-{ "id1": 37, "id2": 14, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
-{ "id1": 37, "id2": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null }, "add2": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
+{ "id1": 37, "add1": { "state": "MO", "country": "USA", "street": "10th", "apt": null } }
 { "id1": 38, "id2": 28, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 38, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
 { "id1": 38, "id2": 41, "add1": { "state": "AL", "country": "USA" }, "add2": { "state": "AL", "country": "USA" } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/sorting/arrays/arrays.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/sorting/arrays/arrays.3.adm
index 3cb68d9..91ede03 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/sorting/arrays/arrays.3.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/sorting/arrays/arrays.3.adm
@@ -16,6 +16,8 @@
 { "id": 18, "name": "Kayle", "dept_ids": [ 8, 2, 1, 7, 9 ] }
 { "id": 7, "name": "Suzanna", "dept_ids": [ 9, 5, 2 ] }
 { "id": 16, "name": "Wail", "dept_ids": [ 9, 5, 2 ] }
+{ "id": 38, "name": "Kim", "dept_ids": [ 33, null, 3, 16 ] }
+{ "id": 45, "name": "Kim", "dept_ids": [ 33, null, 3, 16 ] }
 { "id": 3, "name": "Emory", "dept_ids": [ 33, 11, 3, 16 ] }
 { "id": 8, "name": "Nicole", "dept_ids": [ 33, 11, 3, 16 ] }
 { "id": 2, "name": "Isac", "dept_ids": [ 99, 12, 14, 15, 77 ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/sorting/arrays/arrays.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/sorting/arrays/arrays.5.adm
index efdea4b..c956670 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/sorting/arrays/arrays.5.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/sorting/arrays/arrays.5.adm
@@ -1,6 +1,8 @@
 { "id": 2, "name": "Isac", "dept_ids": [ 99, 12, 14, 15, 77 ] }
 { "id": 3, "name": "Emory", "dept_ids": [ 33, 11, 3, 16 ] }
 { "id": 8, "name": "Nicole", "dept_ids": [ 33, 11, 3, 16 ] }
+{ "id": 38, "name": "Kim", "dept_ids": [ 33, null, 3, 16 ] }
+{ "id": 45, "name": "Kim", "dept_ids": [ 33, null, 3, 16 ] }
 { "id": 7, "name": "Suzanna", "dept_ids": [ 9, 5, 2 ] }
 { "id": 16, "name": "Wail", "dept_ids": [ 9, 5, 2 ] }
 { "id": 18, "name": "Kayle", "dept_ids": [ 8, 2, 1, 7, 9 ] }
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 1d84843..e1becc4 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
@@ -44,6 +44,7 @@ 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;
@@ -219,7 +220,9 @@ public class LogicalComplexBinaryComparator implements ILogicalBinaryComparator
         if (!isEquality) {
             return Result.INCOMPARABLE;
         }
-        return Result.NULL;
+        return ILogicalBinaryComparator
+                .asResult(RawBinaryComparatorFactory.compare(left.getByteArray(), left.getStartOffset(),
+                        left.getLength(), right.getByteArray(), right.getStartOffset(), right.getLength()));
     }
 
     private Result compareRecords(IAType leftType, IPointable left, IAType rightType, IPointable right)
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/formats/nontagged/BinaryBooleanInspector.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/formats/nontagged/BinaryBooleanInspector.java
index 1dfa05b..7bf5f96 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/formats/nontagged/BinaryBooleanInspector.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/formats/nontagged/BinaryBooleanInspector.java
@@ -27,15 +27,16 @@ import org.apache.hyracks.algebricks.data.IBinaryBooleanInspectorFactory;
 import org.apache.hyracks.api.context.IHyracksTaskContext;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 
-public class BinaryBooleanInspector implements IBinaryBooleanInspector {
-    private static final BinaryBooleanInspector INSTANCE = new BinaryBooleanInspector();
+public class BinaryBooleanInspector {
+
     public static final IBinaryBooleanInspectorFactory FACTORY = new IBinaryBooleanInspectorFactory() {
+
         private static final long serialVersionUID = 1L;
 
         @Override
         public IBinaryBooleanInspector createBinaryBooleanInspector(IHyracksTaskContext ctx) {
             // Stateless class. No need to construct an object per call
-            return INSTANCE;
+            return BinaryBooleanInspector::getBooleanValue;
         }
     };
 
@@ -44,14 +45,14 @@ public class BinaryBooleanInspector implements IBinaryBooleanInspector {
     private BinaryBooleanInspector() {
     }
 
-    @Override
-    public boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException {
+    @SuppressWarnings("squid:S1172") // unused parameter
+    public static boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException {
         byte serializedTypeTag = bytes[offset];
         if (serializedTypeTag == ATypeTag.SERIALIZED_MISSING_TYPE_TAG
                 || serializedTypeTag == ATypeTag.SERIALIZED_NULL_TYPE_TAG) {
             return false;
         }
-        /** check if the runtime type is boolean */
+        // check if the runtime type is boolean
         ATypeTag typeTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(serializedTypeTag);
         if (typeTag != ATypeTag.BOOLEAN) {
             throw new RuntimeDataException(ErrorCode.TYPE_MISMATCH_FUNCTION, NAME, 0, ATypeTag.BOOLEAN, typeTag);
@@ -59,5 +60,4 @@ public class BinaryBooleanInspector implements IBinaryBooleanInspector {
 
         return bytes[offset + 1] == 1;
     }
-
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-core/pom.xml b/hyracks-fullstack/algebricks/algebricks-core/pom.xml
index 55aa465..42e30dd 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/pom.xml
+++ b/hyracks-fullstack/algebricks/algebricks-core/pom.xml
@@ -79,11 +79,6 @@
     </dependency>
     <dependency>
       <groupId>org.apache.hyracks</groupId>
-      <artifactId>hyracks-data-std</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.hyracks</groupId>
       <artifactId>hyracks-storage-am-common</artifactId>
       <version>${project.version}</version>
     </dependency>
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/HybridHashJoinPOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/HybridHashJoinPOperator.java
index 8a56fbb..c7c7588 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/HybridHashJoinPOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/HybridHashJoinPOperator.java
@@ -30,7 +30,9 @@ import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
 import org.apache.hyracks.algebricks.core.algebra.base.PhysicalOperatorTag;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionRuntimeProvider;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator.JoinKind;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.IOperatorSchema;
@@ -39,20 +41,15 @@ import org.apache.hyracks.algebricks.core.algebra.properties.IPhysicalProperties
 import org.apache.hyracks.algebricks.core.algebra.properties.LocalGroupingProperty;
 import org.apache.hyracks.algebricks.core.jobgen.impl.JobGenContext;
 import org.apache.hyracks.algebricks.core.jobgen.impl.JobGenHelper;
-import org.apache.hyracks.algebricks.data.IBinaryComparatorFactoryProvider;
-import org.apache.hyracks.api.comm.IFrameTupleAccessor;
-import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.algebricks.runtime.evaluators.TuplePairEvaluatorFactory;
 import org.apache.hyracks.api.dataflow.IOperatorDescriptor;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.IBinaryHashFunctionFamily;
 import org.apache.hyracks.api.dataflow.value.IMissingWriterFactory;
 import org.apache.hyracks.api.dataflow.value.IPredicateEvaluatorFactory;
 import org.apache.hyracks.api.dataflow.value.IPredicateEvaluatorFactoryProvider;
-import org.apache.hyracks.api.dataflow.value.ITuplePairComparator;
 import org.apache.hyracks.api.dataflow.value.ITuplePairComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.RecordDescriptor;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.job.IOperatorDescriptorRegistry;
 import org.apache.hyracks.dataflow.std.join.OptimizedHybridHashJoinOperatorDescriptor;
 import org.apache.logging.log4j.LogManager;
@@ -110,17 +107,6 @@ public class HybridHashJoinPOperator extends AbstractHashJoinPOperator {
                 JobGenHelper.variablesToBinaryHashFunctionFamilies(keysLeftBranch, env, context);
         IBinaryHashFunctionFamily[] rightHashFunFamilies =
                 JobGenHelper.variablesToBinaryHashFunctionFamilies(keysRightBranch, env, context);
-        IBinaryComparatorFactory[] leftCompFactories = new IBinaryComparatorFactory[keysLeft.length];
-        IBinaryComparatorFactory[] rightCompFactories = new IBinaryComparatorFactory[keysRight.length];
-        IBinaryComparatorFactoryProvider bcfp = context.getBinaryComparatorFactoryProvider();
-        Object leftType;
-        Object rightType;
-        for (int i = 0; i < keysLeftBranch.size(); i++) {
-            leftType = env.getVarType(keysLeftBranch.get(i));
-            rightType = env.getVarType(keysRightBranch.get(i));
-            leftCompFactories[i] = bcfp.getBinaryComparatorFactory(leftType, rightType, true);
-            rightCompFactories[i] = bcfp.getBinaryComparatorFactory(rightType, leftType, true);
-        }
 
         IPredicateEvaluatorFactoryProvider predEvaluatorFactoryProvider =
                 context.getPredicateEvaluatorFactoryProvider();
@@ -129,11 +115,22 @@ public class HybridHashJoinPOperator extends AbstractHashJoinPOperator {
 
         RecordDescriptor recDescriptor =
                 JobGenHelper.mkRecordDescriptor(context.getTypeEnvironment(op), propagatedSchema, context);
+        IOperatorSchema[] conditionInputSchemas = new IOperatorSchema[1];
+        conditionInputSchemas[0] = propagatedSchema;
+        IExpressionRuntimeProvider expressionRuntimeProvider = context.getExpressionRuntimeProvider();
+        AbstractBinaryJoinOperator joinOp = (AbstractBinaryJoinOperator) op;
+        IScalarEvaluatorFactory cond = expressionRuntimeProvider.createEvaluatorFactory(
+                joinOp.getCondition().getValue(), context.getTypeEnvironment(op), conditionInputSchemas, context);
+        ITuplePairComparatorFactory comparatorFactory =
+                new TuplePairEvaluatorFactory(cond, false, context.getBinaryBooleanInspectorFactory());
+        ITuplePairComparatorFactory reverseComparatorFactory =
+                new TuplePairEvaluatorFactory(cond, true, context.getBinaryBooleanInspectorFactory());
         IOperatorDescriptorRegistry spec = builder.getJobSpec();
         IOperatorDescriptor opDesc;
 
         opDesc = generateOptimizedHashJoinRuntime(context, inputSchemas, keysLeft, keysRight, leftHashFunFamilies,
-                rightHashFunFamilies, leftCompFactories, rightCompFactories, predEvaluatorFactory, recDescriptor, spec);
+                rightHashFunFamilies, comparatorFactory, reverseComparatorFactory, predEvaluatorFactory, recDescriptor,
+                spec);
         opDesc.setSourceLocation(op.getSourceLocation());
         contributeOpDesc(builder, (AbstractLogicalOperator) op, opDesc);
 
@@ -145,28 +142,24 @@ public class HybridHashJoinPOperator extends AbstractHashJoinPOperator {
 
     private IOperatorDescriptor generateOptimizedHashJoinRuntime(JobGenContext context, IOperatorSchema[] inputSchemas,
             int[] keysLeft, int[] keysRight, IBinaryHashFunctionFamily[] leftHashFunFamilies,
-            IBinaryHashFunctionFamily[] rightHashFunFamilies, IBinaryComparatorFactory[] leftCompFactories,
-            IBinaryComparatorFactory[] rightCompFactories, IPredicateEvaluatorFactory predEvaluatorFactory,
+            IBinaryHashFunctionFamily[] rightHashFunFamilies, ITuplePairComparatorFactory comparatorFactory,
+            ITuplePairComparatorFactory reverseComparatorFactory, IPredicateEvaluatorFactory predEvaluatorFactory,
             RecordDescriptor recDescriptor, IOperatorDescriptorRegistry spec) {
         int memSizeInFrames = localMemoryRequirements.getMemoryBudgetInFrames();
         switch (kind) {
             case INNER:
                 return new OptimizedHybridHashJoinOperatorDescriptor(spec, memSizeInFrames, maxInputBuildSizeInFrames,
-                        getFudgeFactor(), keysLeft, keysRight, leftHashFunFamilies, rightHashFunFamilies,
-                        leftCompFactories, rightCompFactories, recDescriptor,
-                        new JoinMultiComparatorFactory(leftCompFactories, keysLeft, keysRight),
-                        new JoinMultiComparatorFactory(rightCompFactories, keysRight, keysLeft), predEvaluatorFactory);
+                        getFudgeFactor(), keysLeft, keysRight, leftHashFunFamilies, rightHashFunFamilies, recDescriptor,
+                        comparatorFactory, reverseComparatorFactory, predEvaluatorFactory);
             case LEFT_OUTER:
                 IMissingWriterFactory[] nonMatchWriterFactories = new IMissingWriterFactory[inputSchemas[1].getSize()];
                 for (int j = 0; j < nonMatchWriterFactories.length; j++) {
                     nonMatchWriterFactories[j] = context.getMissingWriterFactory();
                 }
                 return new OptimizedHybridHashJoinOperatorDescriptor(spec, memSizeInFrames, maxInputBuildSizeInFrames,
-                        getFudgeFactor(), keysLeft, keysRight, leftHashFunFamilies, rightHashFunFamilies,
-                        leftCompFactories, rightCompFactories, recDescriptor,
-                        new JoinMultiComparatorFactory(leftCompFactories, keysLeft, keysRight),
-                        new JoinMultiComparatorFactory(rightCompFactories, keysRight, keysLeft), predEvaluatorFactory,
-                        true, nonMatchWriterFactories);
+                        getFudgeFactor(), keysLeft, keysRight, leftHashFunFamilies, rightHashFunFamilies, recDescriptor,
+                        comparatorFactory, reverseComparatorFactory, predEvaluatorFactory, true,
+                        nonMatchWriterFactories);
             default:
                 throw new NotImplementedException();
         }
@@ -204,74 +197,4 @@ public class HybridHashJoinPOperator extends AbstractHashJoinPOperator {
         }
         return deliveredLocalProperties;
     }
-
-}
-
-/**
- * {@code ITuplePairComparatorFactory} implementation for optimized hybrid hash join.
- */
-class JoinMultiComparatorFactory implements ITuplePairComparatorFactory {
-    private static final long serialVersionUID = 1L;
-
-    private final IBinaryComparatorFactory[] binaryComparatorFactories;
-    private final int[] keysLeft;
-    private final int[] keysRight;
-
-    JoinMultiComparatorFactory(IBinaryComparatorFactory[] binaryComparatorFactory, int[] keysLeft, int[] keysRight) {
-        this.binaryComparatorFactories = binaryComparatorFactory;
-        this.keysLeft = keysLeft;
-        this.keysRight = keysRight;
-    }
-
-    @Override
-    public ITuplePairComparator createTuplePairComparator(IHyracksTaskContext ctx) {
-        IBinaryComparator[] binaryComparators = new IBinaryComparator[binaryComparatorFactories.length];
-        for (int i = 0; i < binaryComparators.length; i++) {
-            binaryComparators[i] = binaryComparatorFactories[i].createBinaryComparator();
-        }
-        return new JoinMultiComparator(binaryComparators, keysLeft, keysRight);
-    }
-}
-
-/**
- * {@code ITuplePairComparator} implementation for optimized hybrid hash join.
- * The comparator applies multiple binary comparators, one for each key pairs
- */
-class JoinMultiComparator implements ITuplePairComparator {
-    private final IBinaryComparator[] binaryComparators;
-    private final int[] keysLeft;
-    private final int[] keysRight;
-
-    JoinMultiComparator(IBinaryComparator[] bComparator, int[] keysLeft, int[] keysRight) {
-        this.binaryComparators = bComparator;
-        this.keysLeft = keysLeft;
-        this.keysRight = keysRight;
-    }
-
-    @Override
-    public int compare(IFrameTupleAccessor accessor0, int tIndex0, IFrameTupleAccessor accessor1, int tIndex1)
-            throws HyracksDataException {
-        int tStart0 = accessor0.getTupleStartOffset(tIndex0);
-        int fStartOffset0 = accessor0.getFieldSlotsLength() + tStart0;
-
-        int tStart1 = accessor1.getTupleStartOffset(tIndex1);
-        int fStartOffset1 = accessor1.getFieldSlotsLength() + tStart1;
-
-        for (int i = 0; i < binaryComparators.length; i++) {
-            int fStart0 = accessor0.getFieldStartOffset(tIndex0, keysLeft[i]);
-            int fEnd0 = accessor0.getFieldEndOffset(tIndex0, keysLeft[i]);
-            int fLen0 = fEnd0 - fStart0;
-
-            int fStart1 = accessor1.getFieldStartOffset(tIndex1, keysRight[i]);
-            int fEnd1 = accessor1.getFieldEndOffset(tIndex1, keysRight[i]);
-            int fLen1 = fEnd1 - fStart1;
-
-            int c = binaryComparators[i].compare(accessor0.getBuffer().array(), fStart0 + fStartOffset0, fLen0,
-                    accessor1.getBuffer().array(), fStart1 + fStartOffset1, fLen1);
-            if (c != 0) {
-                return c;
-            }
-        }
-        return 0;
-    }
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/InMemoryHashJoinPOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/InMemoryHashJoinPOperator.java
index 90ae4cd..8f911b4 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/InMemoryHashJoinPOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/InMemoryHashJoinPOperator.java
@@ -28,7 +28,9 @@ import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
 import org.apache.hyracks.algebricks.core.algebra.base.PhysicalOperatorTag;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionRuntimeProvider;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator.JoinKind;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.IOperatorSchema;
@@ -36,13 +38,14 @@ import org.apache.hyracks.algebricks.core.algebra.properties.ILocalStructuralPro
 import org.apache.hyracks.algebricks.core.algebra.properties.IPhysicalPropertiesVector;
 import org.apache.hyracks.algebricks.core.jobgen.impl.JobGenContext;
 import org.apache.hyracks.algebricks.core.jobgen.impl.JobGenHelper;
-import org.apache.hyracks.algebricks.data.IBinaryComparatorFactoryProvider;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.algebricks.runtime.evaluators.TuplePairEvaluatorFactory;
 import org.apache.hyracks.api.dataflow.IOperatorDescriptor;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.IBinaryHashFunctionFactory;
 import org.apache.hyracks.api.dataflow.value.IMissingWriterFactory;
 import org.apache.hyracks.api.dataflow.value.IPredicateEvaluatorFactory;
 import org.apache.hyracks.api.dataflow.value.IPredicateEvaluatorFactoryProvider;
+import org.apache.hyracks.api.dataflow.value.ITuplePairComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.RecordDescriptor;
 import org.apache.hyracks.api.job.IOperatorDescriptorRegistry;
 import org.apache.hyracks.dataflow.std.join.InMemoryHashJoinOperatorDescriptor;
@@ -88,15 +91,6 @@ public class InMemoryHashJoinPOperator extends AbstractHashJoinPOperator {
                 JobGenHelper.variablesToBinaryHashFunctionFactories(keysLeftBranch, env, context);
         IBinaryHashFunctionFactory[] rightHashFunFactories =
                 JobGenHelper.variablesToBinaryHashFunctionFactories(keysRightBranch, env, context);
-        IBinaryComparatorFactory[] comparatorFactories = new IBinaryComparatorFactory[keysLeft.length];
-        IBinaryComparatorFactoryProvider bcfp = context.getBinaryComparatorFactoryProvider();
-        Object leftType;
-        Object rightType;
-        for (int i = 0; i < keysLeftBranch.size(); i++) {
-            leftType = env.getVarType(keysLeftBranch.get(i));
-            rightType = env.getVarType(keysRightBranch.get(i));
-            comparatorFactories[i] = bcfp.getBinaryComparatorFactory(leftType, rightType, true);
-        }
 
         IPredicateEvaluatorFactoryProvider predEvaluatorFactoryProvider =
                 context.getPredicateEvaluatorFactoryProvider();
@@ -105,6 +99,14 @@ public class InMemoryHashJoinPOperator extends AbstractHashJoinPOperator {
 
         RecordDescriptor recDescriptor =
                 JobGenHelper.mkRecordDescriptor(context.getTypeEnvironment(op), propagatedSchema, context);
+        IOperatorSchema[] conditionInputSchemas = new IOperatorSchema[1];
+        conditionInputSchemas[0] = propagatedSchema;
+        IExpressionRuntimeProvider expressionRuntimeProvider = context.getExpressionRuntimeProvider();
+        AbstractBinaryJoinOperator joinOp = (AbstractBinaryJoinOperator) op;
+        IScalarEvaluatorFactory cond = expressionRuntimeProvider.createEvaluatorFactory(
+                joinOp.getCondition().getValue(), context.getTypeEnvironment(op), conditionInputSchemas, context);
+        ITuplePairComparatorFactory comparatorFactory =
+                new TuplePairEvaluatorFactory(cond, false, context.getBinaryBooleanInspectorFactory());
         IOperatorDescriptorRegistry spec = builder.getJobSpec();
         IOperatorDescriptor opDesc;
 
@@ -113,7 +115,7 @@ public class InMemoryHashJoinPOperator extends AbstractHashJoinPOperator {
         switch (kind) {
             case INNER:
                 opDesc = new InMemoryHashJoinOperatorDescriptor(spec, keysLeft, keysRight, leftHashFunFactories,
-                        rightHashFunFactories, comparatorFactories, recDescriptor, tableSize, predEvaluatorFactory,
+                        rightHashFunFactories, comparatorFactory, recDescriptor, tableSize, predEvaluatorFactory,
                         memSizeInFrames);
                 break;
             case LEFT_OUTER:
@@ -122,7 +124,7 @@ public class InMemoryHashJoinPOperator extends AbstractHashJoinPOperator {
                     nonMatchWriterFactories[j] = context.getMissingWriterFactory();
                 }
                 opDesc = new InMemoryHashJoinOperatorDescriptor(spec, keysLeft, keysRight, leftHashFunFactories,
-                        rightHashFunFactories, comparatorFactories, predEvaluatorFactory, recDescriptor, true,
+                        rightHashFunFactories, comparatorFactory, predEvaluatorFactory, recDescriptor, true,
                         nonMatchWriterFactories, tableSize, memSizeInFrames);
                 break;
             default:
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/NestedLoopJoinPOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/NestedLoopJoinPOperator.java
index 524b336..0991db8 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/NestedLoopJoinPOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/NestedLoopJoinPOperator.java
@@ -39,23 +39,13 @@ import org.apache.hyracks.algebricks.core.algebra.properties.StructuralPropertie
 import org.apache.hyracks.algebricks.core.algebra.util.OperatorPropertiesUtil;
 import org.apache.hyracks.algebricks.core.jobgen.impl.JobGenContext;
 import org.apache.hyracks.algebricks.core.jobgen.impl.JobGenHelper;
-import org.apache.hyracks.algebricks.data.IBinaryBooleanInspector;
-import org.apache.hyracks.algebricks.data.IBinaryBooleanInspectorFactory;
-import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
-import org.apache.hyracks.api.comm.IFrameTupleAccessor;
-import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.algebricks.runtime.evaluators.TuplePairEvaluatorFactory;
 import org.apache.hyracks.api.dataflow.IOperatorDescriptor;
 import org.apache.hyracks.api.dataflow.value.IMissingWriterFactory;
-import org.apache.hyracks.api.dataflow.value.ITuplePairComparator;
 import org.apache.hyracks.api.dataflow.value.ITuplePairComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.RecordDescriptor;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.job.IOperatorDescriptorRegistry;
-import org.apache.hyracks.data.std.api.IPointable;
-import org.apache.hyracks.data.std.primitive.VoidPointable;
-import org.apache.hyracks.dataflow.common.data.accessors.FrameTupleReference;
-import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
 import org.apache.hyracks.dataflow.std.join.NestedLoopJoinOperatorDescriptor;
 
 /**
@@ -134,7 +124,7 @@ public class NestedLoopJoinPOperator extends AbstractJoinPOperator {
         IScalarEvaluatorFactory cond = expressionRuntimeProvider.createEvaluatorFactory(join.getCondition().getValue(),
                 context.getTypeEnvironment(op), conditionInputSchemas, context);
         ITuplePairComparatorFactory comparatorFactory =
-                new TuplePairEvaluatorFactory(cond, context.getBinaryBooleanInspectorFactory());
+                new TuplePairEvaluatorFactory(cond, false, context.getBinaryBooleanInspectorFactory());
         IOperatorDescriptorRegistry spec = builder.getJobSpec();
         IOperatorDescriptor opDesc;
 
@@ -164,119 +154,4 @@ public class NestedLoopJoinPOperator extends AbstractJoinPOperator {
         ILogicalOperator src2 = op.getInputs().get(1).getValue();
         builder.contributeGraphEdge(src2, 0, op, 1);
     }
-
-    public static class TuplePairEvaluatorFactory implements ITuplePairComparatorFactory {
-
-        private static final long serialVersionUID = 1L;
-        private final IScalarEvaluatorFactory cond;
-        private final IBinaryBooleanInspectorFactory binaryBooleanInspectorFactory;
-
-        public TuplePairEvaluatorFactory(IScalarEvaluatorFactory cond,
-                IBinaryBooleanInspectorFactory binaryBooleanInspectorFactory) {
-            this.cond = cond;
-            this.binaryBooleanInspectorFactory = binaryBooleanInspectorFactory;
-        }
-
-        @Override
-        public synchronized ITuplePairComparator createTuplePairComparator(IHyracksTaskContext ctx)
-                throws HyracksDataException {
-            return new TuplePairEvaluator(ctx, cond, binaryBooleanInspectorFactory.createBinaryBooleanInspector(ctx));
-        }
-    }
-
-    public static class TuplePairEvaluator implements ITuplePairComparator {
-        private IScalarEvaluator condEvaluator;
-        private final IPointable p;
-        private final CompositeFrameTupleReference compositeTupleRef;
-        private final FrameTupleReference leftRef;
-        private final FrameTupleReference rightRef;
-        private final IBinaryBooleanInspector binaryBooleanInspector;
-
-        public TuplePairEvaluator(IHyracksTaskContext ctx, IScalarEvaluatorFactory condFactory,
-                IBinaryBooleanInspector binaryBooleanInspector) throws HyracksDataException {
-            this.condEvaluator = condFactory.createScalarEvaluator(ctx);
-            this.binaryBooleanInspector = binaryBooleanInspector;
-            this.leftRef = new FrameTupleReference();
-            this.p = VoidPointable.FACTORY.createPointable();
-            this.rightRef = new FrameTupleReference();
-            this.compositeTupleRef = new CompositeFrameTupleReference(leftRef, rightRef);
-        }
-
-        @Override
-        public int compare(IFrameTupleAccessor outerAccessor, int outerIndex, IFrameTupleAccessor innerAccessor,
-                int innerIndex) throws HyracksDataException {
-            compositeTupleRef.reset(outerAccessor, outerIndex, innerAccessor, innerIndex);
-            condEvaluator.evaluate(compositeTupleRef, p);
-            boolean result =
-                    binaryBooleanInspector.getBooleanValue(p.getByteArray(), p.getStartOffset(), p.getLength());
-            if (result) {
-                return 0;
-            } else {
-                return 1;
-            }
-        }
-    }
-
-    public static class CompositeFrameTupleReference implements IFrameTupleReference {
-
-        private final FrameTupleReference refLeft;
-        private final FrameTupleReference refRight;
-
-        public CompositeFrameTupleReference(FrameTupleReference refLeft, FrameTupleReference refRight) {
-            this.refLeft = refLeft;
-            this.refRight = refRight;
-        }
-
-        public void reset(IFrameTupleAccessor outerAccessor, int outerIndex, IFrameTupleAccessor innerAccessor,
-                int innerIndex) {
-            refLeft.reset(outerAccessor, outerIndex);
-            refRight.reset(innerAccessor, innerIndex);
-        }
-
-        @Override
-        public int getFieldCount() {
-            return refLeft.getFieldCount() + refRight.getFieldCount();
-        }
-
-        @Override
-        public byte[] getFieldData(int fIdx) {
-            int leftFieldCount = refLeft.getFieldCount();
-            if (fIdx < leftFieldCount) {
-                return refLeft.getFieldData(fIdx);
-            } else {
-                return refRight.getFieldData(fIdx - leftFieldCount);
-            }
-        }
-
-        @Override
-        public int getFieldStart(int fIdx) {
-            int leftFieldCount = refLeft.getFieldCount();
-            if (fIdx < leftFieldCount) {
-                return refLeft.getFieldStart(fIdx);
-            } else {
-                return refRight.getFieldStart(fIdx - leftFieldCount);
-            }
-        }
-
-        @Override
-        public int getFieldLength(int fIdx) {
-            int leftFieldCount = refLeft.getFieldCount();
-            if (fIdx < leftFieldCount) {
-                return refLeft.getFieldLength(fIdx);
-            } else {
-                return refRight.getFieldLength(fIdx - leftFieldCount);
-            }
-        }
-
-        @Override
-        public IFrameTupleAccessor getFrameTupleAccessor() {
-            throw new NotImplementedException();
-        }
-
-        @Override
-        public int getTupleIndex() {
-            throw new NotImplementedException();
-        }
-
-    }
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
index d507e5a..d25ea82 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
@@ -20,6 +20,8 @@ package org.apache.hyracks.algebricks.data;
 
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 
+@FunctionalInterface
 public interface IBinaryBooleanInspector {
-    public boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
+
+    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/impl/BinaryBooleanInspectorImpl.java b/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/impl/BinaryBooleanInspectorImpl.java
index 98b8d66..0b74700 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/impl/BinaryBooleanInspectorImpl.java
+++ b/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/impl/BinaryBooleanInspectorImpl.java
@@ -22,23 +22,24 @@ import org.apache.hyracks.algebricks.data.IBinaryBooleanInspector;
 import org.apache.hyracks.algebricks.data.IBinaryBooleanInspectorFactory;
 import org.apache.hyracks.api.context.IHyracksTaskContext;
 
-public class BinaryBooleanInspectorImpl implements IBinaryBooleanInspector {
-    private static final BinaryBooleanInspectorImpl INSTANCE = new BinaryBooleanInspectorImpl();
+public class BinaryBooleanInspectorImpl {
+
     public static final IBinaryBooleanInspectorFactory FACTORY = new IBinaryBooleanInspectorFactory() {
+
         private static final long serialVersionUID = 1L;
 
         @Override
         public IBinaryBooleanInspector createBinaryBooleanInspector(IHyracksTaskContext ctx) {
             // A stateless class. no need to create an instance per call
-            return INSTANCE;
+            return BinaryBooleanInspectorImpl::getBooleanValue;
         }
     };
 
     private BinaryBooleanInspectorImpl() {
     }
 
-    @Override
-    public boolean getBooleanValue(byte[] bytes, int offset, int length) {
+    @SuppressWarnings("squid:S1172") // unused parameter
+    public static boolean getBooleanValue(byte[] bytes, int offset, int length) {
         return bytes[offset] == 1;
     }
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/evaluators/TuplePairEvaluatorFactory.java b/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/evaluators/TuplePairEvaluatorFactory.java
new file mode 100644
index 0000000..98266b8
--- /dev/null
+++ b/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/evaluators/TuplePairEvaluatorFactory.java
@@ -0,0 +1,150 @@
+/*
+ * 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.hyracks.algebricks.runtime.evaluators;
+
+import org.apache.hyracks.algebricks.common.exceptions.NotImplementedException;
+import org.apache.hyracks.algebricks.data.IBinaryBooleanInspector;
+import org.apache.hyracks.algebricks.data.IBinaryBooleanInspectorFactory;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.comm.IFrameTupleAccessor;
+import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.api.dataflow.value.ITuplePairComparator;
+import org.apache.hyracks.api.dataflow.value.ITuplePairComparatorFactory;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.data.std.api.IPointable;
+import org.apache.hyracks.data.std.primitive.VoidPointable;
+import org.apache.hyracks.dataflow.common.data.accessors.FrameTupleReference;
+import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+import org.apache.hyracks.util.annotations.CriticalPath;
+
+public class TuplePairEvaluatorFactory implements ITuplePairComparatorFactory {
+
+    private static final long serialVersionUID = 1L;
+    private final IScalarEvaluatorFactory condition;
+    private final IBinaryBooleanInspectorFactory booleanInspectorFactory;
+    private final boolean tuplesAreReversed; // whether input tuples are in reverse order of the one in join condition
+
+    public TuplePairEvaluatorFactory(IScalarEvaluatorFactory condition, boolean tuplesAreReversed,
+            IBinaryBooleanInspectorFactory booleanInspectorFactory) {
+        this.condition = condition;
+        this.booleanInspectorFactory = booleanInspectorFactory;
+        this.tuplesAreReversed = tuplesAreReversed;
+    }
+
+    @Override
+    public ITuplePairComparator createTuplePairComparator(IHyracksTaskContext ctx) throws HyracksDataException {
+        return new TuplePairEvaluator(ctx, condition, booleanInspectorFactory.createBinaryBooleanInspector(ctx),
+                tuplesAreReversed);
+    }
+
+    private static class TuplePairEvaluator implements ITuplePairComparator {
+
+        private final IScalarEvaluator conditionEvaluator;
+        private final IPointable res;
+        private final CompositeFrameTupleReference tuplePairRef;
+        private final IBinaryBooleanInspector booleanInspector;
+        private final Reseter reseter;
+
+        TuplePairEvaluator(IHyracksTaskContext ctx, IScalarEvaluatorFactory conditionFactory,
+                IBinaryBooleanInspector booleanInspector, boolean tuplesAreReversed) throws HyracksDataException {
+            this.conditionEvaluator = conditionFactory.createScalarEvaluator(ctx);
+            this.booleanInspector = booleanInspector;
+            this.res = VoidPointable.FACTORY.createPointable();
+            this.tuplePairRef = new CompositeFrameTupleReference(new FrameTupleReference(), new FrameTupleReference());
+            this.reseter = tuplesAreReversed ? TuplePairEvaluator::reverseReset : TuplePairEvaluator::reset;
+        }
+
+        @Override
+        @CriticalPath
+        public int compare(IFrameTupleAccessor leftAccessor, int leftIndex, IFrameTupleAccessor rightAccessor,
+                int rightIndex) throws HyracksDataException {
+            reseter.reset(tuplePairRef, leftAccessor, leftIndex, rightAccessor, rightIndex);
+            conditionEvaluator.evaluate(tuplePairRef, res);
+            return booleanInspector.getBooleanValue(res.getByteArray(), res.getStartOffset(), res.getLength()) ? 0 : 1;
+        }
+
+        private static void reset(CompositeFrameTupleReference ref, IFrameTupleAccessor leftAccessor,
+                int leftTupleIndex, IFrameTupleAccessor rightAccessor, int rightTupleIndex) {
+            ref.reset(leftAccessor, leftTupleIndex, rightAccessor, rightTupleIndex);
+        }
+
+        private static void reverseReset(CompositeFrameTupleReference ref, IFrameTupleAccessor leftAccessor,
+                int leftIndex, IFrameTupleAccessor rightAccessor, int rightIndex) {
+            ref.reset(rightAccessor, rightIndex, leftAccessor, leftIndex);
+        }
+    }
+
+    @FunctionalInterface
+    private interface Reseter {
+        void reset(CompositeFrameTupleReference ref, IFrameTupleAccessor leftAccessor, int leftTupleIndex,
+                IFrameTupleAccessor rightAccessor, int rightTupleIndex);
+    }
+
+    private static class CompositeFrameTupleReference implements IFrameTupleReference {
+
+        private final FrameTupleReference refLeft;
+        private final FrameTupleReference refRight;
+
+        CompositeFrameTupleReference(FrameTupleReference refLeft, FrameTupleReference refRight) {
+            this.refLeft = refLeft;
+            this.refRight = refRight;
+        }
+
+        private void reset(IFrameTupleAccessor leftAccessor, int leftIndex, IFrameTupleAccessor rightAccessor,
+                int rightIndex) {
+            refLeft.reset(leftAccessor, leftIndex);
+            refRight.reset(rightAccessor, rightIndex);
+        }
+
+        @Override
+        public int getFieldCount() {
+            return refLeft.getFieldCount() + refRight.getFieldCount();
+        }
+
+        @Override
+        public byte[] getFieldData(int fIdx) {
+            int leftFieldCount = refLeft.getFieldCount();
+            return fIdx < leftFieldCount ? refLeft.getFieldData(fIdx) : refRight.getFieldData(fIdx - leftFieldCount);
+        }
+
+        @Override
+        public int getFieldStart(int fIdx) {
+            int leftFieldCount = refLeft.getFieldCount();
+            return fIdx < leftFieldCount ? refLeft.getFieldStart(fIdx) : refRight.getFieldStart(fIdx - leftFieldCount);
+        }
+
+        @Override
+        public int getFieldLength(int fIdx) {
+            int leftFieldCount = refLeft.getFieldCount();
+            return fIdx < leftFieldCount ? refLeft.getFieldLength(fIdx)
+                    : refRight.getFieldLength(fIdx - leftFieldCount);
+        }
+
+        @Override
+        public IFrameTupleAccessor getFrameTupleAccessor() {
+            throw new NotImplementedException();
+        }
+
+        @Override
+        public int getTupleIndex() {
+            throw new NotImplementedException();
+        }
+    }
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/dataflow/value/ITuplePairComparator.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/dataflow/value/ITuplePairComparator.java
index 80b9f24..d4a9b8f 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/dataflow/value/ITuplePairComparator.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/dataflow/value/ITuplePairComparator.java
@@ -20,10 +20,12 @@ package org.apache.hyracks.api.dataflow.value;
 
 import org.apache.hyracks.api.comm.IFrameTupleAccessor;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.util.annotations.CriticalPath;
 
 public interface ITuplePairComparator {
 
-    public int compare(IFrameTupleAccessor outerRef, int outerIndex, IFrameTupleAccessor innerRef, int innerIndex)
+    @CriticalPath
+    int compare(IFrameTupleAccessor outerRef, int outerIndex, IFrameTupleAccessor innerRef, int innerIndex)
             throws HyracksDataException;
 
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/HybridHashJoinUtil.java b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/HybridHashJoinUtil.java
new file mode 100644
index 0000000..398dee9
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/HybridHashJoinUtil.java
@@ -0,0 +1,99 @@
+/*
+ * 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.hyracks.dataflow.std.join;
+
+import java.util.BitSet;
+
+import org.apache.hyracks.dataflow.common.io.RunFileWriter;
+import org.apache.hyracks.dataflow.std.buffermanager.IPartitionedTupleBufferManager;
+
+public class HybridHashJoinUtil {
+
+    private HybridHashJoinUtil() {
+    }
+
+    /**
+     * Prints out the detailed information for partitions: in-memory and spilled partitions.
+     * This method exists for a debug purpose.
+     */
+    public String printPartitionInfo(BitSet spilledStatus, OptimizedHybridHashJoin.SIDE whichSide, int numOfPartitions,
+            int[] probePSizeInTups, int[] buildPSizeInTups, RunFileWriter[] probeRFWriters,
+            RunFileWriter[] buildRFWriters, IPartitionedTupleBufferManager bufferManager) {
+        StringBuilder buf = new StringBuilder();
+        buf.append(">>> " + this + " " + Thread.currentThread().getId() + " printInfo():" + "\n");
+        if (whichSide == OptimizedHybridHashJoin.SIDE.BUILD) {
+            buf.append("BUILD side" + "\n");
+        } else {
+            buf.append("PROBE side" + "\n");
+        }
+        buf.append("# of partitions:\t" + numOfPartitions + "\t#spilled:\t" + spilledStatus.cardinality()
+                + "\t#in-memory:\t" + (numOfPartitions - spilledStatus.cardinality()) + "\n");
+        buf.append("(A) Spilled partitions" + "\n");
+        int spilledTupleCount = 0;
+        int spilledPartByteSize = 0;
+        for (int pid = spilledStatus.nextSetBit(0); pid >= 0 && pid < numOfPartitions; pid =
+                spilledStatus.nextSetBit(pid + 1)) {
+            if (whichSide == OptimizedHybridHashJoin.SIDE.BUILD) {
+                spilledTupleCount += buildPSizeInTups[pid];
+                spilledPartByteSize += buildRFWriters[pid].getFileSize();
+                buf.append("part:\t" + pid + "\t#tuple:\t" + buildPSizeInTups[pid] + "\tsize(MB):\t"
+                        + ((double) buildRFWriters[pid].getFileSize() / 1048576) + "\n");
+            } else {
+                spilledTupleCount += probePSizeInTups[pid];
+                spilledPartByteSize += probeRFWriters[pid].getFileSize();
+            }
+        }
+        if (spilledStatus.cardinality() > 0) {
+            buf.append("# of spilled tuples:\t" + spilledTupleCount + "\tsize(MB):\t"
+                    + ((double) spilledPartByteSize / 1048576) + "avg #tuples per spilled part:\t"
+                    + (spilledTupleCount / spilledStatus.cardinality()) + "\tavg size per part(MB):\t"
+                    + ((double) spilledPartByteSize / 1048576 / spilledStatus.cardinality()) + "\n");
+        }
+        buf.append("(B) In-memory partitions" + "\n");
+        int inMemoryTupleCount = 0;
+        int inMemoryPartByteSize = 0;
+        for (int pid = spilledStatus.nextClearBit(0); pid >= 0 && pid < numOfPartitions; pid =
+                spilledStatus.nextClearBit(pid + 1)) {
+            if (whichSide == OptimizedHybridHashJoin.SIDE.BUILD) {
+                inMemoryTupleCount += buildPSizeInTups[pid];
+                inMemoryPartByteSize += bufferManager.getPhysicalSize(pid);
+            } else {
+                inMemoryTupleCount += probePSizeInTups[pid];
+                inMemoryPartByteSize += bufferManager.getPhysicalSize(pid);
+            }
+        }
+        if (spilledStatus.cardinality() > 0) {
+            buf.append("# of in-memory tuples:\t" + inMemoryTupleCount + "\tsize(MB):\t"
+                    + ((double) inMemoryPartByteSize / 1048576) + "avg #tuples per spilled part:\t"
+                    + (inMemoryTupleCount / spilledStatus.cardinality()) + "\tavg size per part(MB):\t"
+                    + ((double) inMemoryPartByteSize / 1048576 / (numOfPartitions - spilledStatus.cardinality()))
+                    + "\n");
+        }
+        if (inMemoryTupleCount + spilledTupleCount > 0) {
+            buf.append("# of all tuples:\t" + (inMemoryTupleCount + spilledTupleCount) + "\tsize(MB):\t"
+                    + ((double) (inMemoryPartByteSize + spilledPartByteSize) / 1048576) + " ratio of spilled tuples:\t"
+                    + (spilledTupleCount / (inMemoryTupleCount + spilledTupleCount)) + "\n");
+        } else {
+            buf.append("# of all tuples:\t" + (inMemoryTupleCount + spilledTupleCount) + "\tsize(MB):\t"
+                    + ((double) (inMemoryPartByteSize + spilledPartByteSize) / 1048576) + " ratio of spilled tuples:\t"
+                    + "N/A" + "\n");
+        }
+        return buf.toString();
+    }
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/InMemoryHashJoin.java b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/InMemoryHashJoin.java
index 89c370f..eadbcf7 100644
--- a/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/InMemoryHashJoin.java
+++ b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/InMemoryHashJoin.java
@@ -29,6 +29,7 @@ import org.apache.hyracks.api.comm.VSizeFrame;
 import org.apache.hyracks.api.context.IHyracksTaskContext;
 import org.apache.hyracks.api.dataflow.value.IMissingWriter;
 import org.apache.hyracks.api.dataflow.value.IPredicateEvaluator;
+import org.apache.hyracks.api.dataflow.value.ITuplePairComparator;
 import org.apache.hyracks.api.dataflow.value.ITuplePartitionComputer;
 import org.apache.hyracks.api.dataflow.value.RecordDescriptor;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -40,7 +41,6 @@ import org.apache.hyracks.dataflow.std.buffermanager.ISimpleFrameBufferManager;
 import org.apache.hyracks.dataflow.std.buffermanager.TupleInFrameListAccessor;
 import org.apache.hyracks.dataflow.std.structures.ISerializableTable;
 import org.apache.hyracks.dataflow.std.structures.TuplePointer;
-import org.apache.hyracks.dataflow.std.util.FrameTuplePairComparator;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -52,7 +52,7 @@ public class InMemoryHashJoin {
     private IFrameTupleAccessor accessorProbe;
     private final ITuplePartitionComputer tpcProbe;
     private final FrameTupleAppender appender;
-    private final FrameTuplePairComparator tpComparator;
+    private final ITuplePairComparator tpComparator;
     private final boolean isLeftOuter;
     private final ArrayTupleBuilder missingTupleBuild;
     private final ISerializableTable table;
@@ -68,7 +68,7 @@ public class InMemoryHashJoin {
 
     public InMemoryHashJoin(IHyracksTaskContext ctx, FrameTupleAccessor accessorProbe, ITuplePartitionComputer tpcProbe,
             FrameTupleAccessor accessorBuild, RecordDescriptor rDBuild, ITuplePartitionComputer tpcBuild,
-            FrameTuplePairComparator comparator, boolean isLeftOuter, IMissingWriter[] missingWritersBuild,
+            ITuplePairComparator comparator, boolean isLeftOuter, IMissingWriter[] missingWritersBuild,
             ISerializableTable table, IPredicateEvaluator predEval, ISimpleFrameBufferManager bufferManager)
             throws HyracksDataException {
         this(ctx, accessorProbe, tpcProbe, accessorBuild, rDBuild, tpcBuild, comparator, isLeftOuter,
@@ -77,7 +77,7 @@ public class InMemoryHashJoin {
 
     public InMemoryHashJoin(IHyracksTaskContext ctx, FrameTupleAccessor accessorProbe, ITuplePartitionComputer tpcProbe,
             FrameTupleAccessor accessorBuild, RecordDescriptor rDBuild, ITuplePartitionComputer tpcBuild,
-            FrameTuplePairComparator comparator, boolean isLeftOuter, IMissingWriter[] missingWritersBuild,
+            ITuplePairComparator comparator, boolean isLeftOuter, IMissingWriter[] missingWritersBuild,
             ISerializableTable table, IPredicateEvaluator predEval, boolean reverse,
             ISimpleFrameBufferManager bufferManager) throws HyracksDataException {
         this.table = table;
diff --git a/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/InMemoryHashJoinOperatorDescriptor.java b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/InMemoryHashJoinOperatorDescriptor.java
index 9ef36cf..ccca62d 100644
--- a/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/InMemoryHashJoinOperatorDescriptor.java
+++ b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/InMemoryHashJoinOperatorDescriptor.java
@@ -28,14 +28,14 @@ import org.apache.hyracks.api.dataflow.ActivityId;
 import org.apache.hyracks.api.dataflow.IActivityGraphBuilder;
 import org.apache.hyracks.api.dataflow.IOperatorNodePushable;
 import org.apache.hyracks.api.dataflow.TaskId;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.IBinaryHashFunctionFactory;
 import org.apache.hyracks.api.dataflow.value.IMissingWriter;
 import org.apache.hyracks.api.dataflow.value.IMissingWriterFactory;
 import org.apache.hyracks.api.dataflow.value.IPredicateEvaluator;
 import org.apache.hyracks.api.dataflow.value.IPredicateEvaluatorFactory;
 import org.apache.hyracks.api.dataflow.value.IRecordDescriptorProvider;
+import org.apache.hyracks.api.dataflow.value.ITuplePairComparator;
+import org.apache.hyracks.api.dataflow.value.ITuplePairComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.ITuplePartitionComputer;
 import org.apache.hyracks.api.dataflow.value.RecordDescriptor;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -55,7 +55,6 @@ import org.apache.hyracks.dataflow.std.buffermanager.IDeallocatableFramePool;
 import org.apache.hyracks.dataflow.std.buffermanager.ISimpleFrameBufferManager;
 import org.apache.hyracks.dataflow.std.structures.ISerializableTable;
 import org.apache.hyracks.dataflow.std.structures.SerializableHashTable;
-import org.apache.hyracks.dataflow.std.util.FrameTuplePairComparator;
 
 public class InMemoryHashJoinOperatorDescriptor extends AbstractOperatorDescriptor {
     private static final long serialVersionUID = 1L;
@@ -63,7 +62,7 @@ public class InMemoryHashJoinOperatorDescriptor extends AbstractOperatorDescript
     private final int[] keys1;
     private final IBinaryHashFunctionFactory[] hashFunctionFactories0;
     private final IBinaryHashFunctionFactory[] hashFunctionFactories1;
-    private final IBinaryComparatorFactory[] comparatorFactories;
+    private final ITuplePairComparatorFactory comparatorFactory;
     private final IPredicateEvaluatorFactory predEvaluatorFactory;
     private final boolean isLeftOuter;
     private final IMissingWriterFactory[] nonMatchWriterFactories;
@@ -73,14 +72,14 @@ public class InMemoryHashJoinOperatorDescriptor extends AbstractOperatorDescript
 
     public InMemoryHashJoinOperatorDescriptor(IOperatorDescriptorRegistry spec, int[] keys0, int[] keys1,
             IBinaryHashFunctionFactory[] hashFunctionFactories0, IBinaryHashFunctionFactory[] hashFunctionFactories1,
-            IBinaryComparatorFactory[] comparatorFactories, RecordDescriptor recordDescriptor, int tableSize,
+            ITuplePairComparatorFactory comparatorFactory, RecordDescriptor recordDescriptor, int tableSize,
             IPredicateEvaluatorFactory predEvalFactory, int memSizeInFrames) {
         super(spec, 2, 1);
         this.keys0 = keys0;
         this.keys1 = keys1;
         this.hashFunctionFactories0 = hashFunctionFactories0;
         this.hashFunctionFactories1 = hashFunctionFactories1;
-        this.comparatorFactories = comparatorFactories;
+        this.comparatorFactory = comparatorFactory;
         this.predEvaluatorFactory = predEvalFactory;
         outRecDescs[0] = recordDescriptor;
         this.isLeftOuter = false;
@@ -91,7 +90,7 @@ public class InMemoryHashJoinOperatorDescriptor extends AbstractOperatorDescript
 
     public InMemoryHashJoinOperatorDescriptor(IOperatorDescriptorRegistry spec, int[] keys0, int[] keys1,
             IBinaryHashFunctionFactory[] hashFunctionFactories0, IBinaryHashFunctionFactory[] hashFunctionFactories1,
-            IBinaryComparatorFactory[] comparatorFactories, IPredicateEvaluatorFactory predEvalFactory,
+            ITuplePairComparatorFactory comparatorFactory, IPredicateEvaluatorFactory predEvalFactory,
             RecordDescriptor recordDescriptor, boolean isLeftOuter, IMissingWriterFactory[] missingWriterFactories1,
             int tableSize, int memSizeInFrames) {
         super(spec, 2, 1);
@@ -99,7 +98,7 @@ public class InMemoryHashJoinOperatorDescriptor extends AbstractOperatorDescript
         this.keys1 = keys1;
         this.hashFunctionFactories0 = hashFunctionFactories0;
         this.hashFunctionFactories1 = hashFunctionFactories1;
-        this.comparatorFactories = comparatorFactories;
+        this.comparatorFactory = comparatorFactory;
         this.predEvaluatorFactory = predEvalFactory;
         outRecDescs[0] = recordDescriptor;
         this.isLeftOuter = isLeftOuter;
@@ -159,13 +158,11 @@ public class InMemoryHashJoinOperatorDescriptor extends AbstractOperatorDescript
 
         @Override
         public IOperatorNodePushable createPushRuntime(final IHyracksTaskContext ctx,
-                IRecordDescriptorProvider recordDescProvider, final int partition, int nPartitions) {
+                IRecordDescriptorProvider recordDescProvider, final int partition, int nPartitions)
+                throws HyracksDataException {
             final RecordDescriptor rd0 = recordDescProvider.getInputRecordDescriptor(hpaId, 0);
             final RecordDescriptor rd1 = recordDescProvider.getInputRecordDescriptor(getActivityId(), 0);
-            final IBinaryComparator[] comparators = new IBinaryComparator[comparatorFactories.length];
-            for (int i = 0; i < comparatorFactories.length; ++i) {
-                comparators[i] = comparatorFactories[i].createBinaryComparator();
-            }
+            final ITuplePairComparator comparator = comparatorFactory.createTuplePairComparator(ctx);
             final IMissingWriter[] nullWriters1 =
                     isLeftOuter ? new IMissingWriter[nonMatchWriterFactories.length] : null;
             if (isLeftOuter) {
@@ -192,10 +189,9 @@ public class InMemoryHashJoinOperatorDescriptor extends AbstractOperatorDescript
                     state = new HashBuildTaskState(ctx.getJobletContext().getJobId(),
                             new TaskId(getActivityId(), partition));
                     ISerializableTable table = new SerializableHashTable(tableSize, ctx, bufferManager);
-                    state.joiner =
-                            new InMemoryHashJoin(ctx, new FrameTupleAccessor(rd0), hpc0, new FrameTupleAccessor(rd1),
-                                    rd1, hpc1, new FrameTuplePairComparator(keys0, keys1, comparators), isLeftOuter,
-                                    nullWriters1, table, predEvaluator, bufferManager);
+                    state.joiner = new InMemoryHashJoin(ctx, new FrameTupleAccessor(rd0), hpc0,
+                            new FrameTupleAccessor(rd1), rd1, hpc1, comparator, isLeftOuter, nullWriters1, table,
+                            predEvaluator, bufferManager);
                 }
 
                 @Override
diff --git a/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/OptimizedHybridHashJoin.java b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/OptimizedHybridHashJoin.java
index ddf1741..c78e0dc 100644
--- a/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/OptimizedHybridHashJoin.java
+++ b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/OptimizedHybridHashJoin.java
@@ -25,10 +25,10 @@ import org.apache.hyracks.api.comm.IFrame;
 import org.apache.hyracks.api.comm.IFrameWriter;
 import org.apache.hyracks.api.comm.VSizeFrame;
 import org.apache.hyracks.api.context.IHyracksTaskContext;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
 import org.apache.hyracks.api.dataflow.value.IMissingWriter;
 import org.apache.hyracks.api.dataflow.value.IMissingWriterFactory;
 import org.apache.hyracks.api.dataflow.value.IPredicateEvaluator;
+import org.apache.hyracks.api.dataflow.value.ITuplePairComparator;
 import org.apache.hyracks.api.dataflow.value.ITuplePartitionComputer;
 import org.apache.hyracks.api.dataflow.value.RecordDescriptor;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -47,7 +47,6 @@ import org.apache.hyracks.dataflow.std.buffermanager.VPartitionTupleBufferManage
 import org.apache.hyracks.dataflow.std.structures.ISerializableTable;
 import org.apache.hyracks.dataflow.std.structures.SerializableHashTable;
 import org.apache.hyracks.dataflow.std.structures.TuplePointer;
-import org.apache.hyracks.dataflow.std.util.FrameTuplePairComparator;
 
 /**
  * This class mainly applies one level of HHJ on a pair of
@@ -58,7 +57,7 @@ public class OptimizedHybridHashJoin {
     // Used for special probe BigObject which can not be held into the Join memory
     private FrameTupleAppender bigProbeFrameAppender;
 
-    enum SIDE {
+    public enum SIDE {
         BUILD,
         PROBE
     }
@@ -68,11 +67,7 @@ public class OptimizedHybridHashJoin {
     private final String buildRelName;
     private final String probeRelName;
 
-    private final int[] buildKeys;
-    private final int[] probeKeys;
-
-    private final IBinaryComparator[] comparators;
-
+    private final ITuplePairComparator comparator;
     private final ITuplePartitionComputer buildHpc;
     private final ITuplePartitionComputer probeHpc;
 
@@ -110,19 +105,16 @@ public class OptimizedHybridHashJoin {
     private int[] probePSizeInTups;
 
     public OptimizedHybridHashJoin(IHyracksTaskContext ctx, int memSizeInFrames, int numOfPartitions,
-            String probeRelName, String buildRelName, int[] probeKeys, int[] buildKeys, IBinaryComparator[] comparators,
-            RecordDescriptor probeRd, RecordDescriptor buildRd, ITuplePartitionComputer probeHpc,
-            ITuplePartitionComputer buildHpc, IPredicateEvaluator predEval, boolean isLeftOuter,
-            IMissingWriterFactory[] nullWriterFactories1) {
+            String probeRelName, String buildRelName, ITuplePairComparator comparator, RecordDescriptor probeRd,
+            RecordDescriptor buildRd, ITuplePartitionComputer probeHpc, ITuplePartitionComputer buildHpc,
+            IPredicateEvaluator predEval, boolean isLeftOuter, IMissingWriterFactory[] nullWriterFactories1) {
         this.ctx = ctx;
         this.memSizeInFrames = memSizeInFrames;
         this.buildRd = buildRd;
         this.probeRd = probeRd;
         this.buildHpc = buildHpc;
         this.probeHpc = probeHpc;
-        this.buildKeys = buildKeys;
-        this.probeKeys = probeKeys;
-        this.comparators = comparators;
+        this.comparator = comparator;
         this.buildRelName = buildRelName;
         this.probeRelName = probeRelName;
 
@@ -450,10 +442,9 @@ public class OptimizedHybridHashJoin {
 
     private void createInMemoryJoiner(int inMemTupCount) throws HyracksDataException {
         ISerializableTable table = new SerializableHashTable(inMemTupCount, ctx, bufferManagerForHashTable);
-        this.inMemJoiner =
-                new InMemoryHashJoin(ctx, new FrameTupleAccessor(probeRd), probeHpc, new FrameTupleAccessor(buildRd),
-                        buildRd, buildHpc, new FrameTuplePairComparator(probeKeys, buildKeys, comparators), isLeftOuter,
-                        nonMatchWriters, table, predEvaluator, isReversed, bufferManagerForHashTable);
+        this.inMemJoiner = new InMemoryHashJoin(ctx, new FrameTupleAccessor(probeRd), probeHpc,
+                new FrameTupleAccessor(buildRd), buildRd, buildHpc, comparator, isLeftOuter, nonMatchWriters, table,
+                predEvaluator, isReversed, bufferManagerForHashTable);
     }
 
     private void loadDataInMemJoin() throws HyracksDataException {
@@ -612,71 +603,4 @@ public class OptimizedHybridHashJoin {
     public void setIsReversed(boolean b) {
         this.isReversed = b;
     }
-
-    /**
-     * Prints out the detailed information for partitions: in-memory and spilled partitions.
-     * This method exists for a debug purpose.
-     */
-    public String printPartitionInfo(SIDE whichSide) {
-        StringBuilder buf = new StringBuilder();
-        buf.append(">>> " + this + " " + Thread.currentThread().getId() + " printInfo():" + "\n");
-        if (whichSide == SIDE.BUILD) {
-            buf.append("BUILD side" + "\n");
-        } else {
-            buf.append("PROBE side" + "\n");
-        }
-        buf.append("# of partitions:\t" + numOfPartitions + "\t#spilled:\t" + spilledStatus.cardinality()
-                + "\t#in-memory:\t" + (numOfPartitions - spilledStatus.cardinality()) + "\n");
-        buf.append("(A) Spilled partitions" + "\n");
-        int spilledTupleCount = 0;
-        int spilledPartByteSize = 0;
-        for (int pid = spilledStatus.nextSetBit(0); pid >= 0 && pid < numOfPartitions; pid =
-                spilledStatus.nextSetBit(pid + 1)) {
-            if (whichSide == SIDE.BUILD) {
-                spilledTupleCount += buildPSizeInTups[pid];
-                spilledPartByteSize += buildRFWriters[pid].getFileSize();
-                buf.append("part:\t" + pid + "\t#tuple:\t" + buildPSizeInTups[pid] + "\tsize(MB):\t"
-                        + ((double) buildRFWriters[pid].getFileSize() / 1048576) + "\n");
-            } else {
-                spilledTupleCount += probePSizeInTups[pid];
-                spilledPartByteSize += probeRFWriters[pid].getFileSize();
-            }
-        }
-        if (spilledStatus.cardinality() > 0) {
-            buf.append("# of spilled tuples:\t" + spilledTupleCount + "\tsize(MB):\t"
-                    + ((double) spilledPartByteSize / 1048576) + "avg #tuples per spilled part:\t"
-                    + (spilledTupleCount / spilledStatus.cardinality()) + "\tavg size per part(MB):\t"
-                    + ((double) spilledPartByteSize / 1048576 / spilledStatus.cardinality()) + "\n");
-        }
-        buf.append("(B) In-memory partitions" + "\n");
-        int inMemoryTupleCount = 0;
-        int inMemoryPartByteSize = 0;
-        for (int pid = spilledStatus.nextClearBit(0); pid >= 0 && pid < numOfPartitions; pid =
-                spilledStatus.nextClearBit(pid + 1)) {
-            if (whichSide == SIDE.BUILD) {
-                inMemoryTupleCount += buildPSizeInTups[pid];
-                inMemoryPartByteSize += bufferManager.getPhysicalSize(pid);
-            } else {
-                inMemoryTupleCount += probePSizeInTups[pid];
-                inMemoryPartByteSize += bufferManager.getPhysicalSize(pid);
-            }
-        }
-        if (spilledStatus.cardinality() > 0) {
-            buf.append("# of in-memory tuples:\t" + inMemoryTupleCount + "\tsize(MB):\t"
-                    + ((double) inMemoryPartByteSize / 1048576) + "avg #tuples per spilled part:\t"
-                    + (inMemoryTupleCount / spilledStatus.cardinality()) + "\tavg size per part(MB):\t"
-                    + ((double) inMemoryPartByteSize / 1048576 / (numOfPartitions - spilledStatus.cardinality()))
-                    + "\n");
-        }
-        if (inMemoryTupleCount + spilledTupleCount > 0) {
-            buf.append("# of all tuples:\t" + (inMemoryTupleCount + spilledTupleCount) + "\tsize(MB):\t"
-                    + ((double) (inMemoryPartByteSize + spilledPartByteSize) / 1048576) + " ratio of spilled tuples:\t"
-                    + (spilledTupleCount / (inMemoryTupleCount + spilledTupleCount)) + "\n");
-        } else {
-            buf.append("# of all tuples:\t" + (inMemoryTupleCount + spilledTupleCount) + "\tsize(MB):\t"
-                    + ((double) (inMemoryPartByteSize + spilledPartByteSize) / 1048576) + " ratio of spilled tuples:\t"
-                    + "N/A" + "\n");
-        }
-        return buf.toString();
-    }
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/OptimizedHybridHashJoinOperatorDescriptor.java b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/OptimizedHybridHashJoinOperatorDescriptor.java
index 81d08b2..2fd17da 100644
--- a/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/OptimizedHybridHashJoinOperatorDescriptor.java
+++ b/hyracks-fullstack/hyracks/hyracks-dataflow-std/src/main/java/org/apache/hyracks/dataflow/std/join/OptimizedHybridHashJoinOperatorDescriptor.java
@@ -31,8 +31,6 @@ import org.apache.hyracks.api.dataflow.ActivityId;
 import org.apache.hyracks.api.dataflow.IActivityGraphBuilder;
 import org.apache.hyracks.api.dataflow.IOperatorNodePushable;
 import org.apache.hyracks.api.dataflow.TaskId;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.IBinaryHashFunctionFamily;
 import org.apache.hyracks.api.dataflow.value.IMissingWriter;
 import org.apache.hyracks.api.dataflow.value.IMissingWriterFactory;
@@ -63,7 +61,6 @@ import org.apache.hyracks.dataflow.std.buffermanager.IDeallocatableFramePool;
 import org.apache.hyracks.dataflow.std.buffermanager.ISimpleFrameBufferManager;
 import org.apache.hyracks.dataflow.std.structures.ISerializableTable;
 import org.apache.hyracks.dataflow.std.structures.SerializableHashTable;
-import org.apache.hyracks.dataflow.std.util.FrameTuplePairComparator;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -125,10 +122,8 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
     private final int[] buildKeys;
     private final IBinaryHashFunctionFamily[] propHashFunctionFactories;
     private final IBinaryHashFunctionFamily[] buildHashFunctionFactories;
-    private final IBinaryComparatorFactory[] probCompFactories; //For in-mem HJ
-    private final IBinaryComparatorFactory[] buildCompFactories; //For in-mem HJ
-    private final ITuplePairComparatorFactory tuplePairComparatorFactoryProbe2Build; //For NLJ in probe
-    private final ITuplePairComparatorFactory tuplePairComparatorFactoryBuild2Probe; //For NLJ in probe
+    private final ITuplePairComparatorFactory tuplePairComparatorFactoryProbe2Build; //For HHJ & NLJ in probe
+    private final ITuplePairComparatorFactory tuplePairComparatorFactoryBuild2Probe; //For HHJ & NLJ in probe
     private final IPredicateEvaluatorFactory predEvaluatorFactory;
 
     private final boolean isLeftOuter;
@@ -144,8 +139,7 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
     public OptimizedHybridHashJoinOperatorDescriptor(IOperatorDescriptorRegistry spec, int memSizeInFrames,
             int inputsize0, double factor, int[] keys0, int[] keys1,
             IBinaryHashFunctionFamily[] propHashFunctionFactories,
-            IBinaryHashFunctionFamily[] buildHashFunctionFactories, IBinaryComparatorFactory[] probCompFactories,
-            IBinaryComparatorFactory[] buildCompFactories, RecordDescriptor recordDescriptor,
+            IBinaryHashFunctionFamily[] buildHashFunctionFactories, RecordDescriptor recordDescriptor,
             ITuplePairComparatorFactory tupPaircomparatorFactory01,
             ITuplePairComparatorFactory tupPaircomparatorFactory10, IPredicateEvaluatorFactory predEvaluatorFactory,
             boolean isLeftOuter, IMissingWriterFactory[] nonMatchWriterFactories) {
@@ -157,8 +151,6 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
         this.buildKeys = keys1;
         this.propHashFunctionFactories = propHashFunctionFactories;
         this.buildHashFunctionFactories = buildHashFunctionFactories;
-        this.probCompFactories = probCompFactories;
-        this.buildCompFactories = buildCompFactories;
         this.tuplePairComparatorFactoryProbe2Build = tupPaircomparatorFactory01;
         this.tuplePairComparatorFactoryBuild2Probe = tupPaircomparatorFactory10;
         outRecDescs[0] = recordDescriptor;
@@ -170,13 +162,12 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
     public OptimizedHybridHashJoinOperatorDescriptor(IOperatorDescriptorRegistry spec, int memSizeInFrames,
             int inputsize0, double factor, int[] keys0, int[] keys1,
             IBinaryHashFunctionFamily[] propHashFunctionFactories,
-            IBinaryHashFunctionFamily[] buildHashFunctionFactories, IBinaryComparatorFactory[] probCompFactories,
-            IBinaryComparatorFactory[] buildCompFactories, RecordDescriptor recordDescriptor,
+            IBinaryHashFunctionFamily[] buildHashFunctionFactories, RecordDescriptor recordDescriptor,
             ITuplePairComparatorFactory tupPaircomparatorFactory01,
             ITuplePairComparatorFactory tupPaircomparatorFactory10, IPredicateEvaluatorFactory predEvaluatorFactory) {
         this(spec, memSizeInFrames, inputsize0, factor, keys0, keys1, propHashFunctionFactories,
-                buildHashFunctionFactories, probCompFactories, buildCompFactories, recordDescriptor,
-                tupPaircomparatorFactory01, tupPaircomparatorFactory10, predEvaluatorFactory, false, null);
+                buildHashFunctionFactories, recordDescriptor, tupPaircomparatorFactory01, tupPaircomparatorFactory10,
+                predEvaluatorFactory, false, null);
     }
 
     @Override
@@ -246,11 +237,10 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
 
     }
 
-    /*
+    /**
      * Build phase of Hybrid Hash Join:
-     * Creating an instance of Hybrid Hash Join, using Shapiro's formula
-     * to get the optimal number of partitions, build relation is read and
-     * partitioned, and hybrid hash join instance gets ready for the probing.
+     * Creating an instance of Hybrid Hash Join, using Shapiro's formula to get the optimal number of partitions, build
+     * relation is read and partitioned, and hybrid hash join instance gets ready for the probing.
      * (See OptimizedHybridHashJoin for the details on different steps)
      */
     private class PartitionAndBuildActivityNode extends AbstractActivityNode {
@@ -265,20 +255,17 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
 
         @Override
         public IOperatorNodePushable createPushRuntime(final IHyracksTaskContext ctx,
-                IRecordDescriptorProvider recordDescProvider, final int partition, final int nPartitions) {
+                IRecordDescriptorProvider recordDescProvider, final int partition, final int nPartitions)
+                throws HyracksDataException {
 
             final RecordDescriptor buildRd = recordDescProvider.getInputRecordDescriptor(getActivityId(), 0);
             final RecordDescriptor probeRd = recordDescProvider.getInputRecordDescriptor(probeAid, 0);
-
-            final IBinaryComparator[] probComparators = new IBinaryComparator[probCompFactories.length];
-            for (int i = 0; i < probCompFactories.length; i++) {
-                probComparators[i] = probCompFactories[i].createBinaryComparator();
-            }
-
+            final ITuplePairComparator probComparator =
+                    tuplePairComparatorFactoryProbe2Build.createTuplePairComparator(ctx);
             final IPredicateEvaluator predEvaluator =
                     (predEvaluatorFactory == null ? null : predEvaluatorFactory.createPredicateEvaluator());
 
-            IOperatorNodePushable op = new AbstractUnaryInputSinkOperatorNodePushable() {
+            return new AbstractUnaryInputSinkOperatorNodePushable() {
                 private BuildAndPartitionTaskState state = new BuildAndPartitionTaskState(
                         ctx.getJobletContext().getJobId(), new TaskId(getActivityId(), partition));
 
@@ -298,8 +285,8 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
                     state.numOfPartitions =
                             getNumberOfPartitions(state.memForJoin, inputsize0, fudgeFactor, nPartitions);
                     state.hybridHJ = new OptimizedHybridHashJoin(ctx, state.memForJoin, state.numOfPartitions,
-                            PROBE_REL, BUILD_REL, probeKeys, buildKeys, probComparators, probeRd, buildRd, probeHpc,
-                            buildHpc, predEvaluator, isLeftOuter, nonMatchWriterFactories);
+                            PROBE_REL, BUILD_REL, probComparator, probeRd, buildRd, probeHpc, buildHpc, predEvaluator,
+                            isLeftOuter, nonMatchWriterFactories);
 
                     state.hybridHJ.initBuild();
                     if (LOGGER.isTraceEnabled()) {
@@ -334,17 +321,15 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
                 }
 
             };
-            return op;
         }
     }
 
-    /*
+    /**
      * Probe phase of Hybrid Hash Join:
-     * Reading the probe side and partitioning it, resident tuples get
-     * joined with the build side residents (through formerly created HybridHashJoin in the build phase)
-     * and spilled partitions get written to run files. During the close() call, pairs of spilled partition
-     * (build side spilled partition and its corresponding probe side spilled partition) join, by applying
-     * Hybrid Hash Join recursively on them.
+     * Reading the probe side and partitioning it, resident tuples get joined with the build side residents (through
+     * formerly created HybridHashJoin in the build phase) and spilled partitions get written to run files. During
+     * the close() call, pairs of spilled partition (build side spilled partition and its corresponding probe side
+     * spilled partition) join, by applying Hybrid Hash Join recursively on them.
      */
     private class ProbeAndJoinActivityNode extends AbstractActivityNode {
 
@@ -364,21 +349,11 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
 
             final RecordDescriptor buildRd = recordDescProvider.getInputRecordDescriptor(buildAid, 0);
             final RecordDescriptor probeRd = recordDescProvider.getInputRecordDescriptor(getActivityId(), 0);
-            final IBinaryComparator[] probComp = new IBinaryComparator[probCompFactories.length];
-            final IBinaryComparator[] buildComp = new IBinaryComparator[buildCompFactories.length];
-            final ITuplePairComparator nljComparatorProbe2Build =
-                    tuplePairComparatorFactoryProbe2Build.createTuplePairComparator(ctx);
-            final ITuplePairComparator nljComparatorBuild2Probe =
-                    tuplePairComparatorFactoryBuild2Probe.createTuplePairComparator(ctx);
+            final ITuplePairComparator probComp = tuplePairComparatorFactoryProbe2Build.createTuplePairComparator(ctx);
+            final ITuplePairComparator buildComp = tuplePairComparatorFactoryBuild2Probe.createTuplePairComparator(ctx);
             final IPredicateEvaluator predEvaluator =
                     predEvaluatorFactory == null ? null : predEvaluatorFactory.createPredicateEvaluator();
 
-            for (int i = 0; i < probCompFactories.length; i++) {
-                probComp[i] = probCompFactories[i].createBinaryComparator();
-            }
-            for (int i = 0; i < buildCompFactories.length; i++) {
-                buildComp[i] = buildCompFactories[i].createBinaryComparator();
-            }
             final IMissingWriter[] nonMatchWriter =
                     isLeftOuter ? new IMissingWriter[nonMatchWriterFactories.length] : null;
             final ArrayTupleBuilder nullTupleBuild =
@@ -581,7 +556,7 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
                         final int[] probeKeys, final int[] buildKeys, final RecordDescriptor probeRd,
                         final RecordDescriptor buildRd, final ITuplePartitionComputer probeHpc,
                         final ITuplePartitionComputer buildHpc, RunFileReader probeSideReader,
-                        RunFileReader buildSideReader, final int level, final long beforeMax, IBinaryComparator[] comp)
+                        RunFileReader buildSideReader, final int level, final long beforeMax, ITuplePairComparator comp)
                         throws HyracksDataException {
 
                     boolean isReversed = probeKeys == OptimizedHybridHashJoinOperatorDescriptor.this.buildKeys
@@ -589,9 +564,8 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
                     assert isLeftOuter ? !isReversed : true : "LeftOut Join can not reverse roles";
                     OptimizedHybridHashJoin rHHj;
                     int n = getNumberOfPartitions(state.memForJoin, tableSize, fudgeFactor, nPartitions);
-                    rHHj = new OptimizedHybridHashJoin(ctx, state.memForJoin, n, PROBE_REL, BUILD_REL, probeKeys,
-                            buildKeys, comp, probeRd, buildRd, probeHpc, buildHpc, predEvaluator, isLeftOuter,
-                            nonMatchWriterFactories); //checked-confirmed
+                    rHHj = new OptimizedHybridHashJoin(ctx, state.memForJoin, n, PROBE_REL, BUILD_REL, comp, probeRd,
+                            buildRd, probeHpc, buildHpc, predEvaluator, isLeftOuter, nonMatchWriterFactories); //checked-confirmed
 
                     rHHj.setIsReversed(isReversed);
                     try {
@@ -723,7 +697,7 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
                 private void applyInMemHashJoin(int[] bKeys, int[] pKeys, int tabSize, RecordDescriptor buildRDesc,
                         RecordDescriptor probeRDesc, ITuplePartitionComputer hpcRepBuild,
                         ITuplePartitionComputer hpcRepProbe, RunFileReader bReader, RunFileReader pReader,
-                        IBinaryComparator[] comp) throws HyracksDataException {
+                        ITuplePairComparator comp) throws HyracksDataException {
                     boolean isReversed = pKeys == OptimizedHybridHashJoinOperatorDescriptor.this.buildKeys
                             && bKeys == OptimizedHybridHashJoinOperatorDescriptor.this.probeKeys;
                     assert isLeftOuter ? !isReversed : true : "LeftOut Join can not reverse roles";
@@ -733,9 +707,8 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
 
                     ISerializableTable table = new SerializableHashTable(tabSize, ctx, bufferManager);
                     InMemoryHashJoin joiner = new InMemoryHashJoin(ctx, new FrameTupleAccessor(probeRDesc), hpcRepProbe,
-                            new FrameTupleAccessor(buildRDesc), buildRDesc, hpcRepBuild,
-                            new FrameTuplePairComparator(pKeys, bKeys, comp), isLeftOuter, nonMatchWriter, table,
-                            predEvaluator, isReversed, bufferManager);
+                            new FrameTupleAccessor(buildRDesc), buildRDesc, hpcRepBuild, comp, isLeftOuter,
+                            nonMatchWriter, table, predEvaluator, isReversed, bufferManager);
 
                     try {
                         bReader.open();
@@ -791,8 +764,7 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
                     // Hence the reverse relation is different.
                     boolean isReversed = outerRd == buildRd && innerRd == probeRd;
                     assert isLeftOuter ? !isReversed : true : "LeftOut Join can not reverse roles";
-                    ITuplePairComparator nljComptorOuterInner =
-                            isReversed ? nljComparatorBuild2Probe : nljComparatorProbe2Build;
+                    ITuplePairComparator nljComptorOuterInner = isReversed ? buildComp : probComp;
                     NestedLoopJoin nlj =
                             new NestedLoopJoin(ctx, new FrameTupleAccessor(outerRd), new FrameTupleAccessor(innerRd),
                                     nljComptorOuterInner, memorySize, predEvaluator, isLeftOuter, nonMatchWriter);
@@ -832,5 +804,4 @@ public class OptimizedHybridHashJoinOperatorDescriptor extends AbstractOperatorD
             return op;
         }
     }
-
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/integration/TPCHCustomerOrderHashJoinTest.java b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/integration/TPCHCustomerOrderHashJoinTest.java
index c9fe22b..9ca3528 100644
--- a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/integration/TPCHCustomerOrderHashJoinTest.java
+++ b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/integration/TPCHCustomerOrderHashJoinTest.java
@@ -24,7 +24,6 @@ import java.util.Arrays;
 import org.apache.hyracks.api.constraints.PartitionConstraintHelper;
 import org.apache.hyracks.api.dataflow.IConnectorDescriptor;
 import org.apache.hyracks.api.dataflow.IOperatorDescriptor;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.IBinaryHashFunctionFactory;
 import org.apache.hyracks.api.dataflow.value.IBinaryHashFunctionFamily;
 import org.apache.hyracks.api.dataflow.value.IMissingWriterFactory;
@@ -133,7 +132,7 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new int[] { 0 },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
-                new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE }, custOrderJoinDesc, 128,
+                new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0), custOrderJoinDesc, 128,
                 null, 128);
         PartitionConstraintHelper.addAbsoluteLocationConstraint(spec, join, NC1_ID);
 
@@ -181,8 +180,6 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new OptimizedHybridHashJoinOperatorDescriptor(spec, 32, 20, 1.2, new int[] { 1 }, new int[] { 0 },
                         new IBinaryHashFunctionFamily[] { MurmurHash3BinaryHashFunctionFamily.INSTANCE },
                         new IBinaryHashFunctionFamily[] { MurmurHash3BinaryHashFunctionFamily.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
                         custOrderJoinDesc, new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0),
                         new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 0, 1), null, false, null);
 
@@ -237,7 +234,7 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new int[] { 1 },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
-                new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE }, null, custOrderJoinDesc,
+                new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 0, 1), null, custOrderJoinDesc,
                 true, nonMatchWriterFactories, 128, 128);
         PartitionConstraintHelper.addAbsoluteLocationConstraint(spec, join, NC1_ID);
 
@@ -290,8 +287,6 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new OptimizedHybridHashJoinOperatorDescriptor(spec, 32, 20, 1.2, new int[] { 0 }, new int[] { 1 },
                         new IBinaryHashFunctionFamily[] { MurmurHash3BinaryHashFunctionFamily.INSTANCE },
                         new IBinaryHashFunctionFamily[] { MurmurHash3BinaryHashFunctionFamily.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
                         custOrderJoinDesc, new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 0, 1),
                         new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0), null, true,
                         nonMatchWriterFactories);
@@ -347,7 +342,7 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new int[] { 0 },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
-                new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE }, custOrderJoinDesc, 128,
+                new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0), custOrderJoinDesc, 128,
                 null, 128);
         PartitionConstraintHelper.addAbsoluteLocationConstraint(spec, join, NC1_ID, NC2_ID);
 
@@ -405,8 +400,6 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new OptimizedHybridHashJoinOperatorDescriptor(spec, 5, 20, 1.2, new int[] { 1 }, new int[] { 0 },
                         new IBinaryHashFunctionFamily[] { MurmurHash3BinaryHashFunctionFamily.INSTANCE },
                         new IBinaryHashFunctionFamily[] { MurmurHash3BinaryHashFunctionFamily.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
                         custOrderJoinDesc, new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0),
                         new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 0, 1), null, false, null);
 
@@ -466,7 +459,7 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new int[] { 0 },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
-                new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE }, custOrderJoinDesc, 128,
+                new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0), custOrderJoinDesc, 128,
                 null, 128);
         PartitionConstraintHelper.addPartitionCountConstraint(spec, join, 2);
 
@@ -530,7 +523,7 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new int[] { 0 },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
                 new IBinaryHashFunctionFactory[] { PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
-                new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE }, custOrderJoinDesc, 128,
+                new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0), custOrderJoinDesc, 128,
                 null, 128);
         PartitionConstraintHelper.addAbsoluteLocationConstraint(spec, join, NC1_ID, NC2_ID);
 
@@ -587,8 +580,6 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new OptimizedHybridHashJoinOperatorDescriptor(spec, 15, 243, 1.2, new int[] { 0 }, new int[] { 1 },
                         new IBinaryHashFunctionFamily[] { UTF8StringBinaryHashFunctionFamily.INSTANCE },
                         new IBinaryHashFunctionFamily[] { UTF8StringBinaryHashFunctionFamily.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
                         custOrderJoinDesc, new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 0, 1),
                         new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0), null);
 
@@ -637,8 +628,6 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new OptimizedHybridHashJoinOperatorDescriptor(spec, 15, 122, 1.2, new int[] { 0 }, new int[] { 1 },
                         new IBinaryHashFunctionFamily[] { UTF8StringBinaryHashFunctionFamily.INSTANCE },
                         new IBinaryHashFunctionFamily[] { UTF8StringBinaryHashFunctionFamily.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
                         custOrderJoinDesc, new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 0, 1),
                         new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0), null);
 
@@ -688,8 +677,6 @@ public class TPCHCustomerOrderHashJoinTest extends AbstractIntegrationTest {
                 new OptimizedHybridHashJoinOperatorDescriptor(spec, 6, 122, 1.2, new int[] { 0 }, new int[] { 1 },
                         new IBinaryHashFunctionFamily[] { UTF8StringBinaryHashFunctionFamily.INSTANCE },
                         new IBinaryHashFunctionFamily[] { UTF8StringBinaryHashFunctionFamily.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
-                        new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
                         custOrderJoinDesc, new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 0, 1),
                         new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0), null);
 
diff --git a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/integration/TPCHCustomerOrderNestedLoopJoinTest.java b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/integration/TPCHCustomerOrderNestedLoopJoinTest.java
index 7c7da9a..c0bbfe3 100644
--- a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/integration/TPCHCustomerOrderNestedLoopJoinTest.java
+++ b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/integration/TPCHCustomerOrderNestedLoopJoinTest.java
@@ -20,19 +20,12 @@ package org.apache.hyracks.tests.integration;
 
 import java.io.File;
 
-import org.apache.hyracks.api.comm.IFrameTupleAccessor;
 import org.apache.hyracks.api.constraints.PartitionConstraintHelper;
-import org.apache.hyracks.api.context.IHyracksTaskContext;
 import org.apache.hyracks.api.dataflow.IConnectorDescriptor;
 import org.apache.hyracks.api.dataflow.IOperatorDescriptor;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.IMissingWriterFactory;
 import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer;
-import org.apache.hyracks.api.dataflow.value.ITuplePairComparator;
-import org.apache.hyracks.api.dataflow.value.ITuplePairComparatorFactory;
 import org.apache.hyracks.api.dataflow.value.RecordDescriptor;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.io.FileSplit;
 import org.apache.hyracks.api.io.ManagedFileSplit;
 import org.apache.hyracks.api.job.JobSpecification;
@@ -47,6 +40,7 @@ import org.apache.hyracks.dataflow.std.file.ConstantFileSplitProvider;
 import org.apache.hyracks.dataflow.std.file.DelimitedDataTupleParserFactory;
 import org.apache.hyracks.dataflow.std.file.FileScanOperatorDescriptor;
 import org.apache.hyracks.dataflow.std.file.IFileSplitProvider;
+import org.apache.hyracks.dataflow.std.join.JoinComparatorFactory;
 import org.apache.hyracks.dataflow.std.join.NestedLoopJoinOperatorDescriptor;
 import org.apache.hyracks.dataflow.std.result.ResultWriterOperatorDescriptor;
 import org.apache.hyracks.tests.util.NoopMissingWriterFactory;
@@ -54,62 +48,6 @@ import org.apache.hyracks.tests.util.ResultSerializerFactoryProvider;
 import org.junit.Test;
 
 public class TPCHCustomerOrderNestedLoopJoinTest extends AbstractIntegrationTest {
-    private static class JoinComparatorFactory implements ITuplePairComparatorFactory {
-        private static final long serialVersionUID = 1L;
-
-        private final IBinaryComparatorFactory bFactory;
-        private final int pos0;
-        private final int pos1;
-
-        public JoinComparatorFactory(IBinaryComparatorFactory bFactory, int pos0, int pos1) {
-            this.bFactory = bFactory;
-            this.pos0 = pos0;
-            this.pos1 = pos1;
-        }
-
-        @Override
-        public ITuplePairComparator createTuplePairComparator(IHyracksTaskContext ctx) {
-            return new JoinComparator(bFactory.createBinaryComparator(), pos0, pos1);
-        }
-    }
-
-    private static class JoinComparator implements ITuplePairComparator {
-
-        private final IBinaryComparator bComparator;
-        private final int field0;
-        private final int field1;
-
-        public JoinComparator(IBinaryComparator bComparator, int field0, int field1) {
-            this.bComparator = bComparator;
-            this.field0 = field0;
-            this.field1 = field1;
-        }
-
-        @Override
-        public int compare(IFrameTupleAccessor accessor0, int tIndex0, IFrameTupleAccessor accessor1, int tIndex1)
-                throws HyracksDataException {
-            int tStart0 = accessor0.getTupleStartOffset(tIndex0);
-            int fStartOffset0 = accessor0.getFieldSlotsLength() + tStart0;
-
-            int tStart1 = accessor1.getTupleStartOffset(tIndex1);
-            int fStartOffset1 = accessor1.getFieldSlotsLength() + tStart1;
-
-            int fStart0 = accessor0.getFieldStartOffset(tIndex0, field0);
-            int fEnd0 = accessor0.getFieldEndOffset(tIndex0, field0);
-            int fLen0 = fEnd0 - fStart0;
-
-            int fStart1 = accessor1.getFieldStartOffset(tIndex1, field1);
-            int fEnd1 = accessor1.getFieldEndOffset(tIndex1, field1);
-            int fLen1 = fEnd1 - fStart1;
-
-            int c = bComparator.compare(accessor0.getBuffer().array(), fStart0 + fStartOffset0, fLen0,
-                    accessor1.getBuffer().array(), fStart1 + fStartOffset1, fLen1);
-            if (c != 0) {
-                return c;
-            }
-            return 0;
-        }
-    }
 
     /*
      * TPCH Customer table: CREATE TABLE CUSTOMER ( C_CUSTKEY INTEGER NOT NULL,
diff --git a/hyracks-fullstack/hyracks/hyracks-examples/tpch-example/tpchclient/src/main/java/org/apache/hyracks/examples/tpch/client/Join.java b/hyracks-fullstack/hyracks/hyracks-examples/tpch-example/tpchclient/src/main/java/org/apache/hyracks/examples/tpch/client/Join.java
index 90156f4..209bf34 100644
--- a/hyracks-fullstack/hyracks/hyracks-examples/tpch-example/tpchclient/src/main/java/org/apache/hyracks/examples/tpch/client/Join.java
+++ b/hyracks-fullstack/hyracks/hyracks-examples/tpch-example/tpchclient/src/main/java/org/apache/hyracks/examples/tpch/client/Join.java
@@ -184,7 +184,7 @@ public class Join {
                             PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
                     new IBinaryHashFunctionFactory[] {
                             PointableBinaryHashFunctionFactory.of(UTF8StringPointable.FACTORY) },
-                    new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
+                    new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 0, 1),
                     Common.custOrderJoinDesc, tableSize, null, memSize * frameSize);
 
         } else if ("hybrid".equalsIgnoreCase(algo)) {
@@ -192,8 +192,6 @@ public class Join {
                     new int[] { 0 }, new int[] { 1 },
                     new IBinaryHashFunctionFamily[] { UTF8StringBinaryHashFunctionFamily.INSTANCE },
                     new IBinaryHashFunctionFamily[] { UTF8StringBinaryHashFunctionFamily.INSTANCE },
-                    new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
-                    new IBinaryComparatorFactory[] { UTF8StringBinaryComparatorFactory.INSTANCE },
                     Common.custOrderJoinDesc,
                     new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 0, 1),
                     new JoinComparatorFactory(UTF8StringBinaryComparatorFactory.INSTANCE, 1, 0), null);