1 package net.obsearch.index;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import hep.aida.bin.StaticBin1D;
23
24 import java.io.ByteArrayInputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.io.DataInputStream;
27 import java.io.DataOutputStream;
28 import java.io.File;
29 import java.io.FileWriter;
30 import java.io.IOException;
31 import java.lang.reflect.Array;
32 import java.nio.ByteBuffer;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Random;
36 import java.util.logging.Logger;
37
38
39 import net.obsearch.Index;
40 import net.obsearch.OB;
41 import net.obsearch.OperationStatus;
42 import net.obsearch.Status;
43 import net.obsearch.asserts.OBAsserts;
44 import net.obsearch.cache.OBCacheHandlerLong;
45 import net.obsearch.cache.OBCacheLong;
46 import net.obsearch.constants.OBSearchProperties;
47 import net.obsearch.exception.AlreadyFrozenException;
48 import net.obsearch.exception.IllegalIdException;
49 import net.obsearch.exception.NotFrozenException;
50 import net.obsearch.exception.OBException;
51 import net.obsearch.exception.OBStorageException;
52 import net.obsearch.exception.OutOfRangeException;
53 import net.obsearch.exception.PivotsUnavailableException;
54 import net.obsearch.stats.Statistics;
55 import net.obsearch.storage.OBStore;
56 import net.obsearch.utils.bytes.ByteConversion;
57
58 import net.obsearch.storage.OBStorageConfig;
59 import net.obsearch.storage.OBStoreFactory;
60 import net.obsearch.storage.OBStoreLong;
61 import net.obsearch.storage.OBStorageConfig.IndexType;
62
63 import com.thoughtworks.xstream.XStream;
64
65
66
67
68
69
70
71
72
73 public abstract class AbstractOBIndex<O extends OB> implements Index<O> {
74
75
76
77
78
79 private boolean autoGenerateId = true;
80
81
82
83 private boolean firstInsert = true;
84
85
86
87
88 protected transient Statistics stats;
89
90
91
92
93 protected transient OBStoreLong A;
94
95
96
97
98
99 protected transient OBStoreFactory fact;
100
101
102
103
104
105
106 private transient OBStore preFreeze;
107
108
109
110
111
112
113
114
115
116 private boolean preFreezeCheck = false;
117
118 public boolean isPreFreezeCheck() {
119 return preFreezeCheck;
120 }
121
122 public AbstractOBIndex(){
123
124 }
125
126 public void setPreFreezeCheck(boolean preFreezeCheck) {
127 this.preFreezeCheck = preFreezeCheck;
128 }
129
130
131
132
133 private transient OBCacheLong<O> aCache;
134
135
136
137
138 protected boolean isFrozen;
139
140
141
142
143 protected Class<O> type;
144
145
146
147
148
149 private static final transient Logger logger = Logger
150 .getLogger(AbstractOBIndex.class.getCanonicalName());
151
152
153
154
155
156
157
158
159
160
161 protected AbstractOBIndex(Class<O> type) throws OBStorageException,
162 OBException {
163 this.type = type;
164 }
165
166
167
168
169 protected void clearACache(){
170 aCache.clear();
171 }
172
173
174
175
176
177
178 public final Class<O> getType() {
179 return type;
180 }
181
182
183
184
185
186
187
188
189 protected void assertFrozen() throws NotFrozenException {
190 if (!isFrozen()) {
191 throw new NotFrozenException();
192 }
193 }
194
195
196
197
198
199
200
201 public void init(OBStoreFactory fact) throws OBStorageException,
202 OBException, NotFrozenException, IllegalAccessException,
203 InstantiationException, OBException {
204 this.fact = fact;
205 initStorageDevices();
206 initCache();
207 stats = new Statistics();
208 }
209
210
211
212
213
214
215
216 protected void initStorageDevices() throws OBStorageException, OBException {
217 OBStorageConfig conf = new OBStorageConfig();
218 conf.setTemp(false);
219 conf.setDuplicates(false);
220 conf.setBulkMode(! isFrozen());
221 conf.setIndexType(IndexType.HASH);
222 this.A = fact.createOBStoreLong("A", conf );
223 if (!this.isFrozen()) {
224 conf = new OBStorageConfig();
225 conf.setTemp(false);
226 conf.setDuplicates(false);
227 conf.setBulkMode(false);
228 conf.setRecordSize(fixedRecordSize);
229 if(fixedRecord){
230 conf.setIndexType(IndexType.FIXED_RECORD);
231 }
232 this.preFreeze = fact.createOBStore("pre", conf);
233 }
234 }
235
236 private boolean fixedRecord = false;
237
238 public void setFixedRecord(boolean fixedRecord){
239 this.fixedRecord = fixedRecord;
240 }
241
242 private int fixedRecordSize = -1;
243
244 public void setFixedRecord(int fixedRecordSize){
245 this.fixedRecordSize = fixedRecordSize;
246 }
247
248
249
250
251
252
253
254 protected void initCache() throws OBException {
255 aCache = new OBCacheLong<O>(new ALoader(), OBSearchProperties
256 .getACacheSize());
257 }
258
259
260
261
262
263
264 private class ALoader implements OBCacheHandlerLong<O> {
265
266 public long getDBSize() throws OBStorageException {
267 return A.size();
268 }
269
270 public O loadObject(long i) throws OBException, InstantiationException,
271 IllegalAccessException, IllegalIdException {
272
273 byte[] data = A.getValue(i);
274 if (data == null) {
275 throw new IllegalIdException(i);
276 }
277
278 return bytesToObject(data);
279 }
280
281 @Override
282 public void store(long key, O object) throws OBException {
283
284 }
285
286 }
287
288
289
290
291
292
293
294
295 public void loadObject(long i, O object) throws OBException {
296 try{
297 byte[] data = A.getValue(i);
298 object.load(data);
299 }catch(IOException e){
300 throw new OBStorageException(e);
301 }
302 }
303
304
305
306
307
308
309
310
311
312
313 public double intrinsicDimensionality(int sampleSize) throws IllegalIdException, IllegalAccessException, InstantiationException, OBException{
314
315 List<O> objs = new ArrayList<O>(sampleSize);
316 Random r = new Random();
317 long max = this.databaseSize();
318
319 int i = 0;
320 while(i < sampleSize){
321 long id = Math.abs(r.nextLong() % max);
322 objs.add(getObject(id));
323 i++;
324 }
325 i = 0;
326 StaticBin1D stats = new StaticBin1D();
327 while(i < sampleSize){
328 int i2 = 0;
329 O a = objs.get(i);
330 while(i2 < sampleSize){
331 if(i2 != i){
332 O b = objs.get(i2);
333 stats.add(distance(a,b));
334 }
335 i2++;
336 }
337 logger.info("Doing: " + i);
338 i++;
339 }
340 logger.info("Distance Stats: " + stats.toString());
341 return Math.pow(stats.mean(), 2) / (2 * stats.variance());
342 }
343
344 protected double distance(O a2, O b) throws OBException{
345 throw new UnsupportedOperationException();
346 }
347
348
349
350
351
352
353
354 public boolean isPreFreeze() {
355 return preFreezeCheck;
356 }
357
358
359
360
361
362
363
364
365
366 public void setPreFreeze(boolean isPreFreeze) {
367 this.preFreezeCheck = isPreFreeze;
368 }
369
370
371
372
373 protected O bytesToObject(ByteBuffer data) throws OBException,
374 InstantiationException, IllegalAccessException, IllegalIdException {
375 return bytesToObject(data.array());
376 }
377
378
379
380
381 protected O bytesToObject(byte[] data) throws OBException,
382 InstantiationException, IllegalAccessException, IllegalIdException {
383 O res = type.newInstance();
384 try {
385 res.load(data);
386 } catch (IOException e) {
387 throw new OBException(e);
388 }
389 return res;
390 }
391
392
393
394
395
396
397 @Override
398 public void close() throws OBException {
399 A.close();
400 if (this.preFreeze != null) {
401 preFreeze.close();
402 }
403 fact.close();
404 }
405
406
407
408
409
410
411 @Override
412 public long databaseSize() throws OBStorageException {
413 return A.size();
414 }
415
416 public String debug(O object) throws OBException, InstantiationException,
417 IllegalAccessException {
418 return object.toString();
419 }
420
421 @Override
422 public OperationStatus delete(O object) throws OBException,
423 IllegalAccessException, InstantiationException, NotFrozenException {
424 if (this.isFrozen()) {
425 OperationStatus res = deleteAux(object);
426 if (res.getStatus() == Status.OK) {
427 this.A.delete(res.getId());
428 assert A.getValue(res.getId()) == null;
429 }
430 return res;
431 } else {
432 throw new NotFrozenException();
433 }
434 }
435
436
437
438
439
440
441
442
443
444
445
446 protected abstract OperationStatus deleteAux(O object) throws OBException,
447 IllegalAccessException, InstantiationException;
448
449
450
451
452
453
454
455
456 protected byte[] objectToBytes(O object) throws OBException {
457 try {
458 return object.store();
459
460 } catch (IOException e) {
461 throw new OBException(e);
462 }
463 }
464
465 protected byte[] objectToByteBuffer(O object) throws OBException {
466 return objectToBytes(object);
467 }
468
469
470
471
472
473
474 @Override
475 public O getObject(long id) throws IllegalIdException,
476 IllegalAccessException, InstantiationException, OBException {
477
478 return aCache.get(id);
479 }
480
481
482
483
484
485
486
487
488
489
490
491
492
493 protected OperationStatus findAux(O object) throws IllegalIdException,
494 IllegalAccessException, InstantiationException, OBException {
495 throw new UnsupportedOperationException();
496 }
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516 @Override
517 public Statistics getStats() throws OBStorageException {
518
519
520 return stats;
521 }
522
523 public void setIdAutoGeneration(boolean auto) throws OBException {
524 OBAsserts.chkAssert(A.size() == 0,
525 "Cannot change id generation if the index is not empty");
526 autoGenerateId = auto;
527 }
528
529
530
531
532
533
534 @Override
535 public OperationStatus insert(O object) throws OBStorageException,
536 OBException, IllegalAccessException, InstantiationException {
537 return insert(object, -1);
538
539 }
540
541 public OperationStatus insertBulk(O object) throws OBStorageException,
542 OBException, IllegalAccessException, InstantiationException {
543 return insertBulk(object, A.nextId());
544 }
545
546 public OperationStatus insertBulk(O object, long id)
547 throws OBStorageException, OBException, IllegalAccessException,
548 InstantiationException {
549
550 OperationStatus res = new OperationStatus();
551 res.setStatus(Status.OK);
552
553
554 res.setId(id);
555 if (this.isFrozen()) {
556
557
558 OBAsserts.chkAssert(A.getValue(id) == null,
559 "id already used, fatal error");
560 this.A.put(id, objectToByteBuffer(object));
561
562
563 res = insertAux(id, object);
564
565
566 } else {
567
568 OBAsserts.chkAssert(A.getValue(id) == null,
569 "id already used, fatal error");
570 this.A.put(id, objectToByteBuffer(object));
571
572 }
573 return res;
574 }
575
576
577
578 public OperationStatus insert(O object, long id) throws OBStorageException,
579 OBException, IllegalAccessException, InstantiationException {
580
581 OperationStatus res = new OperationStatus();
582 res.setStatus(Status.OK);
583
584
585
586 if (this.isFrozen()) {
587 res = exists(object);
588 if (res.getStatus() == Status.NOT_EXISTS) {
589 if (id == -1) {
590 id = A.nextId();
591 }
592
593 OBAsserts.chkAssert( A.getValue(id) == null,
594 "id already used, fatal error" + id);
595 this.A.put(id, objectToByteBuffer(object));
596
597
598 res = insertAux(id, object);
599 res.setId(id);
600 }
601
602 } else {
603
604
605
606
607 if (preFreezeCheck) {
608 byte[] key = objectToBytes(object);
609 byte[] value = this.preFreeze.getValue(key);
610 if (value == null) {
611 if (id == -1) {
612 id = A.nextId();
613 }
614 res.setId(id);
615 preFreeze.put(key, ByteConversion.longToBytes(id));
616 } else {
617 res.setStatus(Status.EXISTS);
618 res.setId(ByteConversion.bytesToLong(value));
619 }
620 } else {
621 res.setId(id);
622 }
623
624
625 if (res.getStatus() == Status.OK) {
626 if (id == -1) {
627 id = A.nextId();
628 res.setId(id);
629 }
630 OBAsserts.chkAssert(A.getValue(id) == null,
631 "id already used, fatal error: " + id);
632 this.A.put(res.getId(), objectToByteBuffer(object));
633 }
634
635 }
636 return res;
637 }
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657 protected abstract OperationStatus insertAux(long id, O object)
658 throws OBStorageException, OBException, IllegalAccessException,
659 InstantiationException;
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676 protected abstract OperationStatus insertAuxBulk(long id, O object)
677 throws OBStorageException, OBException, IllegalAccessException,
678 InstantiationException;
679
680
681
682
683
684
685 @Override
686 public void freeze() throws AlreadyFrozenException,
687 IllegalIdException, IllegalAccessException, InstantiationException,
688 OBStorageException, OutOfRangeException, OBException, PivotsUnavailableException, IOException {
689 if (isFrozen()) {
690
691 throw new AlreadyFrozenException();
692 }
693 this.isFrozen = true;
694
695 }
696
697
698
699
700
701
702 @Override
703 public long getBox(O object) throws OBException {
704 throw new UnsupportedOperationException();
705 }
706
707
708
709
710
711
712 @Override
713 public boolean isFrozen() {
714 return this.isFrozen;
715 }
716
717
718
719
720
721
722 @Override
723 public void resetStats() {
724 stats = new Statistics();
725 }
726
727
728
729
730
731
732 @Override
733 public long totalBoxes() {
734 throw new UnsupportedOperationException();
735 }
736
737
738
739
740
741
742
743
744
745
746
747
748 protected byte[][] serializePivots(final long[] ids)
749 throws IllegalIdException, IllegalAccessException,
750 InstantiationException, OBException {
751 byte[][] result = new byte[ids.length][];
752 int i = 0;
753 while (i < ids.length) {
754 O obj = getObject(ids[i]);
755 result[i] = this.objectToBytes(obj);
756
757 i++;
758 }
759 return result;
760 }
761
762
763
764
765
766
767 public O[] emptyPivotsArray(int size) {
768 return (O[]) Array.newInstance(this.getType(), size);
769 }
770
771 protected O[] loadPivots(byte[][] serializedPivots)
772 throws IllegalIdException, OBException, InstantiationException,
773 IllegalAccessException {
774 O[] result = emptyPivotsArray(serializedPivots.length);
775 int i = 0;
776 while (i < serializedPivots.length) {
777 result[i] = bytesToObject(serializedPivots[i]);
778 i++;
779 }
780 assert i == serializedPivots.length;
781 return result;
782 }
783
784 }