View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.client;
21  
22  import java.io.IOException;
23  import java.nio.BufferOverflowException;
24  import java.nio.ByteBuffer;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Comparator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.NavigableMap;
31  import java.util.TreeMap;
32  
33  import org.apache.hadoop.hbase.Cell;
34  import org.apache.hadoop.hbase.CellComparator;
35  import org.apache.hadoop.hbase.CellScannable;
36  import org.apache.hadoop.hbase.CellScanner;
37  import org.apache.hadoop.hbase.CellUtil;
38  import org.apache.hadoop.hbase.HConstants;
39  import org.apache.hadoop.hbase.KeyValue;
40  import org.apache.hadoop.hbase.KeyValueUtil;
41  import org.apache.hadoop.hbase.classification.InterfaceAudience;
42  import org.apache.hadoop.hbase.classification.InterfaceStability;
43  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
44  import org.apache.hadoop.hbase.util.Bytes;
45  
46  /**
47   * Single row result of a {@link Get} or {@link Scan} query.<p>
48   *
49   * This class is <b>NOT THREAD SAFE</b>.<p>
50   *
51   * Convenience methods are available that return various {@link Map}
52   * structures and values directly.<p>
53   *
54   * To get a complete mapping of all cells in the Result, which can include
55   * multiple families and multiple versions, use {@link #getMap()}.<p>
56   *
57   * To get a mapping of each family to its columns (qualifiers and values),
58   * including only the latest version of each, use {@link #getNoVersionMap()}.
59   *
60   * To get a mapping of qualifiers to latest values for an individual family use
61   * {@link #getFamilyMap(byte[])}.<p>
62   *
63   * To get the latest value for a specific family and qualifier use
64   * {@link #getValue(byte[], byte[])}.
65   *
66   * A Result is backed by an array of {@link Cell} objects, each representing
67   * an HBase cell defined by the row, family, qualifier, timestamp, and value.<p>
68   *
69   * The underlying {@link Cell} objects can be accessed through the method {@link #listCells()}.
70   * This will create a List from the internal Cell []. Better is to exploit the fact that
71   * a new Result instance is a primed {@link CellScanner}; just call {@link #advance()} and
72   * {@link #current()} to iterate over Cells as you would any {@link CellScanner}.
73   * Call {@link #cellScanner()} to reset should you need to iterate the same Result over again
74   * ({@link CellScanner}s are one-shot).
75   *
76   * If you need to overwrite a Result with another Result instance -- as in the old 'mapred'
77   * RecordReader next invocations -- then create an empty Result with the null constructor and
78   * in then use {@link #copyFrom(Result)}
79   */
80  @InterfaceAudience.Public
81  @InterfaceStability.Stable
82  public class Result implements CellScannable, CellScanner {
83    private Cell[] cells;
84    private Boolean exists; // if the query was just to check existence.
85    private boolean stale = false;
86  
87    /**
88     * Partial results do not contain the full row's worth of cells. The result had to be returned in
89     * parts because the size of the cells in the row exceeded the RPC result size on the server.
90     * Partial results must be combined client side with results representing the remainder of the
91     * row's cells to form the complete result. Partial results and RPC result size allow us to avoid
92     * OOME on the server when servicing requests for large rows. The Scan configuration used to
93     * control the result size on the server is {@link Scan#setMaxResultSize(long)} and the default
94     * value can be seen here: {@link HConstants#DEFAULT_HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE}
95     */
96    private boolean partial = false;
97    // We're not using java serialization.  Transient here is just a marker to say
98    // that this is where we cache row if we're ever asked for it.
99    private transient byte [] row = null;
100   // Ditto for familyMap.  It can be composed on fly from passed in kvs.
101   private transient NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
102       familyMap = null;
103 
104   private static ThreadLocal<byte[]> localBuffer = new ThreadLocal<byte[]>();
105   private static final int PAD_WIDTH = 128;
106   public static final Result EMPTY_RESULT = new Result(true);
107 
108   private final static int INITIAL_CELLSCANNER_INDEX = -1;
109 
110   /**
111    * Index for where we are when Result is acting as a {@link CellScanner}.
112    */
113   private int cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
114   private ClientProtos.RegionLoadStats stats;
115 
116   private final boolean readonly;
117 
118   /**
119    * Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}.
120    * Use this to represent no results if {@code null} won't do or in old 'mapred' as opposed
121    * to 'mapreduce' package MapReduce where you need to overwrite a Result instance with a
122    * {@link #copyFrom(Result)} call.
123    */
124   public Result() {
125     this(false);
126   }
127 
128   /**
129    * Allows to construct special purpose immutable Result objects,
130    * such as EMPTY_RESULT.
131    * @param readonly whether this Result instance is readonly
132    */
133   private Result(boolean readonly) {
134     this.readonly = readonly;
135   }
136 
137   /**
138    * Instantiate a Result with the specified List of KeyValues.
139    * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
140    * @param cells List of cells
141    */
142   public static Result create(List<Cell> cells) {
143     return create(cells, null);
144   }
145 
146   public static Result create(List<Cell> cells, Boolean exists) {
147     return create(cells, exists, false);
148   }
149 
150   public static Result create(List<Cell> cells, Boolean exists, boolean stale) {
151     return create(cells, exists, stale, false);
152   }
153 
154   public static Result create(List<Cell> cells, Boolean exists, boolean stale, boolean partial) {
155     if (exists != null){
156       return new Result(null, exists, stale, partial);
157     }
158     return new Result(cells.toArray(new Cell[cells.size()]), null, stale, partial);
159   }
160 
161   /**
162    * Instantiate a Result with the specified array of KeyValues.
163    * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
164    * @param cells array of cells
165    */
166   public static Result create(Cell[] cells) {
167     return create(cells, null, false);
168   }
169 
170   public static Result create(Cell[] cells, Boolean exists, boolean stale) {
171     return create(cells, exists, stale, false);
172   }
173 
174   public static Result create(Cell[] cells, Boolean exists, boolean stale, boolean partial) {
175     if (exists != null){
176       return new Result(null, exists, stale, partial);
177     }
178     return new Result(cells, null, stale, partial);
179   }
180 
181   /** Private ctor. Use {@link #create(Cell[])}. */
182   private Result(Cell[] cells, Boolean exists, boolean stale, boolean partial) {
183     this.cells = cells;
184     this.exists = exists;
185     this.stale = stale;
186     this.partial = partial;
187     this.readonly = false;
188   }
189 
190   /**
191    * Method for retrieving the row key that corresponds to
192    * the row from which this Result was created.
193    * @return row
194    */
195   public byte [] getRow() {
196     if (this.row == null) {
197       this.row = (this.cells == null || this.cells.length == 0) ?
198           null :
199           CellUtil.cloneRow(this.cells[0]);
200     }
201     return this.row;
202   }
203 
204   /**
205    * Return the array of Cells backing this Result instance.
206    *
207    * The array is sorted from smallest -&gt; largest using the
208    * {@link CellComparator#COMPARATOR}.
209    *
210    * The array only contains what your Get or Scan specifies and no more.
211    * For example if you request column "A" 1 version you will have at most 1
212    * Cell in the array. If you request column "A" with 2 version you will
213    * have at most 2 Cells, with the first one being the newer timestamp and
214    * the second being the older timestamp (this is the sort order defined by
215    * {@link CellComparator#COMPARATOR}).  If columns don't exist, they won't be
216    * present in the result. Therefore if you ask for 1 version all columns,
217    * it is safe to iterate over this array and expect to see 1 Cell for
218    * each column and no more.
219    *
220    * This API is faster than using getFamilyMap() and getMap()
221    *
222    * @return array of Cells; can be null if nothing in the result
223    */
224   public Cell[] rawCells() {
225     return cells;
226   }
227 
228   /**
229    * Create a sorted list of the Cell's in this result.
230    *
231    * Since HBase 0.20.5 this is equivalent to raw().
232    *
233    * @return sorted List of Cells; can be null if no cells in the result
234    */
235   public List<Cell> listCells() {
236     return isEmpty()? null: Arrays.asList(rawCells());
237   }
238 
239   /**
240    * Return the Cells for the specific column.  The Cells are sorted in
241    * the {@link CellComparator#COMPARATOR} order.  That implies the first entry in
242    * the list is the most recent column.  If the query (Scan or Get) only
243    * requested 1 version the list will contain at most 1 entry.  If the column
244    * did not exist in the result set (either the column does not exist
245    * or the column was not selected in the query) the list will be empty.
246    *
247    * Also see getColumnLatest which returns just a Cell
248    *
249    * @param family the family
250    * @param qualifier
251    * @return a list of Cells for this column or empty list if the column
252    * did not exist in the result set
253    */
254   public List<Cell> getColumnCells(byte [] family, byte [] qualifier) {
255     List<Cell> result = new ArrayList<Cell>();
256 
257     Cell [] kvs = rawCells();
258 
259     if (kvs == null || kvs.length == 0) {
260       return result;
261     }
262     int pos = binarySearch(kvs, family, qualifier);
263     if (pos == -1) {
264       return result; // cant find it
265     }
266 
267     for (int i = pos; i < kvs.length; i++) {
268       if (CellUtil.matchingColumn(kvs[i], family,qualifier)) {
269         result.add(kvs[i]);
270       } else {
271         break;
272       }
273     }
274 
275     return result;
276   }
277 
278   protected int binarySearch(final Cell [] kvs,
279                              final byte [] family,
280                              final byte [] qualifier) {
281     Cell searchTerm =
282         KeyValueUtil.createFirstOnRow(CellUtil.cloneRow(kvs[0]),
283             family, qualifier);
284 
285     // pos === ( -(insertion point) - 1)
286     int pos = Arrays.binarySearch(kvs, searchTerm, CellComparator.COMPARATOR);
287     // never will exact match
288     if (pos < 0) {
289       pos = (pos+1) * -1;
290       // pos is now insertion point
291     }
292     if (pos == kvs.length) {
293       return -1; // doesn't exist
294     }
295     return pos;
296   }
297 
298   /**
299    * Searches for the latest value for the specified column.
300    *
301    * @param kvs the array to search
302    * @param family family name
303    * @param foffset family offset
304    * @param flength family length
305    * @param qualifier column qualifier
306    * @param qoffset qualifier offset
307    * @param qlength qualifier length
308    *
309    * @return the index where the value was found, or -1 otherwise
310    */
311   protected int binarySearch(final Cell [] kvs,
312       final byte [] family, final int foffset, final int flength,
313       final byte [] qualifier, final int qoffset, final int qlength) {
314 
315     double keyValueSize = (double)
316         KeyValue.getKeyValueDataStructureSize(kvs[0].getRowLength(), flength, qlength, 0);
317 
318     byte[] buffer = localBuffer.get();
319     if (buffer == null || keyValueSize > buffer.length) {
320       // pad to the smallest multiple of the pad width
321       buffer = new byte[(int) Math.ceil(keyValueSize / PAD_WIDTH) * PAD_WIDTH];
322       localBuffer.set(buffer);
323     }
324 
325     Cell searchTerm = KeyValueUtil.createFirstOnRow(buffer, 0,
326         kvs[0].getRowArray(), kvs[0].getRowOffset(), kvs[0].getRowLength(),
327         family, foffset, flength,
328         qualifier, qoffset, qlength);
329 
330     // pos === ( -(insertion point) - 1)
331     int pos = Arrays.binarySearch(kvs, searchTerm, CellComparator.COMPARATOR);
332     // never will exact match
333     if (pos < 0) {
334       pos = (pos+1) * -1;
335       // pos is now insertion point
336     }
337     if (pos == kvs.length) {
338       return -1; // doesn't exist
339     }
340     return pos;
341   }
342 
343   /**
344    * The Cell for the most recent timestamp for a given column.
345    *
346    * @param family
347    * @param qualifier
348    *
349    * @return the Cell for the column, or null if no value exists in the row or none have been
350    * selected in the query (Get/Scan)
351    */
352   public Cell getColumnLatestCell(byte [] family, byte [] qualifier) {
353     Cell [] kvs = rawCells(); // side effect possibly.
354     if (kvs == null || kvs.length == 0) {
355       return null;
356     }
357     int pos = binarySearch(kvs, family, qualifier);
358     if (pos == -1) {
359       return null;
360     }
361     if (CellUtil.matchingColumn(kvs[pos], family, qualifier)) {
362       return kvs[pos];
363     }
364     return null;
365   }
366 
367   /**
368    * The Cell for the most recent timestamp for a given column.
369    *
370    * @param family family name
371    * @param foffset family offset
372    * @param flength family length
373    * @param qualifier column qualifier
374    * @param qoffset qualifier offset
375    * @param qlength qualifier length
376    *
377    * @return the Cell for the column, or null if no value exists in the row or none have been
378    * selected in the query (Get/Scan)
379    */
380   public Cell getColumnLatestCell(byte [] family, int foffset, int flength,
381       byte [] qualifier, int qoffset, int qlength) {
382 
383     Cell [] kvs = rawCells(); // side effect possibly.
384     if (kvs == null || kvs.length == 0) {
385       return null;
386     }
387     int pos = binarySearch(kvs, family, foffset, flength, qualifier, qoffset, qlength);
388     if (pos == -1) {
389       return null;
390     }
391     if (CellUtil.matchingColumn(kvs[pos], family, foffset, flength, qualifier, qoffset, qlength)) {
392       return kvs[pos];
393     }
394     return null;
395   }
396 
397   /**
398    * Get the latest version of the specified column.
399    * Note: this call clones the value content of the hosting Cell. See
400    * {@link #getValueAsByteBuffer(byte[], byte[])}, etc., or {@link #listCells()} if you would
401    * avoid the cloning.
402    * @param family family name
403    * @param qualifier column qualifier
404    * @return value of latest version of column, null if none found
405    */
406   public byte[] getValue(byte [] family, byte [] qualifier) {
407     Cell kv = getColumnLatestCell(family, qualifier);
408     if (kv == null) {
409       return null;
410     }
411     return CellUtil.cloneValue(kv);
412   }
413 
414   /**
415    * Returns the value wrapped in a new <code>ByteBuffer</code>.
416    *
417    * @param family family name
418    * @param qualifier column qualifier
419    *
420    * @return the latest version of the column, or <code>null</code> if none found
421    */
422   public ByteBuffer getValueAsByteBuffer(byte [] family, byte [] qualifier) {
423 
424     Cell kv = getColumnLatestCell(family, 0, family.length, qualifier, 0, qualifier.length);
425 
426     if (kv == null) {
427       return null;
428     }
429     return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()).
430       asReadOnlyBuffer();
431   }
432 
433   /**
434    * Returns the value wrapped in a new <code>ByteBuffer</code>.
435    *
436    * @param family family name
437    * @param foffset family offset
438    * @param flength family length
439    * @param qualifier column qualifier
440    * @param qoffset qualifier offset
441    * @param qlength qualifier length
442    *
443    * @return the latest version of the column, or <code>null</code> if none found
444    */
445   public ByteBuffer getValueAsByteBuffer(byte [] family, int foffset, int flength,
446       byte [] qualifier, int qoffset, int qlength) {
447 
448     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
449 
450     if (kv == null) {
451       return null;
452     }
453     return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()).
454       asReadOnlyBuffer();
455   }
456 
457   /**
458    * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
459    * <p>
460    * Does not clear or flip the buffer.
461    *
462    * @param family family name
463    * @param qualifier column qualifier
464    * @param dst the buffer where to write the value
465    *
466    * @return <code>true</code> if a value was found, <code>false</code> otherwise
467    *
468    * @throws BufferOverflowException there is insufficient space remaining in the buffer
469    */
470   public boolean loadValue(byte [] family, byte [] qualifier, ByteBuffer dst)
471           throws BufferOverflowException {
472     return loadValue(family, 0, family.length, qualifier, 0, qualifier.length, dst);
473   }
474 
475   /**
476    * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
477    * <p>
478    * Does not clear or flip the buffer.
479    *
480    * @param family family name
481    * @param foffset family offset
482    * @param flength family length
483    * @param qualifier column qualifier
484    * @param qoffset qualifier offset
485    * @param qlength qualifier length
486    * @param dst the buffer where to write the value
487    *
488    * @return <code>true</code> if a value was found, <code>false</code> otherwise
489    *
490    * @throws BufferOverflowException there is insufficient space remaining in the buffer
491    */
492   public boolean loadValue(byte [] family, int foffset, int flength,
493       byte [] qualifier, int qoffset, int qlength, ByteBuffer dst)
494           throws BufferOverflowException {
495     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
496 
497     if (kv == null) {
498       return false;
499     }
500     dst.put(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
501     return true;
502   }
503 
504   /**
505    * Checks if the specified column contains a non-empty value (not a zero-length byte array).
506    *
507    * @param family family name
508    * @param qualifier column qualifier
509    *
510    * @return whether or not a latest value exists and is not empty
511    */
512   public boolean containsNonEmptyColumn(byte [] family, byte [] qualifier) {
513 
514     return containsNonEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
515   }
516 
517   /**
518    * Checks if the specified column contains a non-empty value (not a zero-length byte array).
519    *
520    * @param family family name
521    * @param foffset family offset
522    * @param flength family length
523    * @param qualifier column qualifier
524    * @param qoffset qualifier offset
525    * @param qlength qualifier length
526    *
527    * @return whether or not a latest value exists and is not empty
528    */
529   public boolean containsNonEmptyColumn(byte [] family, int foffset, int flength,
530       byte [] qualifier, int qoffset, int qlength) {
531 
532     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
533 
534     return (kv != null) && (kv.getValueLength() > 0);
535   }
536 
537   /**
538    * Checks if the specified column contains an empty value (a zero-length byte array).
539    *
540    * @param family family name
541    * @param qualifier column qualifier
542    *
543    * @return whether or not a latest value exists and is empty
544    */
545   public boolean containsEmptyColumn(byte [] family, byte [] qualifier) {
546 
547     return containsEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
548   }
549 
550   /**
551    * Checks if the specified column contains an empty value (a zero-length byte array).
552    *
553    * @param family family name
554    * @param foffset family offset
555    * @param flength family length
556    * @param qualifier column qualifier
557    * @param qoffset qualifier offset
558    * @param qlength qualifier length
559    *
560    * @return whether or not a latest value exists and is empty
561    */
562   public boolean containsEmptyColumn(byte [] family, int foffset, int flength,
563       byte [] qualifier, int qoffset, int qlength) {
564     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
565 
566     return (kv != null) && (kv.getValueLength() == 0);
567   }
568 
569   /**
570    * Checks for existence of a value for the specified column (empty or not).
571    *
572    * @param family family name
573    * @param qualifier column qualifier
574    *
575    * @return true if at least one value exists in the result, false if not
576    */
577   public boolean containsColumn(byte [] family, byte [] qualifier) {
578     Cell kv = getColumnLatestCell(family, qualifier);
579     return kv != null;
580   }
581 
582   /**
583    * Checks for existence of a value for the specified column (empty or not).
584    *
585    * @param family family name
586    * @param foffset family offset
587    * @param flength family length
588    * @param qualifier column qualifier
589    * @param qoffset qualifier offset
590    * @param qlength qualifier length
591    *
592    * @return true if at least one value exists in the result, false if not
593    */
594   public boolean containsColumn(byte [] family, int foffset, int flength,
595       byte [] qualifier, int qoffset, int qlength) {
596 
597     return getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength) != null;
598   }
599 
600   /**
601    * Map of families to all versions of its qualifiers and values.
602    * <p>
603    * Returns a three level Map of the form:
604    * <code>Map&amp;family,Map&lt;qualifier,Map&lt;timestamp,value&gt;&gt;&gt;</code>
605    * <p>
606    * Note: All other map returning methods make use of this map internally.
607    * @return map from families to qualifiers to versions
608    */
609   public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> getMap() {
610     if (this.familyMap != null) {
611       return this.familyMap;
612     }
613     if(isEmpty()) {
614       return null;
615     }
616     this.familyMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
617     for(Cell kv : this.cells) {
618       byte [] family = CellUtil.cloneFamily(kv);
619       NavigableMap<byte[], NavigableMap<Long, byte[]>> columnMap = familyMap.get(family);
620       if(columnMap == null) {
621         columnMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
622         familyMap.put(family, columnMap);
623       }
624       byte [] qualifier = CellUtil.cloneQualifier(kv);
625       NavigableMap<Long, byte[]> versionMap = columnMap.get(qualifier);
626       if(versionMap == null) {
627         versionMap = new TreeMap<>(new Comparator<Long>() {
628           @Override
629           public int compare(Long l1, Long l2) {
630             return l2.compareTo(l1);
631           }
632         });
633         columnMap.put(qualifier, versionMap);
634       }
635       Long timestamp = kv.getTimestamp();
636       byte [] value = CellUtil.cloneValue(kv);
637 
638       versionMap.put(timestamp, value);
639     }
640     return this.familyMap;
641   }
642 
643   /**
644    * Map of families to their most recent qualifiers and values.
645    * <p>
646    * Returns a two level Map of the form: <code>Map&amp;family,Map&lt;qualifier,value&gt;&gt;</code>
647    * <p>
648    * The most recent version of each qualifier will be used.
649    * @return map from families to qualifiers and value
650    */
651   public NavigableMap<byte[], NavigableMap<byte[], byte[]>> getNoVersionMap() {
652     if(this.familyMap == null) {
653       getMap();
654     }
655     if(isEmpty()) {
656       return null;
657     }
658     NavigableMap<byte[], NavigableMap<byte[], byte[]>> returnMap =
659       new TreeMap<byte[], NavigableMap<byte[], byte[]>>(Bytes.BYTES_COMPARATOR);
660     for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
661       familyEntry : familyMap.entrySet()) {
662       NavigableMap<byte[], byte[]> qualifierMap =
663         new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
664       for(Map.Entry<byte[], NavigableMap<Long, byte[]>> qualifierEntry :
665         familyEntry.getValue().entrySet()) {
666         byte [] value =
667           qualifierEntry.getValue().get(qualifierEntry.getValue().firstKey());
668         qualifierMap.put(qualifierEntry.getKey(), value);
669       }
670       returnMap.put(familyEntry.getKey(), qualifierMap);
671     }
672     return returnMap;
673   }
674 
675   /**
676    * Map of qualifiers to values.
677    * <p>
678    * Returns a Map of the form: <code>Map&lt;qualifier,value&gt;</code>
679    * @param family column family to get
680    * @return map of qualifiers to values
681    */
682   public NavigableMap<byte[], byte[]> getFamilyMap(byte [] family) {
683     if(this.familyMap == null) {
684       getMap();
685     }
686     if(isEmpty()) {
687       return null;
688     }
689     NavigableMap<byte[], byte[]> returnMap =
690       new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
691     NavigableMap<byte[], NavigableMap<Long, byte[]>> qualifierMap =
692       familyMap.get(family);
693     if(qualifierMap == null) {
694       return returnMap;
695     }
696     for(Map.Entry<byte[], NavigableMap<Long, byte[]>> entry :
697       qualifierMap.entrySet()) {
698       byte [] value =
699         entry.getValue().get(entry.getValue().firstKey());
700       returnMap.put(entry.getKey(), value);
701     }
702     return returnMap;
703   }
704 
705   /**
706    * Returns the value of the first column in the Result.
707    * @return value of the first column
708    */
709   public byte [] value() {
710     if (isEmpty()) {
711       return null;
712     }
713     return CellUtil.cloneValue(cells[0]);
714   }
715 
716   /**
717    * Check if the underlying Cell [] is empty or not
718    * @return true if empty
719    */
720   public boolean isEmpty() {
721     return this.cells == null || this.cells.length == 0;
722   }
723 
724   /**
725    * @return the size of the underlying Cell []
726    */
727   public int size() {
728     return this.cells == null? 0: this.cells.length;
729   }
730 
731   /**
732    * @return String
733    */
734   @Override
735   public String toString() {
736     StringBuilder sb = new StringBuilder();
737     sb.append("keyvalues=");
738     if(isEmpty()) {
739       sb.append("NONE");
740       return sb.toString();
741     }
742     sb.append("{");
743     boolean moreThanOne = false;
744     for(Cell kv : this.cells) {
745       if(moreThanOne) {
746         sb.append(", ");
747       } else {
748         moreThanOne = true;
749       }
750       sb.append(kv.toString());
751     }
752     sb.append("}");
753     return sb.toString();
754   }
755 
756   /**
757    * Does a deep comparison of two Results, down to the byte arrays.
758    * @param res1 first result to compare
759    * @param res2 second result to compare
760    * @throws Exception Every difference is throwing an exception
761    */
762   public static void compareResults(Result res1, Result res2)
763       throws Exception {
764     if (res2 == null) {
765       throw new Exception("There wasn't enough rows, we stopped at "
766           + Bytes.toStringBinary(res1.getRow()));
767     }
768     if (res1.size() != res2.size()) {
769       throw new Exception("This row doesn't have the same number of KVs: "
770           + res1.toString() + " compared to " + res2.toString());
771     }
772     Cell[] ourKVs = res1.rawCells();
773     Cell[] replicatedKVs = res2.rawCells();
774     for (int i = 0; i < res1.size(); i++) {
775       if (!ourKVs[i].equals(replicatedKVs[i]) ||
776           !Bytes.equals(CellUtil.cloneValue(ourKVs[i]), CellUtil.cloneValue(replicatedKVs[i]))) {
777         throw new Exception("This result was different: "
778             + res1.toString() + " compared to " + res2.toString());
779       }
780     }
781   }
782 
783   /**
784    * Forms a single result from the partial results in the partialResults list. This method is
785    * useful for reconstructing partial results on the client side.
786    * @param partialResults list of partial results
787    * @return The complete result that is formed by combining all of the partial results together
788    * @throws IOException A complete result cannot be formed because the results in the partial list
789    *           come from different rows
790    */
791   public static Result createCompleteResult(List<Result> partialResults)
792       throws IOException {
793     List<Cell> cells = new ArrayList<Cell>();
794     boolean stale = false;
795     byte[] prevRow = null;
796     byte[] currentRow = null;
797 
798     if (partialResults != null && !partialResults.isEmpty()) {
799       for (int i = 0; i < partialResults.size(); i++) {
800         Result r = partialResults.get(i);
801         currentRow = r.getRow();
802         if (prevRow != null && !Bytes.equals(prevRow, currentRow)) {
803           throw new IOException(
804               "Cannot form complete result. Rows of partial results do not match." +
805                   " Partial Results: " + partialResults);
806         }
807 
808         // Ensure that all Results except the last one are marked as partials. The last result
809         // may not be marked as a partial because Results are only marked as partials when
810         // the scan on the server side must be stopped due to reaching the maxResultSize.
811         // Visualizing it makes it easier to understand:
812         // maxResultSize: 2 cells
813         // (-x-) represents cell number x in a row
814         // Example: row1: -1- -2- -3- -4- -5- (5 cells total)
815         // How row1 will be returned by the server as partial Results:
816         // Result1: -1- -2- (2 cells, size limit reached, mark as partial)
817         // Result2: -3- -4- (2 cells, size limit reached, mark as partial)
818         // Result3: -5- (1 cell, size limit NOT reached, NOT marked as partial)
819         if (i != (partialResults.size() - 1) && !r.isPartial()) {
820           throw new IOException(
821               "Cannot form complete result. Result is missing partial flag. " +
822                   "Partial Results: " + partialResults);
823         }
824         prevRow = currentRow;
825         stale = stale || r.isStale();
826         for (Cell c : r.rawCells()) {
827           cells.add(c);
828         }
829       }
830     }
831 
832     return Result.create(cells, null, stale);
833   }
834 
835   /**
836    * Get total size of raw cells
837    * @param result
838    * @return Total size.
839    */
840   public static long getTotalSizeOfCells(Result result) {
841     long size = 0;
842     for (Cell c : result.rawCells()) {
843       size += CellUtil.estimatedHeapSizeOf(c);
844     }
845     return size;
846   }
847 
848   /**
849    * Copy another Result into this one. Needed for the old Mapred framework
850    * @throws UnsupportedOperationException if invoked on instance of EMPTY_RESULT
851    * (which is supposed to be immutable).
852    * @param other
853    */
854   public void copyFrom(Result other) {
855     checkReadonly();
856     this.row = null;
857     this.familyMap = null;
858     this.cells = other.cells;
859   }
860 
861   @Override
862   public CellScanner cellScanner() {
863     // Reset
864     this.cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
865     return this;
866   }
867 
868   @Override
869   public Cell current() {
870     if (cells == null) return null;
871     return (cellScannerIndex < 0)? null: this.cells[cellScannerIndex];
872   }
873 
874   @Override
875   public boolean advance() {
876     if (cells == null) return false;
877     return ++cellScannerIndex < this.cells.length;
878   }
879 
880   public Boolean getExists() {
881     return exists;
882   }
883 
884   public void setExists(Boolean exists) {
885     checkReadonly();
886     this.exists = exists;
887   }
888 
889   /**
890    * Whether or not the results are coming from possibly stale data. Stale results
891    * might be returned if {@link Consistency} is not STRONG for the query.
892    * @return Whether or not the results are coming from possibly stale data.
893    */
894   public boolean isStale() {
895     return stale;
896   }
897 
898   /**
899    * Whether or not the result is a partial result. Partial results contain a subset of the cells
900    * for a row and should be combined with a result representing the remaining cells in that row to
901    * form a complete (non-partial) result.
902    * @return Whether or not the result is a partial result
903    */
904   public boolean isPartial() {
905     return partial;
906   }
907 
908   /**
909    * Add load information about the region to the information about the result
910    * @param loadStats statistics about the current region from which this was returned
911    * @deprecated use {@link #setStatistics(ClientProtos.RegionLoadStats)} instead
912    * @throws UnsupportedOperationException if invoked on instance of EMPTY_RESULT
913    * (which is supposed to be immutable).
914    */
915   @InterfaceAudience.Private
916   @Deprecated
917   public void addResults(ClientProtos.RegionLoadStats loadStats) {
918     checkReadonly();
919     this.stats = loadStats;
920   }
921 
922   /**
923    * Set load information about the region to the information about the result
924    * @param loadStats statistics about the current region from which this was returned
925    */
926   @InterfaceAudience.Private
927   public void setStatistics(ClientProtos.RegionLoadStats loadStats) {
928     this.stats = loadStats;
929   }
930 
931   /**
932    * @return the associated statistics about the region from which this was returned. Can be
933    * <tt>null</tt> if stats are disabled.
934    */
935   public ClientProtos.RegionLoadStats getStats() {
936     return stats;
937   }
938 
939   /**
940    * All methods modifying state of Result object must call this method
941    * to ensure that special purpose immutable Results can't be accidentally modified.
942    */
943   private void checkReadonly() {
944     if (readonly == true) {
945       throw new UnsupportedOperationException("Attempting to modify readonly EMPTY_RESULT!");
946     }
947   }
948 }