You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ca...@apache.org on 2018/11/23 12:22:47 UTC

svn commit: r1847255 - in /jackrabbit/oak/branches/1.6: ./ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/ oak-core/src/test/java/org/apache/jackrabbit/oak/query/ oak-core/src/test/j...

Author: catholicon
Date: Fri Nov 23 12:22:47 2018
New Revision: 1847255

URL: http://svn.apache.org/viewvc?rev=1847255&view=rev
Log:
OAK-7898: Facet queries with UNION should do trivial merge of facets from sub-queries (backport r1846617 from trunk)


Added:
    jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ResultRowImplTest.java   (with props)
Modified:
    jackrabbit/oak/branches/1.6/   (props changed)
    jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java
    jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java
    jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/FacetResult.java
    jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/package-info.java
    jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/facet/FacetResultTest.java
    jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java

Propchange: jackrabbit/oak/branches/1.6/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Nov 23 12:22:47 2018
@@ -1,4 +1,4 @@
 /jackrabbit/oak/branches/1.0:1665962
 /jackrabbit/oak/branches/1.8:1844835
-/jackrabbit/oak/trunk:1781068,1781075,1781248,1781386,1781846,1781907,1782000,1782029,1782196,1782447,1782476,1782770,1782945,1782966,1782973,1782990,1783061,1783066,1783089,1783104-1783105,1783110,1783619,1783720,1783731,1783733,1783738,1783742,1783773,1783855,1783891,1784023,1784034,1784130,1784162,1784251,1784401,1784551,1784574,1784689,1785095,1785108,1785161,1785172,1785283,1785652,1785838,1785916-1785917,1785919,1785946,1786122,1787074,1787145,1787151,1787217,1787425,1788056,1788378,1788387-1788389,1788463,1788476,1788850,1789056,1789534,1789925,1789940,1789987,1790006,1790013,1790069,1790077,1790079,1790382,1790502-1790503,1792049,1792463,1792742,1792746,1793013,1793088,1793618,1793627,1793644,1794393,1794417,1794683,1795138,1795314,1795330,1795475,1795488,1795491,1795502,1795594,1795613,1795618,1796144,1796230,1796239,1796274,1796278,1796988,1797378,1798035,1798832,1798834,1799219,1799389,1799393,1799924,1800244,1800269,1800606,1800613,1800974,1801011,1801013,1801118-1801119
 ,1801675,1802260,1802262,1802286,1802548,1802905,1802934,1802938,1802973,1803026,1803247-1803249,1803951,1803953-1803955,1804437,1805851-1805852,1806668,1807308,1807688,1808022,1808053,1808125,1808128,1808142,1808240,1808246,1808731,1809024,1809026,1809131,1809163,1809178-1809179,1809253,1809255-1809256,1809289,1809745,1811071-1811072,1811155,1811380,1811655,1811952,1811963,1811986,1813192,1813538,1814189,1814332,1814397,1814475,1815201,1815438,1815926,1817326,1817919,1817987-1817988,1817990,1818038,1818042,1818056,1818124,1818137,1818554,1818576,1818645,1819048,1819050,1821237,1821325,1821358,1821495,1821516,1821847,1822207,1822850,1823172,1823655,1824896,1825471,1825654,1826237,1826338,1826532,1826640,1826932,1826957,1827472,1827486,1827977,1828502,1829527,1829569,1829587,1829665,1829854,1829864,1829987,1829998,1830019,1830160,1830239,1830748,1831190,1831374,1832258,1832379,1832535,1833308,1834112,1834648-1834649,1834681,1835060,1836082,1837475,1837998,1838637,1839746,1840024,1840
 226,1842677,1843175,1843222,1843231,1844549,1844642,1844728,1845730-1845731,1845863,1845865,1846057
+/jackrabbit/oak/trunk:1781068,1781075,1781248,1781386,1781846,1781907,1782000,1782029,1782196,1782447,1782476,1782770,1782945,1782966,1782973,1782990,1783061,1783066,1783089,1783104-1783105,1783110,1783619,1783720,1783731,1783733,1783738,1783742,1783773,1783855,1783891,1784023,1784034,1784130,1784162,1784251,1784401,1784551,1784574,1784689,1785095,1785108,1785161,1785172,1785283,1785652,1785838,1785916-1785917,1785919,1785946,1786122,1787074,1787145,1787151,1787217,1787425,1788056,1788378,1788387-1788389,1788463,1788476,1788850,1789056,1789534,1789925,1789940,1789987,1790006,1790013,1790069,1790077,1790079,1790382,1790502-1790503,1792049,1792463,1792742,1792746,1793013,1793088,1793618,1793627,1793644,1794393,1794417,1794683,1795138,1795314,1795330,1795475,1795488,1795491,1795502,1795594,1795613,1795618,1796144,1796230,1796239,1796274,1796278,1796988,1797378,1798035,1798832,1798834,1799219,1799389,1799393,1799924,1800244,1800269,1800606,1800613,1800974,1801011,1801013,1801118-1801119
 ,1801675,1802260,1802262,1802286,1802548,1802905,1802934,1802938,1802973,1803026,1803247-1803249,1803951,1803953-1803955,1804437,1805851-1805852,1806668,1807308,1807688,1808022,1808053,1808125,1808128,1808142,1808240,1808246,1808731,1809024,1809026,1809131,1809163,1809178-1809179,1809253,1809255-1809256,1809289,1809745,1811071-1811072,1811155,1811380,1811655,1811952,1811963,1811986,1813192,1813538,1814189,1814332,1814397,1814475,1815201,1815438,1815926,1817326,1817919,1817987-1817988,1817990,1818038,1818042,1818056,1818124,1818137,1818554,1818576,1818645,1819048,1819050,1821237,1821325,1821358,1821495,1821516,1821847,1822207,1822850,1823172,1823655,1824896,1825471,1825654,1826237,1826338,1826532,1826640,1826932,1826957,1827472,1827486,1827977,1828502,1829527,1829569,1829587,1829665,1829854,1829864,1829987,1829998,1830019,1830160,1830239,1830748,1831190,1831374,1832258,1832379,1832535,1833308,1834112,1834648-1834649,1834681,1835060,1836082,1837475,1837998,1838637,1839746,1840024,1840
 226,1842677,1843175,1843222,1843231,1844549,1844642,1844728,1845730-1845731,1845863,1845865,1846057,1846617
 /jackrabbit/trunk:1345480

Modified: jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java?rev=1847255&r1=1847254&r2=1847255&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java (original)
+++ jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java Fri Nov 23 12:22:47 2018
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.query;
 
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.Map;
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.PropertyValue;
@@ -264,4 +265,16 @@ public class ResultRowImpl implements Re
 
     }
 
+    static ResultRowImpl getMappingResultRow(ResultRowImpl delegate, final Map<String, String> columnToFacetMap) {
+        if (columnToFacetMap.size() == 0) {
+            return delegate;
+        }
+
+        PropertyValue[] mappedVals = delegate.getValues();
+        for (Map.Entry<String, String> entry : columnToFacetMap.entrySet()) {
+            mappedVals[delegate.query.getColumnIndex(entry.getKey())]   = PropertyValues.newString(entry.getValue());
+        }
+        return new ResultRowImpl(delegate.query, delegate.trees, mappedVals,
+                delegate.distinctValues, delegate.orderValues);
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java?rev=1847255&r1=1847254&r2=1847255&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java (original)
+++ jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java Fri Nov 23 12:22:47 2018
@@ -21,21 +21,22 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import com.google.common.collect.AbstractIterator;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
 import com.google.common.collect.Maps;
-
-import org.apache.jackrabbit.oak.api.PropertyValue;
-import org.apache.jackrabbit.oak.api.Result;
+import com.google.common.collect.PeekingIterator;
+import org.apache.jackrabbit.oak.api.*;
 import org.apache.jackrabbit.oak.api.Result.SizePrecision;
-import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.query.ast.ColumnImpl;
 import org.apache.jackrabbit.oak.query.ast.OrderingImpl;
 import org.apache.jackrabbit.oak.query.QueryImpl.MeasuringIterator;
+import org.apache.jackrabbit.oak.query.facet.FacetResult;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.Iterators;
+import static org.apache.jackrabbit.oak.query.QueryImpl.REP_FACET;
 
 /**
  * Represents a union query.
@@ -295,9 +296,11 @@ public class UnionQueryImpl implements Q
         boolean distinct = !unionAll;
         Comparator<ResultRowImpl> orderBy = ResultRowImpl.getComparator(orderings);
 
+        FacetMerger facetMerger = new FacetMerger(left, right);
+
         Iterator<ResultRowImpl> it;
-        final Iterator<ResultRowImpl> leftRows = left.getRows();
-        final Iterator<ResultRowImpl> rightRows = right.getRows();
+        final Iterator<ResultRowImpl> leftRows = facetMerger.getLeftIterator();;
+        final Iterator<ResultRowImpl> rightRows = facetMerger.getRightIterator();
         Iterator<ResultRowImpl> leftIter = leftRows;
         Iterator<ResultRowImpl> rightIter = rightRows;
 
@@ -408,4 +411,96 @@ public class UnionQueryImpl implements Q
         right.verifyNotPotentiallySlow();
     }
 
+    static class FacetMerger {
+
+        private final Iterator<ResultRowImpl> leftIterator;
+        private final Iterator<ResultRowImpl> rightIterator;
+
+        FacetMerger(Query left, Query right) {
+            ColumnImpl[] columns = left.getColumns();
+            String[] columnNames = new String[columns.length];
+            for (int i = 0; i < columns.length; i++) {
+                columnNames[i] = columns[i].getColumnName();
+            }
+
+            Iterator<ResultRowImpl> lIter = left.getRows();
+            Iterator<ResultRowImpl> rIter = right.getRows();
+
+            if (!hasFacets(columnNames) || !bothHaveRows(lIter, rIter)) {
+                this.leftIterator = lIter;
+                this.rightIterator = rIter;
+
+                return;
+            }
+
+            PeekingIterator<ResultRowImpl> lPeekIter = Iterators.peekingIterator(lIter);
+            PeekingIterator<ResultRowImpl> rPeekIter = Iterators.peekingIterator(rIter);
+
+            final ResultRow lRow = lPeekIter.peek();
+            final ResultRow rRow = rPeekIter.peek();
+
+            FacetResult.FacetResultRow leftResultRow = new FacetResult.FacetResultRow() {
+                @Override
+                public String getValue(String columnName) {
+                    PropertyValue value = lRow.getValue(columnName);
+                    return value == null ? null : value.getValue(Type.STRING);
+                }
+            };
+            FacetResult.FacetResultRow rightResultRow = new FacetResult.FacetResultRow() {
+                @Override
+                public String getValue(String columnName) {
+                    PropertyValue value = rRow.getValue(columnName);
+                    return value == null ? null : value.getValue(Type.STRING);
+                }
+            };
+            FacetResult facetResult = new FacetResult(columnNames, leftResultRow, rightResultRow);
+
+            Map<String, String> columnToFacetMap = facetResult.asColumnToFacetJsonMap();
+
+            this.leftIterator = new MappingRowIterator(columnToFacetMap, lPeekIter);
+            this.rightIterator = new MappingRowIterator(columnToFacetMap, rPeekIter);
+        }
+
+        Iterator<ResultRowImpl> getLeftIterator() {
+            return leftIterator;
+        }
+
+        Iterator<ResultRowImpl> getRightIterator() {
+            return rightIterator;
+        }
+
+        private boolean hasFacets(String[] columnNames) {
+            for (String c : columnNames) {
+                if (c.startsWith(REP_FACET + "(")) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private boolean bothHaveRows(Iterator<ResultRowImpl> lIter, Iterator<ResultRowImpl> rIter) {
+            return lIter.hasNext() && rIter.hasNext();
+        }
+    }
+
+    static class MappingRowIterator extends AbstractIterator<ResultRowImpl> {
+
+        private final Map<String, String> columnToFacetMap;
+        private final Iterator<ResultRowImpl> delegate;
+
+        MappingRowIterator(Map<String, String> columnToFacetMap, Iterator<ResultRowImpl> delegate) {
+            super();
+            this.columnToFacetMap = columnToFacetMap;
+            this.delegate = delegate;
+        }
+
+        @Override
+        protected ResultRowImpl computeNext() {
+            if (delegate.hasNext()) {
+                return ResultRowImpl.getMappingResultRow(delegate.next(), columnToFacetMap);
+            } else {
+                return endOfData();
+            }
+        }
+    }
 }

Modified: jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/FacetResult.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/FacetResult.java?rev=1847255&r1=1847254&r2=1847255&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/FacetResult.java (original)
+++ jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/FacetResult.java Fri Nov 23 12:22:47 2018
@@ -25,15 +25,19 @@ import javax.jcr.Value;
 import javax.jcr.query.QueryResult;
 import javax.jcr.query.Row;
 import javax.jcr.query.RowIterator;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
 import org.apache.jackrabbit.oak.commons.json.JsopReader;
 import org.apache.jackrabbit.oak.commons.json.JsopTokenizer;
 import org.apache.jackrabbit.oak.query.QueryImpl;
+import org.apache.jackrabbit.oak.spi.query.QueryConstants;
+
+import static java.util.Collections.reverseOrder;
+import static java.util.Comparator.comparingInt;
+import static org.apache.jackrabbit.oak.query.QueryImpl.REP_FACET;
 
 /**
  * A facet result is a wrapper for {@link javax.jcr.query.QueryResult} capable of returning information about facets
@@ -47,26 +51,73 @@ public class FacetResult {
         try {
             RowIterator rows = queryResult.getRows();
             if (rows.hasNext()) {
-                Row row = rows.nextRow();
-                for (String column : queryResult.getColumnNames()) {
-                    if (column.startsWith(QueryImpl.REP_FACET)) {
-                        String dimension = column.substring(QueryImpl.REP_FACET.length() + 1, column.length() - 1);
-                        Value value = row.getValue(column);
-                        if (value != null) {
-                            String jsonFacetString = value.getString();
-                            parseJson(dimension, jsonFacetString);
-                        }
+                final Row row = rows.nextRow();
+                parseJson(queryResult.getColumnNames(), new FacetResultRow() {
+                    @Override
+                    public String getValue(String columnName) throws Exception {
+                        Value value = row.getValue(columnName);
+                        return value == null ? null : value.getString();
                     }
-                }
+                });
             }
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
+    public FacetResult(String[] columnNames, FacetResultRow...rows) {
+        try {
+            for (FacetResultRow row : rows) {
+                parseJson(columnNames, row);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Map<String, String> asColumnToFacetJsonMap() {
+        Map<String, String> json = Maps.newHashMap();
+        for (Map.Entry<String, List<Facet>> entry : perDimFacets.entrySet()) {
+            JsopBuilder builder = new JsopBuilder();
+            builder.object();
+
+            for (Facet f : entry.getValue()) {
+                builder.key(f.getLabel());
+                builder.value(f.getCount());
+            }
+
+            builder.endObject();
+
+            json.put(REP_FACET + "(" + entry.getKey() + ")", builder.toString());
+        }
+
+        return json;
+    }
+
+    private void parseJson(String[] columnNames, FacetResultRow row) throws Exception {
+        for (String column : columnNames) {
+            if (column.startsWith(REP_FACET)) {
+                String dimension = column.substring(REP_FACET.length() + 1, column.length() - 1);
+                String value = row.getValue(column);
+                if (value != null) {
+                    String jsonFacetString = value;
+                    parseJson(dimension, jsonFacetString);
+                }
+            }
+        }
+    }
+
     private void parseJson(String dimension, String jsonFacetString) {
         JsopTokenizer jsopTokenizer = new JsopTokenizer(jsonFacetString);
-        List<Facet> facets = new LinkedList<Facet>();
+        List<Facet> facets = perDimFacets.get(dimension);
+        Map<String, Facet> facetsMap = Maps.newLinkedHashMap();
+        if (facets != null) {
+            for (Facet facet : facets) {
+                if (!facetsMap.containsKey(facet.getLabel())) {
+                    facetsMap.put(facet.getLabel(), facet);
+                }
+            }
+        }
         int c;
         String label = null;
         int count;
@@ -76,11 +127,30 @@ public class FacetResult {
             } else if (JsopReader.NUMBER == c) {
                 count = Integer.parseInt(jsopTokenizer.getEscapedToken());
                 if (label != null) {
-                    facets.add(new Facet(label, count));
+                    if (facetsMap.containsKey(label)) {
+                        count += facetsMap.get(label).getCount();
+                    }
+                    facetsMap.put(label, new Facet(label, count));
                 }
                 label = null;
             }
         }
+        facets = Lists.newArrayList(facetsMap.values());
+        Collections.sort(facets, new Comparator<Facet>() {
+            @Override
+            public int compare(Facet facet1, Facet facet2) {
+                int cnt1 = facet1.getCount();
+                int cnt2 = facet2.getCount();
+
+                if (cnt1 > cnt2) {
+                    return -1;
+                } else if (cnt1 == cnt2) {
+                    return 0;
+                } else {
+                    return 1;
+                }
+            }
+        });
         perDimFacets.put(dimension, facets);
     }
 
@@ -102,7 +172,7 @@ public class FacetResult {
         private final String label;
         private final int count;
 
-        private Facet(String label, int count) {
+        Facet(String label, int count) {
             this.label = label;
             this.count = count;
         }
@@ -124,5 +194,9 @@ public class FacetResult {
             return count;
         }
     }
+
+    public interface FacetResultRow {
+        String getValue(String columnName) throws Exception;
+    }
 }
 

Modified: jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/package-info.java?rev=1847255&r1=1847254&r2=1847255&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/package-info.java (original)
+++ jackrabbit/oak/branches/1.6/oak-core/src/main/java/org/apache/jackrabbit/oak/query/facet/package-info.java Fri Nov 23 12:22:47 2018
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.0")
+@Version("1.1.0")
 @Export(optional = "provide:=true")
 package org.apache.jackrabbit.oak.query.facet;
 

Added: jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ResultRowImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ResultRowImplTest.java?rev=1847255&view=auto
==============================================================================
--- jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ResultRowImplTest.java (added)
+++ jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ResultRowImplTest.java Fri Nov 23 12:22:47 2018
@@ -0,0 +1,55 @@
+/*
+ * 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.jackrabbit.oak.query;
+
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.api.Type;
+import org.junit.Test;
+
+import java.util.Map;
+
+import static org.apache.jackrabbit.oak.spi.query.PropertyValues.newString;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ResultRowImplTest {
+    @Test
+    public void mappedGetValue() {
+        Query query = mock(Query.class);
+        when(query.getColumnIndex("origCol")).thenReturn(0);
+        when(query.getColumnIndex("col1")).thenReturn(1);
+
+        PropertyValue[] origVals = new PropertyValue[]{newString("origVal"), newString("overriddenVal")};
+        ResultRowImpl orig = new ResultRowImpl(query, null, origVals, null, null);
+
+        Map<String, String> map = Maps.newHashMap();
+        map.put("col1", "val1");
+
+        ResultRowImpl mappedRow = ResultRowImpl.getMappingResultRow(orig, map);
+
+        assertEquals("origVal", mappedRow.getValue("origCol").getValue(Type.STRING));
+        assertEquals("val1", mappedRow.getValue("col1").getValue(Type.STRING));
+
+        PropertyValue[] mappedVals = mappedRow.getValues();
+        assertEquals(2, mappedVals.length);
+        assertEquals("origVal", mappedVals[0].getValue(Type.STRING));
+        assertEquals("val1", mappedVals[1].getValue(Type.STRING));
+    }
+}

Propchange: jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ResultRowImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/facet/FacetResultTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/facet/FacetResultTest.java?rev=1847255&r1=1847254&r2=1847255&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/facet/FacetResultTest.java (original)
+++ jackrabbit/oak/branches/1.6/oak-core/src/test/java/org/apache/jackrabbit/oak/query/facet/FacetResultTest.java Fri Nov 23 12:22:47 2018
@@ -16,13 +16,22 @@
  */
 package org.apache.jackrabbit.oak.query.facet;
 
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
+import org.apache.jackrabbit.oak.query.facet.FacetResult.Facet;
+import org.apache.jackrabbit.oak.query.facet.FacetResult.FacetResultRow;
+import org.junit.Test;
+
 import javax.jcr.Value;
 import javax.jcr.query.QueryResult;
 import javax.jcr.query.Row;
 import javax.jcr.query.RowIterator;
+import java.util.List;
+import java.util.Map;
 
 import org.junit.Test;
 
+import static org.apache.jackrabbit.oak.query.QueryImpl.REP_FACET;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -65,4 +74,142 @@ public class FacetResultTest {
         assertEquals(1, facetResult.getFacets("jcr:title").get(1).getCount(), 0);
     }
 
+    @Test
+    public void simpleMergeFacets() {
+        String r1c1Facet = json(f("l1", 2), f("l2", 1));
+        String r2c1Facet = json(f("l2", 4), f("l1", 1));
+
+        FacetResult merged = facet(new FacetColumn("x", r1c1Facet, r2c1Facet));
+
+        FacetResult expected = facet(new FacetColumn("x", json(f("l2", 5), f("l1", 3))));
+
+        verify(expected, merged);
+    }
+
+    @Test
+    public void uniqueLabelsMergeFacets() {
+        String r1c1Facet = json(f("l1", 1));
+        String r2c1Facet = json(f("l2", 2));
+
+        FacetResult merged = facet(new FacetColumn("x", r1c1Facet, r2c1Facet));
+
+        FacetResult expected = facet(new FacetColumn("x", json(f("l2", 2), f("l1", 1))));
+
+        verify(expected, merged);
+    }
+
+    @Test
+    public void multipleColumns() {
+        String r1c1Facet = json(f("l1", 1));
+        String r2c1Facet = json(f("l2", 2));
+
+        String r1c2Facet = json(f("m1", 2));
+        String r2c2Facet = json(f("m2", 1));
+
+        FacetResult merged = facet(
+                new FacetColumn("x", r1c1Facet, r2c1Facet),
+                new FacetColumn("y", r1c2Facet, r2c2Facet)
+        );
+
+        FacetResult expected = facet(
+                new FacetColumn("x", json(f("l2", 2), f("l1", 1))),
+                new FacetColumn("y", json(f("m1", 2), f("m2", 1)))
+        );
+
+        verify(expected, merged);
+    }
+
+    @Test
+    public void multipleColumnsWithNullColumns() {
+        String r2c1Facet = json(f("l1", 1));
+        String r1c2Facet = json(f("m1", 1));
+
+        FacetResult merged = facet(
+                new FacetColumn("x", null, r2c1Facet),
+                new FacetColumn("y", r1c2Facet, null)
+        );
+
+        FacetResult expected = facet(
+                new FacetColumn("x", json(f("l1", 1))),
+                new FacetColumn("y", json(f("m1", 1)))
+        );
+
+        verify(expected, merged);
+    }
+
+    private FacetResult facet(FacetColumn ... facetColumns) {
+        String[] colNames = new String[facetColumns.length];
+        colNames[0] = facetColumns[0].colName;
+
+        int numRows = facetColumns[0].facets.length;
+
+        for (int i = 1; i < facetColumns.length; i++) {
+            assertEquals("numRows for col num " + i + " wasn't same as first", numRows, facetColumns[i].facets.length);
+
+            colNames[i] = facetColumns[i].colName;
+        }
+
+        FacetResultRow[] facetResultRows = new FacetResultRow[numRows];
+
+        for (int i = 0; i < numRows; i++) {
+            final Map<String, String> columns = Maps.newHashMap();
+
+            for (FacetColumn col : facetColumns) {
+                columns.put(col.colName, col.facets[i]);
+            }
+
+            facetResultRows[i] = new FacetResultRow() {
+                final Map<String, String> cols = columns;
+                @Override
+                public String getValue(String columnName) {
+                    return cols.get(columnName);
+                }
+            };
+        }
+
+        return new FacetResult(colNames, facetResultRows);
+    }
+
+    private static String json(Facet ... facets) {
+        JsopBuilder builder = new JsopBuilder();
+        builder.object();
+        for (Facet facet : facets) {
+            builder.key(facet.getLabel());
+            builder.value(facet.getCount());
+        }
+        builder.endObject();
+
+        return builder.toString();
+    }
+
+    private static class FacetColumn {
+        final String colName;
+        final String[] facets;
+
+        FacetColumn(String colName, String ... facets) {
+            this.colName = REP_FACET + "(" + colName + ")";
+            this.facets = facets;
+        }
+    }
+
+    private static Facet f(String label, int count) {
+        return new Facet(label, count);
+    }
+
+    private static void verify(FacetResult expected, FacetResult result) {
+        assertEquals("Dimension mismatch", expected.getDimensions(), result.getDimensions());
+
+        for (String dim : expected.getDimensions()) {
+            List<Facet> expectedFacets = expected.getFacets(dim);
+            List<Facet> resultFacets = result.getFacets(dim);
+
+            for (int i = 0; i < expectedFacets.size(); i++) {
+                Facet expectedFacet = expectedFacets.get(i);
+                Facet resultFacet = resultFacets.get(i);
+
+                assertEquals("label mismatch for dim " + dim, expectedFacet.getLabel(), resultFacet.getLabel());
+                assertEquals("count mismatch for dim " + dim, expectedFacet.getCount(), resultFacet.getCount());
+            }
+        }
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java?rev=1847255&r1=1847254&r2=1847255&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java (original)
+++ jackrabbit/oak/branches/1.6/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java Fri Nov 23 12:22:47 2018
@@ -29,6 +29,7 @@ import javax.jcr.query.RowIterator;
 import javax.jcr.security.Privilege;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import com.google.common.collect.Maps;
 import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
@@ -38,6 +39,7 @@ import org.apache.jackrabbit.oak.query.f
 import org.junit.After;
 import org.junit.Before;
 
+import static com.google.common.collect.Sets.newHashSet;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
 
 /**
@@ -778,6 +780,95 @@ public class FacetTest extends AbstractQ
         assertEquals(2, rows.getSize());
     }
 
+    public void testMergedFacetsOverUnionUniqueLabels() throws Exception {
+        Node n1 = testRootNode.addNode("node1");
+        n1.setProperty("text", "t1");
+        n1.setProperty("x", "x1");
+        n1.setProperty("name","Node1");
+
+        Node n2 = testRootNode.addNode("node2");
+        n2.setProperty("text", "t2");
+        n2.setProperty("x", "x2");
+        n2.setProperty("name","Node2");
+
+        Node n3 = testRootNode.addNode("node3");
+        n3.setProperty("text", "t3");
+        n3.setProperty("x", "x3");
+        n3.setProperty("name","Node3");
+        superuser.save();
+
+        String xpath = "//*[@name = 'Node1' or @text = 't2' or @x = 'x3']/(rep:facet(text))";
+
+        Query q = qm.createQuery(xpath, Query.XPATH);
+
+        QueryResult result = q.execute();
+        FacetResult facetResult = new FacetResult(result);
+
+        assertEquals("Unexpected dimensions", newHashSet("text"), facetResult.getDimensions());
+
+        List<FacetResult.Facet> facets = facetResult.getFacets("text");
+
+        Set<String> facetLabels = newHashSet();
+        for (FacetResult.Facet facet : facets) {
+            assertEquals("Unexpected facet count for " + facet.getLabel(), 1, facet.getCount());
+            facetLabels.add(facet.getLabel());
+        }
+
+        assertEquals("Unexpected facet labels", newHashSet("t1", "t2", "t3"), facetLabels);
+    }
+
+    public void testMergedFacetsOverUnionSummingCount() throws Exception {
+        // the distribution of nodes with t1 and t2 are intentionally across first and second set (below)
+        // put such that second condition turns facet count around
+
+        // first set of nodes matching first condition (x1 = v1)
+        Node n11 = testRootNode.addNode("node11");
+        n11.setProperty("text", "t1");
+        n11.setProperty("x1","v1");
+        Node n12 = testRootNode.addNode("node12");
+        n12.setProperty("text", "t1");
+        n12.setProperty("x1","v1");
+        Node n13 = testRootNode.addNode("node13");
+        n13.setProperty("text", "t2");
+        n13.setProperty("x1","v1");
+
+        // second set of nodes matching second condition (x2 = v2)
+        Node n21 = testRootNode.addNode("node21");
+        n21.setProperty("text", "t2");
+        n21.setProperty("x2","v2");
+        Node n22 = testRootNode.addNode("node22");
+        n22.setProperty("text", "t1");
+        n22.setProperty("x2","v2");
+        Node n23 = testRootNode.addNode("node23");
+        n23.setProperty("text", "t1");
+        n23.setProperty("x2","v2");
+        Node n24 = testRootNode.addNode("node24");
+        n24.setProperty("text", "t1");
+        n24.setProperty("x2","v2");
+
+        superuser.save();
+
+        String xpath = "//*[@x1 = 'v1' or @x2 = 'v2']/(rep:facet(text))";
+
+        Query q = qm.createQuery(xpath, Query.XPATH);
+
+        QueryResult result = q.execute();
+        FacetResult facetResult = new FacetResult(result);
+
+        assertEquals("Unexpected dimensions", newHashSet("text"), facetResult.getDimensions());
+
+        List<FacetResult.Facet> facets = facetResult.getFacets("text");
+        assertEquals("Incorrect facet label list size", 2, facets.size());
+
+        FacetResult.Facet facet = facets.get(0);
+        assertEquals("t1", facet.getLabel());
+        assertEquals(5, facet.getCount());
+
+        facet = facets.get(1);
+        assertEquals("t2", facet.getLabel());
+        assertEquals(2, facet.getCount());
+    }
+
     public Node deny(Node node) throws RepositoryException {
         AccessControlUtils.deny(node, "anonymous", Privilege.JCR_ALL);
         return node;