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