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.nio.BufferOverflowException;
23  import java.nio.ByteBuffer;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Comparator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.NavigableMap;
30  import java.util.TreeMap;
31  
32  import org.apache.hadoop.classification.InterfaceAudience;
33  import org.apache.hadoop.classification.InterfaceStability;
34  import org.apache.hadoop.hbase.Cell;
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.KeyValue;
39  import org.apache.hadoop.hbase.KeyValueUtil;
40  import org.apache.hadoop.hbase.util.Bytes;
41  
42  /**
43   * Single row result of a {@link Get} or {@link Scan} query.<p>
44   *
45   * This class is <b>NOT THREAD SAFE</b>.<p>
46   *
47   * Convenience methods are available that return various {@link Map}
48   * structures and values directly.<p>
49   *
50   * To get a complete mapping of all cells in the Result, which can include
51   * multiple families and multiple versions, use {@link #getMap()}.<p>
52   *
53   * To get a mapping of each family to its columns (qualifiers and values),
54   * including only the latest version of each, use {@link #getNoVersionMap()}.
55   *
56   * To get a mapping of qualifiers to latest values for an individual family use
57   * {@link #getFamilyMap(byte[])}.<p>
58   *
59   * To get the latest value for a specific family and qualifier use {@link #getValue(byte[], byte[])}.
60   *
61   * A Result is backed by an array of {@link KeyValue} objects, each representing
62   * an HBase cell defined by the row, family, qualifier, timestamp, and value.<p>
63   *
64   * The underlying {@link KeyValue} objects can be accessed through the method {@link #listCells()}.
65   * Each KeyValue can then be accessed through
66   * {@link KeyValue#getRow()}, {@link KeyValue#getFamily()}, {@link KeyValue#getQualifier()},
67   * {@link KeyValue#getTimestamp()}, and {@link KeyValue#getValue()}.<p>
68   *
69   * If you need to overwrite a Result with another Result instance -- as in the old 'mapred' RecordReader next
70   * invocations -- then create an empty Result with the null constructor and in then use {@link #copyFrom(Result)}
71   */
72  @InterfaceAudience.Public
73  @InterfaceStability.Stable
74  public class Result implements CellScannable {
75    private Cell[] cells;
76    private Boolean exists; // if the query was just to check existence.
77    // We're not using java serialization.  Transient here is just a marker to say
78    // that this is where we cache row if we're ever asked for it.
79    private transient byte [] row = null;
80    // Ditto for familyMap.  It can be composed on fly from passed in kvs.
81    private transient NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> familyMap = null;
82  
83    // never use directly
84    private static byte [] buffer = null;
85    private static final int PAD_WIDTH = 128;
86    public static final Result EMPTY_RESULT = new Result();
87  
88    /**
89     * Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}.
90     * Use this to represent no results if <code>null</code> won't do or in old 'mapred' as oppposed to 'mapreduce' package
91     * MapReduce where you need to overwrite a Result
92     * instance with a {@link #copyFrom(Result)} call.
93     */
94    public Result() {
95      super();
96    }
97  
98    /**
99     * @deprecated Use {@link #create(List)} instead.
100    */
101   @Deprecated
102   public Result(KeyValue [] cells) {
103     this.cells = cells;
104   }
105 
106   /**
107    * @deprecated Use {@link #create(List)} instead.
108    */
109   @Deprecated
110   public Result(List<KeyValue> kvs) {
111     // TODO: Here we presume the passed in Cells are KVs.  One day this won't always be so.
112     this(kvs.toArray(new Cell[kvs.size()]), null);
113   }
114 
115   /**
116    * Instantiate a Result with the specified List of KeyValues.
117    * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
118    * @param cells List of cells
119    */
120   public static Result create(List<Cell> cells) {
121     return new Result(cells.toArray(new Cell[cells.size()]), null);
122   }
123 
124   public static Result create(List<Cell> cells, Boolean exists) {
125     if (exists != null){
126       return new Result(null, exists);
127     }
128     return new Result(cells.toArray(new Cell[cells.size()]), null);
129   }
130 
131   /**
132    * Instantiate a Result with the specified array of KeyValues.
133    * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
134    * @param cells array of cells
135    */
136   public static Result create(Cell[] cells) {
137     return new Result(cells, null);
138   }
139 
140   /** Private ctor. Use {@link #create(Cell[])}. */
141   private Result(Cell[] cells, Boolean exists) {
142     this.cells = cells;
143     this.exists = exists;
144   }
145 
146   /**
147    * Method for retrieving the row key that corresponds to
148    * the row from which this Result was created.
149    * @return row
150    */
151   public byte [] getRow() {
152     if (this.row == null) {
153       this.row = this.cells == null || this.cells.length == 0? null: CellUtil.cloneRow(this.cells[0]);
154     }
155     return this.row;
156   }
157 
158   /**
159    * Return the array of Cells backing this Result instance.
160    *
161    * The array is sorted from smallest -> largest using the
162    * {@link KeyValue#COMPARATOR}.
163    *
164    * The array only contains what your Get or Scan specifies and no more.
165    * For example if you request column "A" 1 version you will have at most 1
166    * Cell in the array. If you request column "A" with 2 version you will
167    * have at most 2 Cells, with the first one being the newer timestamp and
168    * the second being the older timestamp (this is the sort order defined by
169    * {@link KeyValue#COMPARATOR}).  If columns don't exist, they won't be
170    * present in the result. Therefore if you ask for 1 version all columns,
171    * it is safe to iterate over this array and expect to see 1 Cell for
172    * each column and no more.
173    *
174    * This API is faster than using getFamilyMap() and getMap()
175    *
176    * @return array of Cells; can be null if nothing in the result
177    */
178   public Cell[] rawCells() {
179     return cells;
180   }
181 
182   /**
183    * Return an cells of a Result as an array of KeyValues 
184    * 
185    * WARNING do not use, expensive.  This does an arraycopy of the cell[]'s value.
186    *
187    * Added to ease transition from  0.94 -> 0.96.
188    * 
189    * @deprecated as of 0.96, use {@link #rawCells()}  
190    * @return array of KeyValues, empty array if nothing in result.
191    */
192   @Deprecated
193   public KeyValue[] raw() {
194     KeyValue[] kvs = new KeyValue[cells.length];
195     for (int i = 0 ; i < kvs.length; i++) {
196       kvs[i] = KeyValueUtil.ensureKeyValue(cells[i]);
197     }
198     return kvs;
199   }
200 
201   /**
202    * Create a sorted list of the Cell's in this result.
203    *
204    * Since HBase 0.20.5 this is equivalent to raw().
205    *
206    * @return sorted List of Cells; can be null if no cells in the result
207    */
208   public List<Cell> listCells() {
209     return isEmpty()? null: Arrays.asList(rawCells());
210   }
211   
212   /**
213    * Return an cells of a Result as an array of KeyValues 
214    * 
215    * WARNING do not use, expensive.  This does  an arraycopy of the cell[]'s value.
216    *
217    * Added to ease transition from  0.94 -> 0.96.
218    * 
219    * @deprecated as of 0.96, use {@link #listCells()}  
220    * @return all sorted List of KeyValues; can be null if no cells in the result
221    */
222   @Deprecated
223   public List<KeyValue> list() {
224     return isEmpty() ? null : Arrays.asList(raw());
225   }
226 
227   /**
228    * @deprecated Use {@link #getColumnCells(byte[], byte[])} instead.
229    */
230   @Deprecated
231   public List<KeyValue> getColumn(byte [] family, byte [] qualifier) {
232     return KeyValueUtil.ensureKeyValues(getColumnCells(family, qualifier));
233   }
234 
235   /**
236    * Return the Cells for the specific column.  The Cells are sorted in
237    * the {@link KeyValue#COMPARATOR} order.  That implies the first entry in
238    * the list is the most recent column.  If the query (Scan or Get) only
239    * requested 1 version the list will contain at most 1 entry.  If the column
240    * did not exist in the result set (either the column does not exist
241    * or the column was not selected in the query) the list will be empty.
242    *
243    * Also see getColumnLatest which returns just a Cell
244    *
245    * @param family the family
246    * @param qualifier
247    * @return a list of Cells for this column or empty list if the column
248    * did not exist in the result set
249    */
250   public List<Cell> getColumnCells(byte [] family, byte [] qualifier) {
251     List<Cell> result = new ArrayList<Cell>();
252 
253     Cell [] kvs = rawCells();
254 
255     if (kvs == null || kvs.length == 0) {
256       return result;
257     }
258     int pos = binarySearch(kvs, family, qualifier);
259     if (pos == -1) {
260       return result; // cant find it
261     }
262 
263     for (int i = pos ; i < kvs.length ; i++ ) {
264       KeyValue kv = KeyValueUtil.ensureKeyValue(kvs[i]);
265       if (kv.matchingColumn(family,qualifier)) {
266         result.add(kv);
267       } else {
268         break;
269       }
270     }
271 
272     return result;
273   }
274 
275   protected int binarySearch(final Cell [] kvs,
276                              final byte [] family,
277                              final byte [] qualifier) {
278     Cell searchTerm =
279         KeyValue.createFirstOnRow(CellUtil.cloneRow(kvs[0]),
280             family, qualifier);
281 
282     // pos === ( -(insertion point) - 1)
283     int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
284     // never will exact match
285     if (pos < 0) {
286       pos = (pos+1) * -1;
287       // pos is now insertion point
288     }
289     if (pos == kvs.length) {
290       return -1; // doesn't exist
291     }
292     return pos;
293   }
294 
295   /**
296    * Searches for the latest value for the specified column.
297    *
298    * @param kvs the array to search
299    * @param family family name
300    * @param foffset family offset
301    * @param flength family length
302    * @param qualifier column qualifier
303    * @param qoffset qualifier offset
304    * @param qlength qualifier length
305    *
306    * @return the index where the value was found, or -1 otherwise
307    */
308   protected int binarySearch(final Cell [] kvs,
309       final byte [] family, final int foffset, final int flength,
310       final byte [] qualifier, final int qoffset, final int qlength) {
311 
312     double keyValueSize = (double)
313         KeyValue.getKeyValueDataStructureSize(kvs[0].getRowLength(), flength, qlength, 0);
314 
315     if (buffer == null || keyValueSize > buffer.length) {
316       // pad to the smallest multiple of the pad width
317       buffer = new byte[(int) Math.ceil(keyValueSize / PAD_WIDTH) * PAD_WIDTH];
318     }
319 
320     Cell searchTerm = KeyValue.createFirstOnRow(buffer, 0,
321         kvs[0].getRowArray(), kvs[0].getRowOffset(), kvs[0].getRowLength(),
322         family, foffset, flength,
323         qualifier, qoffset, qlength);
324 
325     // pos === ( -(insertion point) - 1)
326     int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
327     // never will exact match
328     if (pos < 0) {
329       pos = (pos+1) * -1;
330       // pos is now insertion point
331     }
332     if (pos == kvs.length) {
333       return -1; // doesn't exist
334     }
335     return pos;
336   }
337 
338   /**
339    * @deprecated Use {@link #getColumnLatestCell(byte[], byte[])} instead.
340    */
341   @Deprecated
342   public KeyValue getColumnLatest(byte [] family, byte [] qualifier) {
343     return KeyValueUtil.ensureKeyValue(getColumnLatestCell(family, qualifier));
344   }
345 
346   /**
347    * The Cell for the most recent timestamp for a given column.
348    *
349    * @param family
350    * @param qualifier
351    *
352    * @return the Cell for the column, or null if no value exists in the row or none have been
353    * selected in the query (Get/Scan)
354    */
355   public Cell getColumnLatestCell(byte [] family, byte [] qualifier) {
356     Cell [] kvs = rawCells(); // side effect possibly.
357     if (kvs == null || kvs.length == 0) {
358       return null;
359     }
360     int pos = binarySearch(kvs, family, qualifier);
361     if (pos == -1) {
362       return null;
363     }
364     KeyValue kv = KeyValueUtil.ensureKeyValue(kvs[pos]);
365     if (kv.matchingColumn(family, qualifier)) {
366       return kv;
367     }
368     return null;
369   }
370 
371   /**
372    * @deprecated Use {@link #getColumnLatestCell(byte[], int, int, byte[], int, int)} instead.
373    */
374   @Deprecated
375   public KeyValue getColumnLatest(byte [] family, int foffset, int flength,
376       byte [] qualifier, int qoffset, int qlength) {
377     return KeyValueUtil.ensureKeyValue(
378         getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength));
379   }
380 
381   /**
382    * The Cell for the most recent timestamp for a given column.
383    *
384    * @param family family name
385    * @param foffset family offset
386    * @param flength family length
387    * @param qualifier column qualifier
388    * @param qoffset qualifier offset
389    * @param qlength qualifier length
390    *
391    * @return the Cell for the column, or null if no value exists in the row or none have been
392    * selected in the query (Get/Scan)
393    */
394   public Cell getColumnLatestCell(byte [] family, int foffset, int flength,
395       byte [] qualifier, int qoffset, int qlength) {
396 
397     Cell [] kvs = rawCells(); // side effect possibly.
398     if (kvs == null || kvs.length == 0) {
399       return null;
400     }
401     int pos = binarySearch(kvs, family, foffset, flength, qualifier, qoffset, qlength);
402     if (pos == -1) {
403       return null;
404     }
405     KeyValue kv = KeyValueUtil.ensureKeyValue(kvs[pos]);
406     if (kv.matchingColumn(family, foffset, flength, qualifier, qoffset, qlength)) {
407       return kv;
408     }
409     return null;
410   }
411 
412   /**
413    * Get the latest version of the specified column.
414    * @param family family name
415    * @param qualifier column qualifier
416    * @return value of latest version of column, null if none found
417    */
418   public byte[] getValue(byte [] family, byte [] qualifier) {
419     Cell kv = getColumnLatestCell(family, qualifier);
420     if (kv == null) {
421       return null;
422     }
423     return CellUtil.cloneValue(kv);
424   }
425 
426   /**
427    * Returns the value wrapped in a new <code>ByteBuffer</code>.
428    *
429    * @param family family name
430    * @param qualifier column qualifier
431    *
432    * @return the latest version of the column, or <code>null</code> if none found
433    */
434   public ByteBuffer getValueAsByteBuffer(byte [] family, byte [] qualifier) {
435 
436     Cell kv = getColumnLatestCell(family, 0, family.length, qualifier, 0, qualifier.length);
437 
438     if (kv == null) {
439       return null;
440     }
441     return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
442   }
443 
444   /**
445    * Returns the value wrapped in a new <code>ByteBuffer</code>.
446    *
447    * @param family family name
448    * @param foffset family offset
449    * @param flength family length
450    * @param qualifier column qualifier
451    * @param qoffset qualifier offset
452    * @param qlength qualifier length
453    *
454    * @return the latest version of the column, or <code>null</code> if none found
455    */
456   public ByteBuffer getValueAsByteBuffer(byte [] family, int foffset, int flength,
457       byte [] qualifier, int qoffset, int qlength) {
458 
459     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
460 
461     if (kv == null) {
462       return null;
463     }
464     return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
465   }
466 
467   /**
468    * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
469    * <p>
470    * Does not clear or flip the buffer.
471    *
472    * @param family family name
473    * @param qualifier column qualifier
474    * @param dst the buffer where to write the value
475    *
476    * @return <code>true</code> if a value was found, <code>false</code> otherwise
477    *
478    * @throws BufferOverflowException there is insufficient space remaining in the buffer
479    */
480   public boolean loadValue(byte [] family, byte [] qualifier, ByteBuffer dst)
481           throws BufferOverflowException {
482     return loadValue(family, 0, family.length, qualifier, 0, qualifier.length, dst);
483   }
484 
485   /**
486    * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
487    * <p>
488    * Does not clear or flip the buffer.
489    *
490    * @param family family name
491    * @param foffset family offset
492    * @param flength family length
493    * @param qualifier column qualifier
494    * @param qoffset qualifier offset
495    * @param qlength qualifier length
496    * @param dst the buffer where to write the value
497    *
498    * @return <code>true</code> if a value was found, <code>false</code> otherwise
499    *
500    * @throws BufferOverflowException there is insufficient space remaining in the buffer
501    */
502   public boolean loadValue(byte [] family, int foffset, int flength,
503       byte [] qualifier, int qoffset, int qlength, ByteBuffer dst)
504           throws BufferOverflowException {
505     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
506 
507     if (kv == null) {
508       return false;
509     }
510     dst.put(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
511     return true;
512   }
513 
514   /**
515    * Checks if the specified column contains a non-empty value (not a zero-length byte array).
516    *
517    * @param family family name
518    * @param qualifier column qualifier
519    *
520    * @return whether or not a latest value exists and is not empty
521    */
522   public boolean containsNonEmptyColumn(byte [] family, byte [] qualifier) {
523 
524     return containsNonEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
525   }
526 
527   /**
528    * Checks if the specified column contains a non-empty value (not a zero-length byte array).
529    *
530    * @param family family name
531    * @param foffset family offset
532    * @param flength family length
533    * @param qualifier column qualifier
534    * @param qoffset qualifier offset
535    * @param qlength qualifier length
536    *
537    * @return whether or not a latest value exists and is not empty
538    */
539   public boolean containsNonEmptyColumn(byte [] family, int foffset, int flength,
540       byte [] qualifier, int qoffset, int qlength) {
541 
542     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
543 
544     return (kv != null) && (kv.getValueLength() > 0);
545   }
546 
547   /**
548    * Checks if the specified column contains an empty value (a zero-length byte array).
549    *
550    * @param family family name
551    * @param qualifier column qualifier
552    *
553    * @return whether or not a latest value exists and is empty
554    */
555   public boolean containsEmptyColumn(byte [] family, byte [] qualifier) {
556 
557     return containsEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
558   }
559 
560   /**
561    * Checks if the specified column contains an empty value (a zero-length byte array).
562    *
563    * @param family family name
564    * @param foffset family offset
565    * @param flength family length
566    * @param qualifier column qualifier
567    * @param qoffset qualifier offset
568    * @param qlength qualifier length
569    *
570    * @return whether or not a latest value exists and is empty
571    */
572   public boolean containsEmptyColumn(byte [] family, int foffset, int flength,
573       byte [] qualifier, int qoffset, int qlength) {
574     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
575 
576     return (kv != null) && (kv.getValueLength() == 0);
577   }
578 
579   /**
580    * Checks for existence of a value for the specified column (empty or not).
581    *
582    * @param family family name
583    * @param qualifier column qualifier
584    *
585    * @return true if at least one value exists in the result, false if not
586    */
587   public boolean containsColumn(byte [] family, byte [] qualifier) {
588     Cell kv = getColumnLatestCell(family, qualifier);
589     return kv != null;
590   }
591 
592   /**
593    * Checks for existence of a value for the specified column (empty or not).
594    *
595    * @param family family name
596    * @param foffset family offset
597    * @param flength family length
598    * @param qualifier column qualifier
599    * @param qoffset qualifier offset
600    * @param qlength qualifier length
601    *
602    * @return true if at least one value exists in the result, false if not
603    */
604   public boolean containsColumn(byte [] family, int foffset, int flength,
605       byte [] qualifier, int qoffset, int qlength) {
606 
607     return getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength) != null;
608   }
609 
610   /**
611    * Map of families to all versions of its qualifiers and values.
612    * <p>
613    * Returns a three level Map of the form:
614    * <code>Map&amp;family,Map&lt;qualifier,Map&lt;timestamp,value>>></code>
615    * <p>
616    * Note: All other map returning methods make use of this map internally.
617    * @return map from families to qualifiers to versions
618    */
619   public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> getMap() {
620     if (this.familyMap != null) {
621       return this.familyMap;
622     }
623     if(isEmpty()) {
624       return null;
625     }
626     this.familyMap = new TreeMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>(Bytes.BYTES_COMPARATOR);
627     for(Cell kv : this.cells) {
628       byte [] family = CellUtil.cloneFamily(kv);
629       NavigableMap<byte[], NavigableMap<Long, byte[]>> columnMap =
630         familyMap.get(family);
631       if(columnMap == null) {
632         columnMap = new TreeMap<byte[], NavigableMap<Long, byte[]>>
633           (Bytes.BYTES_COMPARATOR);
634         familyMap.put(family, columnMap);
635       }
636       byte [] qualifier = CellUtil.cloneQualifier(kv);
637       NavigableMap<Long, byte[]> versionMap = columnMap.get(qualifier);
638       if(versionMap == null) {
639         versionMap = new TreeMap<Long, byte[]>(new Comparator<Long>() {
640           public int compare(Long l1, Long l2) {
641             return l2.compareTo(l1);
642           }
643         });
644         columnMap.put(qualifier, versionMap);
645       }
646       Long timestamp = kv.getTimestamp();
647       byte [] value = CellUtil.cloneValue(kv);
648 
649       versionMap.put(timestamp, value);
650     }
651     return this.familyMap;
652   }
653 
654   /**
655    * Map of families to their most recent qualifiers and values.
656    * <p>
657    * Returns a two level Map of the form: <code>Map&amp;family,Map&lt;qualifier,value>></code>
658    * <p>
659    * The most recent version of each qualifier will be used.
660    * @return map from families to qualifiers and value
661    */
662   public NavigableMap<byte[], NavigableMap<byte[], byte[]>> getNoVersionMap() {
663     if(this.familyMap == null) {
664       getMap();
665     }
666     if(isEmpty()) {
667       return null;
668     }
669     NavigableMap<byte[], NavigableMap<byte[], byte[]>> returnMap =
670       new TreeMap<byte[], NavigableMap<byte[], byte[]>>(Bytes.BYTES_COMPARATOR);
671     for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
672       familyEntry : familyMap.entrySet()) {
673       NavigableMap<byte[], byte[]> qualifierMap =
674         new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
675       for(Map.Entry<byte[], NavigableMap<Long, byte[]>> qualifierEntry :
676         familyEntry.getValue().entrySet()) {
677         byte [] value =
678           qualifierEntry.getValue().get(qualifierEntry.getValue().firstKey());
679         qualifierMap.put(qualifierEntry.getKey(), value);
680       }
681       returnMap.put(familyEntry.getKey(), qualifierMap);
682     }
683     return returnMap;
684   }
685 
686   /**
687    * Map of qualifiers to values.
688    * <p>
689    * Returns a Map of the form: <code>Map&lt;qualifier,value></code>
690    * @param family column family to get
691    * @return map of qualifiers to values
692    */
693   public NavigableMap<byte[], byte[]> getFamilyMap(byte [] family) {
694     if(this.familyMap == null) {
695       getMap();
696     }
697     if(isEmpty()) {
698       return null;
699     }
700     NavigableMap<byte[], byte[]> returnMap =
701       new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
702     NavigableMap<byte[], NavigableMap<Long, byte[]>> qualifierMap =
703       familyMap.get(family);
704     if(qualifierMap == null) {
705       return returnMap;
706     }
707     for(Map.Entry<byte[], NavigableMap<Long, byte[]>> entry :
708       qualifierMap.entrySet()) {
709       byte [] value =
710         entry.getValue().get(entry.getValue().firstKey());
711       returnMap.put(entry.getKey(), value);
712     }
713     return returnMap;
714   }
715 
716   /**
717    * Returns the value of the first column in the Result.
718    * @return value of the first column
719    */
720   public byte [] value() {
721     if (isEmpty()) {
722       return null;
723     }
724     return CellUtil.cloneValue(cells[0]);
725   }
726 
727   /**
728    * Check if the underlying Cell [] is empty or not
729    * @return true if empty
730    */
731   public boolean isEmpty() {
732     return this.cells == null || this.cells.length == 0;
733   }
734 
735   /**
736    * @return the size of the underlying Cell []
737    */
738   public int size() {
739     return this.cells == null? 0: this.cells.length;
740   }
741 
742   /**
743    * @return String
744    */
745   @Override
746   public String toString() {
747     StringBuilder sb = new StringBuilder();
748     sb.append("keyvalues=");
749     if(isEmpty()) {
750       sb.append("NONE");
751       return sb.toString();
752     }
753     sb.append("{");
754     boolean moreThanOne = false;
755     for(Cell kv : this.cells) {
756       if(moreThanOne) {
757         sb.append(", ");
758       } else {
759         moreThanOne = true;
760       }
761       sb.append(kv.toString());
762     }
763     sb.append("}");
764     return sb.toString();
765   }
766 
767   /**
768    * Does a deep comparison of two Results, down to the byte arrays.
769    * @param res1 first result to compare
770    * @param res2 second result to compare
771    * @throws Exception Every difference is throwing an exception
772    */
773   public static void compareResults(Result res1, Result res2)
774       throws Exception {
775     if (res2 == null) {
776       throw new Exception("There wasn't enough rows, we stopped at "
777           + Bytes.toStringBinary(res1.getRow()));
778     }
779     if (res1.size() != res2.size()) {
780       throw new Exception("This row doesn't have the same number of KVs: "
781           + res1.toString() + " compared to " + res2.toString());
782     }
783     Cell[] ourKVs = res1.rawCells();
784     Cell[] replicatedKVs = res2.rawCells();
785     for (int i = 0; i < res1.size(); i++) {
786       if (!ourKVs[i].equals(replicatedKVs[i]) ||
787           !Bytes.equals(CellUtil.cloneValue(ourKVs[i]), CellUtil.cloneValue(replicatedKVs[i]))) {
788         throw new Exception("This result was different: "
789             + res1.toString() + " compared to " + res2.toString());
790       }
791     }
792   }
793 
794   /**
795    * Copy another Result into this one. Needed for the old Mapred framework
796    * @param other
797    */
798   public void copyFrom(Result other) {
799     this.row = null;
800     this.familyMap = null;
801     this.cells = other.cells;
802   }
803 
804   @Override
805   public CellScanner cellScanner() {
806     return CellUtil.createCellScanner(this.cells);
807   }
808 
809   public Boolean getExists() {
810     return exists;
811   }
812 
813   public void setExists(Boolean exists) {
814     this.exists = exists;
815   }
816 }