1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.client;
22
23 import java.io.DataInput;
24 import java.io.DataOutput;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Comparator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.NavigableMap;
32 import java.util.TreeMap;
33
34 import org.apache.hadoop.hbase.KeyValue;
35 import org.apache.hadoop.hbase.KeyValue.SplitKeyValue;
36 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
37 import org.apache.hadoop.hbase.io.WritableWithSize;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.io.Writable;
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 public class Result implements Writable, WritableWithSize {
69 private static final byte RESULT_VERSION = (byte)1;
70
71 private KeyValue [] kvs = null;
72 private NavigableMap<byte[],
73 NavigableMap<byte[], NavigableMap<Long, byte[]>>> familyMap = null;
74
75
76 private transient byte [] row = null;
77 private ImmutableBytesWritable bytes = null;
78
79
80
81
82 public Result() {}
83
84
85
86
87
88 public Result(KeyValue [] kvs) {
89 if(kvs != null && kvs.length > 0) {
90 this.kvs = kvs;
91 }
92 }
93
94
95
96
97
98 public Result(List<KeyValue> kvs) {
99 this(kvs.toArray(new KeyValue[0]));
100 }
101
102
103
104
105
106 public Result(ImmutableBytesWritable bytes) {
107 this.bytes = bytes;
108 }
109
110
111
112
113
114
115 public byte [] getRow() {
116 if (this.row == null) {
117 if(this.kvs == null) {
118 readFields();
119 }
120 this.row = this.kvs.length == 0? null: this.kvs[0].getRow();
121 }
122 return this.row;
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 public KeyValue[] raw() {
146 if(this.kvs == null) {
147 readFields();
148 }
149 return kvs;
150 }
151
152
153
154
155
156
157
158
159 public List<KeyValue> list() {
160 if(this.kvs == null) {
161 readFields();
162 }
163 return isEmpty()? null: Arrays.asList(raw());
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 public List<KeyValue> getColumn(byte [] family, byte [] qualifier) {
182 List<KeyValue> result = new ArrayList<KeyValue>();
183
184 KeyValue [] kvs = raw();
185
186 if (kvs == null || kvs.length == 0) {
187 return result;
188 }
189 int pos = binarySearch(kvs, family, qualifier);
190 if (pos == -1) {
191 return result;
192 }
193
194 for (int i = pos ; i < kvs.length ; i++ ) {
195 KeyValue kv = kvs[i];
196 if (kv.matchingColumn(family,qualifier)) {
197 result.add(kv);
198 } else {
199 break;
200 }
201 }
202
203 return result;
204 }
205
206 protected int binarySearch(final KeyValue [] kvs,
207 final byte [] family,
208 final byte [] qualifier) {
209 KeyValue searchTerm =
210 KeyValue.createFirstOnRow(kvs[0].getRow(),
211 family, qualifier);
212
213
214 int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
215
216 if (pos < 0) {
217 pos = (pos+1) * -1;
218
219 }
220 if (pos == kvs.length) {
221 return -1;
222 }
223 return pos;
224 }
225
226
227
228
229
230
231
232
233
234
235 public KeyValue getColumnLatest(byte [] family, byte [] qualifier) {
236 KeyValue [] kvs = raw();
237 if (kvs == null || kvs.length == 0) {
238 return null;
239 }
240 int pos = binarySearch(kvs, family, qualifier);
241 if (pos == -1) {
242 return null;
243 }
244 KeyValue kv = kvs[pos];
245 if (kv.matchingColumn(family, qualifier)) {
246 return kv;
247 }
248 return null;
249 }
250
251
252
253
254
255
256
257 public byte[] getValue(byte [] family, byte [] qualifier) {
258 KeyValue kv = getColumnLatest(family, qualifier);
259 if (kv == null) {
260 return null;
261 }
262 return kv.getValue();
263 }
264
265
266
267
268
269
270
271 public boolean containsColumn(byte [] family, byte [] qualifier) {
272 KeyValue kv = getColumnLatest(family, qualifier);
273 return kv != null;
274 }
275
276
277
278
279
280
281
282
283
284
285 public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> getMap() {
286 if(this.familyMap != null) {
287 return this.familyMap;
288 }
289 if(isEmpty()) {
290 return null;
291 }
292 this.familyMap =
293 new TreeMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
294 (Bytes.BYTES_COMPARATOR);
295 for(KeyValue kv : this.kvs) {
296 SplitKeyValue splitKV = kv.split();
297 byte [] family = splitKV.getFamily();
298 NavigableMap<byte[], NavigableMap<Long, byte[]>> columnMap =
299 familyMap.get(family);
300 if(columnMap == null) {
301 columnMap = new TreeMap<byte[], NavigableMap<Long, byte[]>>
302 (Bytes.BYTES_COMPARATOR);
303 familyMap.put(family, columnMap);
304 }
305 byte [] qualifier = splitKV.getQualifier();
306 NavigableMap<Long, byte[]> versionMap = columnMap.get(qualifier);
307 if(versionMap == null) {
308 versionMap = new TreeMap<Long, byte[]>(new Comparator<Long>() {
309 public int compare(Long l1, Long l2) {
310 return l2.compareTo(l1);
311 }
312 });
313 columnMap.put(qualifier, versionMap);
314 }
315 Long timestamp = Bytes.toLong(splitKV.getTimestamp());
316 byte [] value = splitKV.getValue();
317 versionMap.put(timestamp, value);
318 }
319 return this.familyMap;
320 }
321
322
323
324
325
326
327
328
329
330 public NavigableMap<byte[], NavigableMap<byte[], byte[]>> getNoVersionMap() {
331 if(this.familyMap == null) {
332 getMap();
333 }
334 if(isEmpty()) {
335 return null;
336 }
337 NavigableMap<byte[], NavigableMap<byte[], byte[]>> returnMap =
338 new TreeMap<byte[], NavigableMap<byte[], byte[]>>(Bytes.BYTES_COMPARATOR);
339 for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
340 familyEntry : familyMap.entrySet()) {
341 NavigableMap<byte[], byte[]> qualifierMap =
342 new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
343 for(Map.Entry<byte[], NavigableMap<Long, byte[]>> qualifierEntry :
344 familyEntry.getValue().entrySet()) {
345 byte [] value =
346 qualifierEntry.getValue().get(qualifierEntry.getValue().firstKey());
347 qualifierMap.put(qualifierEntry.getKey(), value);
348 }
349 returnMap.put(familyEntry.getKey(), qualifierMap);
350 }
351 return returnMap;
352 }
353
354
355
356
357
358
359
360
361 public NavigableMap<byte[], byte[]> getFamilyMap(byte [] family) {
362 if(this.familyMap == null) {
363 getMap();
364 }
365 if(isEmpty()) {
366 return null;
367 }
368 NavigableMap<byte[], byte[]> returnMap =
369 new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
370 NavigableMap<byte[], NavigableMap<Long, byte[]>> qualifierMap =
371 familyMap.get(family);
372 if(qualifierMap == null) {
373 return returnMap;
374 }
375 for(Map.Entry<byte[], NavigableMap<Long, byte[]>> entry :
376 qualifierMap.entrySet()) {
377 byte [] value =
378 entry.getValue().get(entry.getValue().firstKey());
379 returnMap.put(entry.getKey(), value);
380 }
381 return returnMap;
382 }
383
384
385
386
387
388 public byte [] value() {
389 if (isEmpty()) {
390 return null;
391 }
392 return kvs[0].getValue();
393 }
394
395
396
397
398
399
400
401
402
403 public ImmutableBytesWritable getBytes() {
404 return this.bytes;
405 }
406
407
408
409
410
411 public boolean isEmpty() {
412 if(this.kvs == null) {
413 readFields();
414 }
415 return this.kvs == null || this.kvs.length == 0;
416 }
417
418
419
420
421 public int size() {
422 if(this.kvs == null) {
423 readFields();
424 }
425 return this.kvs == null? 0: this.kvs.length;
426 }
427
428
429
430
431 @Override
432 public String toString() {
433 StringBuilder sb = new StringBuilder();
434 sb.append("keyvalues=");
435 if(isEmpty()) {
436 sb.append("NONE");
437 return sb.toString();
438 }
439 sb.append("{");
440 boolean moreThanOne = false;
441 for(KeyValue kv : this.kvs) {
442 if(moreThanOne) {
443 sb.append(", ");
444 } else {
445 moreThanOne = true;
446 }
447 sb.append(kv.toString());
448 }
449 sb.append("}");
450 return sb.toString();
451 }
452
453
454 public void readFields(final DataInput in)
455 throws IOException {
456 familyMap = null;
457 row = null;
458 kvs = null;
459 int totalBuffer = in.readInt();
460 if(totalBuffer == 0) {
461 bytes = null;
462 return;
463 }
464 byte [] raw = new byte[totalBuffer];
465 in.readFully(raw, 0, totalBuffer);
466 bytes = new ImmutableBytesWritable(raw, 0, totalBuffer);
467 }
468
469
470 private void readFields() {
471 if (bytes == null) {
472 this.kvs = new KeyValue[0];
473 return;
474 }
475 byte [] buf = bytes.get();
476 int offset = bytes.getOffset();
477 int finalOffset = bytes.getSize() + offset;
478 List<KeyValue> kvs = new ArrayList<KeyValue>();
479 while(offset < finalOffset) {
480 int keyLength = Bytes.toInt(buf, offset);
481 offset += Bytes.SIZEOF_INT;
482 kvs.add(new KeyValue(buf, offset, keyLength));
483 offset += keyLength;
484 }
485 this.kvs = kvs.toArray(new KeyValue[kvs.size()]);
486 }
487
488 public long getWritableSize() {
489 if (isEmpty())
490 return Bytes.SIZEOF_INT;
491
492 long size = Bytes.SIZEOF_INT;
493
494 for (KeyValue kv : kvs) {
495 size += kv.getLength();
496 size += Bytes.SIZEOF_INT;
497 }
498
499 return size;
500 }
501
502 public void write(final DataOutput out)
503 throws IOException {
504 if(isEmpty()) {
505 out.writeInt(0);
506 } else {
507 int totalLen = 0;
508 for(KeyValue kv : kvs) {
509 totalLen += kv.getLength() + Bytes.SIZEOF_INT;
510 }
511 out.writeInt(totalLen);
512 for(KeyValue kv : kvs) {
513 out.writeInt(kv.getLength());
514 out.write(kv.getBuffer(), kv.getOffset(), kv.getLength());
515 }
516 }
517 }
518
519 public static long getWriteArraySize(Result [] results) {
520 long size = Bytes.SIZEOF_BYTE;
521 if (results == null || results.length == 0) {
522 size += Bytes.SIZEOF_INT;
523 return size;
524 }
525
526 size += Bytes.SIZEOF_INT;
527 size += Bytes.SIZEOF_INT;
528 for (Result result : results) {
529 size += Bytes.SIZEOF_INT;
530 if (result == null || result.isEmpty())
531 continue;
532
533 for (KeyValue kv : result.raw()) {
534 size += Bytes.SIZEOF_INT;
535 size += kv.getLength();
536 }
537 }
538
539 return size;
540 }
541
542 public static void writeArray(final DataOutput out, Result [] results)
543 throws IOException {
544
545
546
547
548 out.writeByte(RESULT_VERSION);
549 if(results == null || results.length == 0) {
550 out.writeInt(0);
551 return;
552 }
553 out.writeInt(results.length);
554 int bufLen = 0;
555 for(Result result : results) {
556 bufLen += Bytes.SIZEOF_INT;
557 if(result == null || result.isEmpty()) {
558 continue;
559 }
560 for(KeyValue key : result.raw()) {
561 bufLen += key.getLength() + Bytes.SIZEOF_INT;
562 }
563 }
564 out.writeInt(bufLen);
565 for(Result result : results) {
566 if(result == null || result.isEmpty()) {
567 out.writeInt(0);
568 continue;
569 }
570 out.writeInt(result.size());
571 for(KeyValue kv : result.raw()) {
572 out.writeInt(kv.getLength());
573 out.write(kv.getBuffer(), kv.getOffset(), kv.getLength());
574 }
575 }
576 }
577
578 public static Result [] readArray(final DataInput in)
579 throws IOException {
580
581
582
583
584 int version = in.readByte();
585 if (version > RESULT_VERSION) {
586 throw new IOException("version not supported");
587 }
588 int numResults = in.readInt();
589 if(numResults == 0) {
590 return new Result[0];
591 }
592 Result [] results = new Result[numResults];
593 int bufSize = in.readInt();
594 byte [] buf = new byte[bufSize];
595 int offset = 0;
596 for(int i=0;i<numResults;i++) {
597 int numKeys = in.readInt();
598 offset += Bytes.SIZEOF_INT;
599 if(numKeys == 0) {
600 results[i] = new Result((ImmutableBytesWritable)null);
601 continue;
602 }
603 int initialOffset = offset;
604 for(int j=0;j<numKeys;j++) {
605 int keyLen = in.readInt();
606 Bytes.putInt(buf, offset, keyLen);
607 offset += Bytes.SIZEOF_INT;
608 in.readFully(buf, offset, keyLen);
609 offset += keyLen;
610 }
611 int totalLength = offset - initialOffset;
612 results[i] = new Result(new ImmutableBytesWritable(buf, initialOffset,
613 totalLength));
614 }
615 return results;
616 }
617
618
619
620
621
622
623
624 public static void compareResults(Result res1, Result res2)
625 throws Exception {
626 if (res2 == null) {
627 throw new Exception("There wasn't enough rows, we stopped at "
628 + Bytes.toStringBinary(res1.getRow()));
629 }
630 if (res1.size() != res2.size()) {
631 throw new Exception("This row doesn't have the same number of KVs: "
632 + res1.toString() + " compared to " + res2.toString());
633 }
634 KeyValue[] ourKVs = res1.raw();
635 KeyValue[] replicatedKVs = res2.raw();
636 for (int i = 0; i < res1.size(); i++) {
637 if (!ourKVs[i].equals(replicatedKVs[i]) ||
638 !Bytes.equals(ourKVs[i].getValue(), replicatedKVs[i].getValue())) {
639 throw new Exception("This result was different: "
640 + res1.toString() + " compared to " + res2.toString());
641 }
642 }
643 }
644 }