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