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