diff --git a/.gitignore b/.gitignore index 6ee8130c..f14a070a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ build/* */.gradle/* */build/* */performance_test/* +.classpath +.project +.settings +bin/ diff --git a/src/main/java/io/tiledb/java/api/Array.java b/src/main/java/io/tiledb/java/api/Array.java index 40e5fda5..083e09b9 100644 --- a/src/main/java/io/tiledb/java/api/Array.java +++ b/src/main/java/io/tiledb/java/api/Array.java @@ -615,6 +615,179 @@ public HashMap> maxBufferElements(NativeArray subarray) return ret; } + /** + * Get a metadata key-value item from an open array. The array must be opened in READ mode, + * otherwise the function will error out. + * + * @param key a key to retrieve from the metadata key-value + * @return NativeArray which contains the metadata + * @throws TileDBError A TileDB exception + */ + public NativeArray getMetadata(String key) throws TileDBError { + return getMetadata(key, null); + } + + /** + * Get a metadata key-value item from an open array. The array must be opened in READ mode, + * otherwise the function will error out. + * + * @param key a key to retrieve from the metadata key-value + * @param nativeType The Datatype + * @return NativeArray which contains the metadata + * @throws TileDBError A TileDB exception + */ + public NativeArray getMetadata(String key, Datatype nativeType) throws TileDBError { + checkIsOpen(); + + SWIGTYPE_p_p_void resultArrpp = tiledb.new_voidpArray(0); + SWIGTYPE_p_unsigned_int value_num = tiledb.new_uintp(); + SWIGTYPE_p_tiledb_datatype_t value_type = + (nativeType == null) + ? tiledb.new_tiledb_datatype_tp() + : tiledb.copy_tiledb_datatype_tp(nativeType.toSwigEnum()); + + ctx.handleError( + tiledb.tiledb_array_get_metadata( + ctx.getCtxp(), arrayp, key, value_type, value_num, resultArrpp)); + + Datatype derivedNativeType = Datatype.fromSwigEnum(tiledb.tiledb_datatype_tp_value(value_type)); + + long value = tiledb.uintp_value(value_num); + NativeArray result = new NativeArray(ctx, derivedNativeType, resultArrpp, (int) value); + + tiledb.delete_uintp(value_num); + tiledb.delete_tiledb_datatype_tp(value_type); + + return result; + } + + /** + * Deletes a metadata key-value item from an open array. The array must be opened in WRITE mode, + * otherwise the function will error out. + * + * @param key a key to delete from the metadata key-value + * @throws TileDBError A TileDB exception + */ + public void deleteMetadata(String key) throws TileDBError { + checkIsOpen(); + + ctx.handleError(tiledb.tiledb_array_delete_metadata(ctx.getCtxp(), arrayp, key)); + } + + /** + * Gets the number of metadata items in an open array. The array must be opened in READ mode, + * otherwise the function will error out. + * + * @return the number of metadata items + * @throws TileDBError A TileDB exception + */ + public BigInteger getMetadataNum() throws TileDBError { + checkIsOpen(); + + SWIGTYPE_p_unsigned_long_long value_num = tiledb.new_ullp(); + + ctx.handleError(tiledb.tiledb_array_get_metadata_num(ctx.getCtxp(), arrayp, value_num)); + + BigInteger value = tiledb.ullp_value(value_num); + + tiledb.delete_ullp(value_num); + + return value; + } + + /** + * Gets a metadata item from an open array using an index. The array must be opened in READ mode, + * otherwise the function will error out. + * + * @param index index to retrieve metadata from + * @return a pair, key and the metadata + * @throws TileDBError A TileDB exception + */ + public Pair getMetadataFromIndex(long index) throws TileDBError { + return getMetadataFromIndex(BigInteger.valueOf(index)); + } + + /** + * Gets a metadata item from an open array using an index. The array must be opened in READ mode, + * otherwise the function will error out. + * + * @param index index to retrieve metadata from + * @return a pair, key and the metadata + * @throws TileDBError A TileDB exception + */ + public Pair getMetadataFromIndex(BigInteger index) throws TileDBError { + checkIsOpen(); + + SWIGTYPE_p_p_char key = tiledb.new_charpp(); + SWIGTYPE_p_unsigned_int key_len = tiledb.new_uintp(); + SWIGTYPE_p_tiledb_datatype_t value_type = tiledb.new_tiledb_datatype_tp(); + SWIGTYPE_p_unsigned_int value_num = tiledb.new_uintp(); + SWIGTYPE_p_p_void value = tiledb.new_voidpArray(0); + + ctx.handleError( + tiledb.tiledb_array_get_metadata_from_index( + ctx.getCtxp(), arrayp, index, key, key_len, value_type, value_num, value)); + + String keyString = tiledb.charpp_value(key); + long valueLength = tiledb.uintp_value(value_num); + Datatype nativeType = Datatype.fromSwigEnum(tiledb.tiledb_datatype_tp_value(value_type)); + + NativeArray result = new NativeArray(ctx, nativeType, value, (int) valueLength); + + tiledb.delete_uintp(value_num); + tiledb.delete_uintp(key_len); + tiledb.delete_charpp(key); + tiledb.delete_tiledb_datatype_tp(value_type); + + return new Pair(keyString, result); + } + + /** + * Checks if the key is present in the Array metadata. The array must be opened in READ mode, + * otherwise the function will error out. + * + * @param key a key to retrieve from the metadata key-value + * @return true if the key is present in the metadata, false if it is not + * @throws TileDBError A TileDB exception + */ + public Boolean hasMetadataKey(String key) throws TileDBError { + checkIsOpen(); + + SWIGTYPE_p_tiledb_datatype_t value_type = tiledb.new_tiledb_datatype_tp(); + SWIGTYPE_p_int has_key = tiledb.new_intp(); + + ctx.handleError( + tiledb.tiledb_array_has_metadata_key(ctx.getCtxp(), arrayp, key, value_type, has_key)); + + Boolean result = tiledb.intp_value(has_key) > 0; + + tiledb.delete_intp(has_key); + tiledb.delete_tiledb_datatype_tp(value_type); + + return result; + } + + /** + * Puts a metadata key-value item to an open array. The array must be opened in WRITE mode, + * otherwise the function will error out. + * + * @param key a key to assign to the input value + * @param value the metadata to put into the Array metadata + * @throws TileDBError A TileDB exception + */ + public void putMetadata(String key, NativeArray value) throws TileDBError { + checkIsOpen(); + + ctx.handleError( + tiledb.tiledb_array_put_metadata( + ctx.getCtxp(), + arrayp, + key, + value.getNativeType().toSwigEnum(), + value.getSize(), + value.toVoidPointer())); + } + /** @return The TileDB Context object associated with the Array instance. */ public Context getCtx() { return ctx; diff --git a/src/test/java/io/tiledb/java/api/ArrayTest.java b/src/test/java/io/tiledb/java/api/ArrayTest.java index ec5119d6..e2cc6cb0 100644 --- a/src/test/java/io/tiledb/java/api/ArrayTest.java +++ b/src/test/java/io/tiledb/java/api/ArrayTest.java @@ -1,5 +1,6 @@ package io.tiledb.java.api; +import static io.tiledb.java.api.Datatype.*; import static io.tiledb.java.api.Layout.TILEDB_ROW_MAJOR; import static io.tiledb.java.api.QueryType.TILEDB_READ; import static io.tiledb.java.api.QueryType.TILEDB_WRITE; @@ -34,6 +35,16 @@ public void tearDown() throws Exception { ctx.close(); } + private Object[] getArray(Object val) { + if (val instanceof Object[]) return (Object[]) val; + int arrlength = java.lang.reflect.Array.getLength(val); + Object[] outputArray = new Object[arrlength]; + for (int i = 0; i < arrlength; i++) { + outputArray[i] = java.lang.reflect.Array.get(val, i); + } + return outputArray; + } + public ArraySchema schemaCreate() throws Exception { Dimension d1 = new Dimension(ctx, "d1", Long.class, new Pair(1l, 4l), 2l); @@ -247,4 +258,161 @@ public void testArraygetNonEmptyDomainFromName() throws Exception { } catch (TileDBError error) { } } + + @Test + public void testArrayMetadata() throws Exception { + Array.create(arrayURI, schemaCreate()); + + long[] array_a = new long[] {1, 2, 3, 6}; + insertArbitraryValues(new NativeArray(ctx, array_a, Long.class)); + + Array arrayw = new Array(ctx, arrayURI, TILEDB_WRITE); + Array array = new Array(ctx, arrayURI, TILEDB_READ); + + NativeArray metadataByte = new NativeArray(ctx, new byte[] {-7, -6, -5, 0, 100}, Byte.class); + + NativeArray metadataShort = new NativeArray(ctx, new short[] {18, 19, 20, 21}, Short.class); + + NativeArray metadataInt = + new NativeArray( + ctx, + new int[] { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 + }, + Integer.class); + + NativeArray metadataFloat = + new NativeArray( + ctx, + new float[] { + 0.1f, 0.2f, 1.1f, 1.2f, 2.1f, 2.2f, 3.1f, 3.2f, + 4.1f, 4.2f, 5.1f, 5.2f, 6.1f, 6.2f, 7.1f, 7.2f, + 8.1f, 8.2f, 9.1f, 9.2f, 10.1f, 10.2f, 11.1f, 11.2f, + 12.1f, 12.2f, 13.1f, 13.2f, 14.1f, 14.2f, 15.1f, 15.2f + }, + Float.class); + + NativeArray metadataDouble = + new NativeArray( + ctx, + new double[] { + 1.1d, 1.2d, 2.1d, 2.2d, 3.1d, 3.2d, 4.1d, 4.2d, + 5.1d, 5.2d, 6.1d, 6.2d, 7.1d, 7.2d, 8.1d, 8.2d, + 9.1d, 9.2d, 10.1d, 10.2d, 11.1d, 11.2d, 12.1d, 12.2d, + 13.1d, 14.2d, 14.1d, 14.2d, 15.1d, 15.2d, 16.1d, 16.2d + }, + Double.class); + + String byteKey = "md-byte"; + String shortKey = "md-short"; + String intKey = "md-int"; + String floatKey = "md-float"; + String doubleKey = "md-double"; + + // metadata keys sorted in a lexicographic ordering + String[] keys = new String[] {byteKey, doubleKey, floatKey, intKey, shortKey}; + Datatype[] types = + new Datatype[] {TILEDB_INT8, TILEDB_FLOAT64, TILEDB_FLOAT32, TILEDB_INT32, TILEDB_INT16}; + int keysNum = keys.length; + NativeArray[] nativeArrays = + new NativeArray[] {metadataByte, metadataDouble, metadataFloat, metadataInt, metadataShort}; + Object[] expectedArrays = + new Object[] { + metadataByte.toJavaArray(), + metadataDouble.toJavaArray(), + metadataFloat.toJavaArray(), + metadataInt.toJavaArray(), + metadataShort.toJavaArray() + }; + + for (int i = 0; i < keysNum; i++) { + Assert.assertFalse(array.hasMetadataKey(keys[i])); + } + + Assert.assertEquals(0, array.getMetadataNum().intValue()); + array.close(); + + for (int i = 0; i < keysNum; i++) { + arrayw.putMetadata(keys[i], nativeArrays[i]); + } + // submit changes + arrayw.close(); + + // open a new session + Array arrayn = new Array(ctx, arrayURI, TILEDB_READ); + + for (int i = 0; i < keysNum; i++) { + Assert.assertTrue(arrayn.hasMetadataKey(keys[i])); + } + + Assert.assertEquals(keysNum, arrayn.getMetadataNum().intValue()); + + // manual extraction of metadata + NativeArray metadataByteActual = arrayn.getMetadata(byteKey, TILEDB_INT8); + NativeArray metadataShortActual = arrayn.getMetadata(shortKey, TILEDB_INT16); + NativeArray metadataIntActual = arrayn.getMetadata(intKey, TILEDB_INT32); + NativeArray metadataFloatActual = arrayn.getMetadata(floatKey, TILEDB_FLOAT32); + NativeArray metadataDoubleActual = arrayn.getMetadata(doubleKey, TILEDB_FLOAT64); + + Assert.assertNotNull(metadataByteActual); + Assert.assertNotNull(metadataShortActual); + Assert.assertNotNull(metadataIntActual); + Assert.assertNotNull(metadataFloatActual); + Assert.assertNotNull(metadataDoubleActual); + + Assert.assertArrayEquals( + (byte[]) metadataByte.toJavaArray(), (byte[]) metadataByteActual.toJavaArray()); + Assert.assertArrayEquals( + (short[]) metadataShort.toJavaArray(), (short[]) metadataShortActual.toJavaArray()); + Assert.assertArrayEquals( + (int[]) metadataInt.toJavaArray(), (int[]) metadataIntActual.toJavaArray()); + Assert.assertArrayEquals( + (float[]) metadataFloat.toJavaArray(), (float[]) metadataFloatActual.toJavaArray(), 1e-10f); + Assert.assertArrayEquals( + (double[]) metadataDouble.toJavaArray(), + (double[]) metadataDoubleActual.toJavaArray(), + 1e-10d); + + // exctracion of metadata without specifying the Datatype + for (int i = 0; i < keysNum; i++) { + NativeArray a = arrayn.getMetadata(keys[i]); + Assert.assertNotNull(a); + Assert.assertEquals(types[i], a.getNativeType()); + Assert.assertEquals(nativeArrays[i].getNativeType(), a.getNativeType()); + Assert.assertArrayEquals(getArray(expectedArrays[i]), getArray(a.toJavaArray())); + } + + // fromIndex tests + for (int i = 0; i < arrayn.getMetadataNum().intValue(); i++) { + Pair p = arrayn.getMetadataFromIndex(i); + NativeArray a = p.getSecond(); + Assert.assertEquals(keys[i], p.getFirst()); + Assert.assertEquals(types[i], a.getNativeType()); + Assert.assertEquals(nativeArrays[i].getNativeType(), a.getNativeType()); + Assert.assertArrayEquals(getArray(expectedArrays[i]), getArray(a.toJavaArray())); + } + + arrayn.close(); + + // open a new write session + Array arrayd = new Array(ctx, arrayURI, TILEDB_WRITE); + + for (int i = 0; i < keysNum; i++) { + arrayd.deleteMetadata(keys[i]); + } + + arrayd.close(); + + // open a new session to check the deletion + Array arraydn = new Array(ctx, arrayURI, TILEDB_READ); + + for (int i = 0; i < keysNum; i++) { + Assert.assertFalse(arraydn.hasMetadataKey(keys[i])); + } + + Assert.assertEquals(0, arraydn.getMetadataNum().intValue()); + + arraydn.close(); + } }