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