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