Skip to content

Commit

Permalink
Merge pull request #1768 from kuzudb/java-struct
Browse files Browse the repository at this point in the history
Add struct support for Java API
  • Loading branch information
mewim committed Jul 6, 2023
2 parents 10f5c58 + d2a01d2 commit 0b5b617
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 39 deletions.
30 changes: 28 additions & 2 deletions tools/java_api/src/jni/kuzu_java.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ void javaMapToCPPMap(
jobject entry = env->CallObjectMethod(iter, next);
jstring key = (jstring)env->CallObjectMethod(entry, entryGetKey);
jobject value = env->CallObjectMethod(entry, entryGetValue);
const char* keyStr = env->GetStringUTFChars(key, NULL);
const char* keyStr = env->GetStringUTFChars(key, JNI_FALSE);
Value* v = getValue(env, value);
std::shared_ptr<Value> value_ptr(v);
cppMap.insert({keyStr, value_ptr});
Expand Down Expand Up @@ -753,7 +753,7 @@ JNIEXPORT jlong JNICALL Java_com_kuzudb_KuzuNative_kuzu_1value_1create_1value(
v = new Value(static_cast<double>(value));
} else if (env->IsInstanceOf(val, env->FindClass("java/lang/String"))) {
jstring value = static_cast<jstring>(val);
const char* str = env->GetStringUTFChars(value, NULL);
const char* str = env->GetStringUTFChars(value, JNI_FALSE);
v = new Value(str);
env->ReleaseStringUTFChars(value, str);
} else if (env->IsInstanceOf(val, env->FindClass("com/kuzudb/KuzuInternalID"))) {
Expand Down Expand Up @@ -1060,3 +1060,29 @@ JNIEXPORT jstring JNICALL Java_com_kuzudb_KuzuNative_kuzu_1rel_1val_1to_1string(
jstring ret = env->NewStringUTF(result_string.c_str());
return ret;
}

JNIEXPORT jstring JNICALL Java_com_kuzudb_KuzuNative_kuzu_1value_1get_1struct_1field_1name(
JNIEnv* env, jclass, jobject thisSV, jlong index) {
auto* sv = getValue(env, thisSV);
auto dataType = sv->getDataType();
auto fieldNames = StructType::getFieldNames(&dataType);
if (index >= fieldNames.size() || index < 0) {
return nullptr;
}
auto name = fieldNames[index];
return env->NewStringUTF(name.c_str());
}

JNIEXPORT jlong JNICALL Java_com_kuzudb_KuzuNative_kuzu_1value_1get_1struct_1index(
JNIEnv* env, jclass, jobject thisSV, jstring field_name) {
auto* sv = getValue(env, thisSV);
const char* field_name_cstr = env->GetStringUTFChars(field_name, JNI_FALSE);
auto dataType = sv->getDataType();
auto index = StructType::getFieldIdx(&dataType, field_name_cstr);
env->ReleaseStringUTFChars(field_name, field_name_cstr);
if (index == INVALID_STRUCT_FIELD_IDX) {
return -1;
} else {
return static_cast<jlong>(index);
}
}
3 changes: 3 additions & 0 deletions tools/java_api/src/main/java/com/kuzudb/KuzuNative.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,7 @@ protected static native long kuzu_data_type_create(

protected static native String kuzu_rel_val_to_string(KuzuValue rel_val);

protected static native String kuzu_value_get_struct_field_name(KuzuValue struct_val, long index);

protected static native long kuzu_value_get_struct_index(KuzuValue struct_val, String field_name);
}
10 changes: 0 additions & 10 deletions tools/java_api/src/main/java/com/kuzudb/KuzuValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,6 @@ public <T> T getValue() throws KuzuObjectRefDestroyedException {
return KuzuNative.kuzu_value_get_value(this);
}

public long getListSize() throws KuzuObjectRefDestroyedException {
checkNotDestroyed();
return KuzuNative.kuzu_value_get_list_size(this);
}

public KuzuValue getListElement(long index) throws KuzuObjectRefDestroyedException {
checkNotDestroyed();
return KuzuNative.kuzu_value_get_list_element(this, index);
}

public KuzuDataType getDataType() throws KuzuObjectRefDestroyedException {
checkNotDestroyed();
return KuzuNative.kuzu_value_get_data_type(this);
Expand Down
13 changes: 13 additions & 0 deletions tools/java_api/src/main/java/com/kuzudb/KuzuValueListUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.kuzudb;

public class KuzuValueListUtil {
public static long getListSize(KuzuValue value) throws KuzuObjectRefDestroyedException {
value.checkNotDestroyed();
return KuzuNative.kuzu_value_get_list_size(value);
}

public static KuzuValue getListElement(KuzuValue value, long index) throws KuzuObjectRefDestroyedException {
value.checkNotDestroyed();
return KuzuNative.kuzu_value_get_list_element(value, index);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.kuzudb;

public class KuzuNodeValue {
public class KuzuValueNodeUtil {
public static KuzuInternalID getID(KuzuValue value) throws KuzuObjectRefDestroyedException {
value.checkNotDestroyed();
return KuzuNative.kuzu_node_val_get_id(value);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.kuzudb;

public class KuzuRelValue {
public class KuzuValueRelUtil {
public static KuzuInternalID getSrcID(KuzuValue value) throws KuzuObjectRefDestroyedException {
value.checkNotDestroyed();
return KuzuNative.kuzu_rel_val_get_src_id(value);
Expand Down
35 changes: 35 additions & 0 deletions tools/java_api/src/main/java/com/kuzudb/KuzuValueStructUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.kuzudb;

public class KuzuValueStructUtil {
public static long getNumFields(KuzuValue value) throws KuzuObjectRefDestroyedException {
value.checkNotDestroyed();
return KuzuNative.kuzu_value_get_list_size(value);
}

public static long getIndexByFieldName(KuzuValue value, String fieldName) throws KuzuObjectRefDestroyedException {
value.checkNotDestroyed();
return KuzuNative.kuzu_value_get_struct_index(value, fieldName);
}

public static String getFieldNameByIndex(KuzuValue value, long index) throws KuzuObjectRefDestroyedException {
value.checkNotDestroyed();
return KuzuNative.kuzu_value_get_struct_field_name(value, index);
}

public static KuzuValue getValueByFieldName(KuzuValue value, String fieldName) throws KuzuObjectRefDestroyedException {
value.checkNotDestroyed();
long index = getIndexByFieldName(value, fieldName);
if (index < 0) {
return null;
}
return getValueByIndex(value, index);
}

public static KuzuValue getValueByIndex(KuzuValue value, long index) throws KuzuObjectRefDestroyedException {
value.checkNotDestroyed();
if (index < 0 || index >= getNumFields(value)) {
return null;
}
return KuzuNative.kuzu_value_get_list_element(value, index);
}
}
140 changes: 115 additions & 25 deletions tools/java_api/src/test/java/com/kuzudb/test/ValueTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ void ValueGetListSize() throws KuzuObjectRefDestroyedException {

assertTrue(value.isOwnedByCPP());
assertFalse(value.isNull());
assertEquals(value.getListSize(), 2);
assertEquals(KuzuValueListUtil.getListSize(value), 2);

value.destroy();
flatTuple.destroy();
Expand All @@ -279,19 +279,19 @@ void ValueGetListElement() throws KuzuObjectRefDestroyedException {
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());
assertFalse(value.isNull());
assertEquals(value.getListSize(), 2);
assertEquals(KuzuValueListUtil.getListSize(value), 2);

KuzuValue listElement = value.getListElement(0);
KuzuValue listElement = KuzuValueListUtil.getListElement(value, 0);
assertTrue(listElement.isOwnedByCPP());
assertTrue(listElement.getValue().equals(10L));
listElement.destroy();

listElement = value.getListElement(1);
listElement = KuzuValueListUtil.getListElement(value, 1);
assertTrue(listElement.isOwnedByCPP());
assertTrue(listElement.getValue().equals(5L));
listElement.destroy();

listElement = value.getListElement(222);
listElement = KuzuValueListUtil.getListElement(value, 222);
assertNull(listElement);

value.destroy();
Expand Down Expand Up @@ -547,7 +547,7 @@ void NodeValGetID() throws KuzuObjectRefDestroyedException {
assertTrue(value.isOwnedByCPP());
assertFalse(value.isNull());

KuzuInternalID id = KuzuNodeValue.getID(value);
KuzuInternalID id = KuzuValueNodeUtil.getID(value);
assertEquals(id.table_id, 0);
assertEquals(id.offset, 0);
value.destroy();
Expand All @@ -566,7 +566,7 @@ void NodeValGetLabelName() throws KuzuObjectRefDestroyedException {
assertTrue(value.isOwnedByCPP());
assertFalse(value.isNull());

String label = KuzuNodeValue.getLabelName(value);
String label = KuzuValueNodeUtil.getLabelName(value);
assertEquals(label, "person");
value.destroy();
flatTuple.destroy();
Expand All @@ -581,28 +581,28 @@ void NodeValGetProperty() throws KuzuObjectRefDestroyedException {
KuzuFlatTuple flatTuple = result.getNext();
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());
String propertyName = KuzuNodeValue.getPropertyNameAt(value, 0);
String propertyName = KuzuValueNodeUtil.getPropertyNameAt(value, 0);
assertTrue(propertyName.equals("ID"));
propertyName = KuzuNodeValue.getPropertyNameAt(value, 1);
propertyName = KuzuValueNodeUtil.getPropertyNameAt(value, 1);
assertTrue(propertyName.equals("fName"));
propertyName = KuzuNodeValue.getPropertyNameAt(value, 2);
propertyName = KuzuValueNodeUtil.getPropertyNameAt(value, 2);
assertTrue(propertyName.equals("gender"));
propertyName = KuzuNodeValue.getPropertyNameAt(value, 3);
propertyName = KuzuValueNodeUtil.getPropertyNameAt(value, 3);
assertTrue(propertyName.equals("isStudent"));

KuzuValue propertyValue = KuzuNodeValue.getPropertyValueAt(value, 0);
KuzuValue propertyValue = KuzuValueNodeUtil.getPropertyValueAt(value, 0);
long propertyValueID = propertyValue.getValue();
assertEquals(propertyValueID, 0);
propertyValue.destroy();
propertyValue = KuzuNodeValue.getPropertyValueAt(value, 1);
propertyValue = KuzuValueNodeUtil.getPropertyValueAt(value, 1);
String propertyValuefName = propertyValue.getValue();
assertTrue(propertyValuefName.equals("Alice"));
propertyValue.destroy();
propertyValue = KuzuNodeValue.getPropertyValueAt(value, 2);
propertyValue = KuzuValueNodeUtil.getPropertyValueAt(value, 2);
long propertyValueGender = propertyValue.getValue();
assertEquals(propertyValueGender, 1);
propertyValue.destroy();
propertyValue = KuzuNodeValue.getPropertyValueAt(value, 3);
propertyValue = KuzuValueNodeUtil.getPropertyValueAt(value, 3);
boolean propertyValueIsStudent = propertyValue.getValue();
assertEquals(propertyValueIsStudent, true);
propertyValue.destroy();
Expand All @@ -620,7 +620,7 @@ void NodeValToString() throws KuzuObjectRefDestroyedException {
KuzuFlatTuple flatTuple = result.getNext();
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());
String str = KuzuNodeValue.toString(value);
String str = KuzuValueNodeUtil.toString(value);
assertEquals(str, "{_ID: 1:0, _LABEL: organisation, ID: 1, name: ABFsUni, orgCode: 325, mark: 3.700000, " +
"score: -2, history: 10 years 5 months 13 hours 24 us, licenseValidInterval: 3 years " +
"5 days, rating: 1.000000, state: {revenue: 138, location: ['toronto', 'montr,eal'], " +
Expand All @@ -641,18 +641,18 @@ void RelValGetIDsAndLabel() throws KuzuObjectRefDestroyedException {
assertTrue(value.isOwnedByCPP());
assertFalse(value.isNull());

KuzuInternalID srcId = KuzuRelValue.getSrcID(value);
KuzuInternalID srcId = KuzuValueRelUtil.getSrcID(value);
assertEquals(srcId.table_id, 0);
assertEquals(srcId.offset, 0);

KuzuInternalID dstId = KuzuRelValue.getDstID(value);
KuzuInternalID dstId = KuzuValueRelUtil.getDstID(value);
assertEquals(dstId.table_id, 0);
assertEquals(dstId.offset, 1);

String label = KuzuRelValue.getLabelName(value);
String label = KuzuValueRelUtil.getLabelName(value);
assertTrue(label.equals("knows"));

long size = KuzuRelValue.getPropertySize(value);
long size = KuzuValueRelUtil.getPropertySize(value);
assertEquals(size, 4);

value.destroy();
Expand All @@ -669,16 +669,16 @@ void RelValGetProperty() throws KuzuObjectRefDestroyedException {
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());

String propertyName = KuzuRelValue.getPropertyNameAt(value, 0);
String propertyName = KuzuValueRelUtil.getPropertyNameAt(value, 0);
assertEquals(propertyName, "year");

propertyName = KuzuRelValue.getPropertyNameAt(value, 1);
propertyName = KuzuValueRelUtil.getPropertyNameAt(value, 1);
assertEquals(propertyName, "grading");

propertyName = KuzuRelValue.getPropertyNameAt(value, 2);
propertyName = KuzuValueRelUtil.getPropertyNameAt(value, 2);
assertEquals(propertyName, "rating");

KuzuValue propertyValue = KuzuRelValue.getPropertyValueAt(value, 0);
KuzuValue propertyValue = KuzuValueRelUtil.getPropertyValueAt(value, 0);
long propertyValueYear = propertyValue.getValue();
assertEquals(propertyValueYear, 2015);

Expand All @@ -696,11 +696,101 @@ void RelValToString() throws KuzuObjectRefDestroyedException {
KuzuFlatTuple flatTuple = result.getNext();
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());
String str = KuzuRelValue.toString(value);
String str = KuzuValueRelUtil.toString(value);
assertEquals(str, "(0:2)-{_LABEL: workAt, _ID: 5:0, year: 2015, grading: [3.800000,2.500000], " +
"rating: 8.200000}->(1:1)");
value.destroy();
flatTuple.destroy();
result.destroy();
}

@Test
void StructValGetNumFields() throws KuzuObjectRefDestroyedException {
KuzuQueryResult result = conn.query("MATCH (m:movies) WHERE m.name=\"Roma\" RETURN m.description");
assertTrue(result.isSuccess());
assertTrue(result.hasNext());
KuzuFlatTuple flatTuple = result.getNext();
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());
assertEquals(KuzuValueStructUtil.getNumFields(value), 4);
value.destroy();
flatTuple.destroy();
result.destroy();
}

@Test
void StructValGetIndexByFieldName() throws KuzuObjectRefDestroyedException {
KuzuQueryResult result = conn.query("MATCH (m:movies) WHERE m.name=\"Roma\" RETURN m.description");
assertTrue(result.isSuccess());
assertTrue(result.hasNext());
KuzuFlatTuple flatTuple = result.getNext();
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());
assertEquals(KuzuValueStructUtil.getIndexByFieldName(value, "NOT_EXIST"), -1);

assertEquals(KuzuValueStructUtil.getIndexByFieldName(value, "rating"), 0);
assertEquals(KuzuValueStructUtil.getIndexByFieldName(value, "views"), 1);
assertEquals(KuzuValueStructUtil.getIndexByFieldName(value, "release"), 2);
assertEquals(KuzuValueStructUtil.getIndexByFieldName(value, "film"), 3);
value.destroy();
flatTuple.destroy();
result.destroy();
}

@Test
void StructValGetFieldNameByIndex() throws KuzuObjectRefDestroyedException {
KuzuQueryResult result = conn.query("MATCH (m:movies) WHERE m.name=\"Roma\" RETURN m.description");
assertTrue(result.isSuccess());
assertTrue(result.hasNext());
KuzuFlatTuple flatTuple = result.getNext();
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());
assertNull(KuzuValueStructUtil.getFieldNameByIndex(value, 1024));
assertNull(KuzuValueStructUtil.getFieldNameByIndex(value, -1));
assertEquals(KuzuValueStructUtil.getFieldNameByIndex(value, 0), "rating");
assertEquals(KuzuValueStructUtil.getFieldNameByIndex(value, 1), "views");
assertEquals(KuzuValueStructUtil.getFieldNameByIndex(value, 2), "release");
assertEquals(KuzuValueStructUtil.getFieldNameByIndex(value, 3), "film");
value.destroy();
flatTuple.destroy();
result.destroy();
}

@Test
void StructValGetValueByFieldName() throws KuzuObjectRefDestroyedException {
KuzuQueryResult result = conn.query("MATCH (m:movies) WHERE m.name=\"Roma\" RETURN m.description ORDER BY m.name");
assertTrue(result.isSuccess());
assertTrue(result.hasNext());
KuzuFlatTuple flatTuple = result.getNext();
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());
KuzuValue fieldValue = KuzuValueStructUtil.getValueByFieldName(value, "NOT_EXIST");
assertNull(fieldValue);
fieldValue = KuzuValueStructUtil.getValueByFieldName(value, "rating");
assertEquals(fieldValue.getValue(), 1223.0);
fieldValue.destroy();
value.destroy();
flatTuple.destroy();
result.destroy();
}

@Test
void StructValGetValueByIndex() throws KuzuObjectRefDestroyedException {
KuzuQueryResult result = conn.query("MATCH (m:movies) WHERE m.name=\"Roma\" RETURN m.description ORDER BY m.name");
assertTrue(result.isSuccess());
assertTrue(result.hasNext());
KuzuFlatTuple flatTuple = result.getNext();
KuzuValue value = flatTuple.getValue(0);
assertTrue(value.isOwnedByCPP());
KuzuValue fieldValue = KuzuValueStructUtil.getValueByIndex(value, 1024);
assertNull(fieldValue);
fieldValue = KuzuValueStructUtil.getValueByIndex(value, -1);
assertNull(fieldValue);
fieldValue = KuzuValueStructUtil.getValueByIndex(value, 0);
assertEquals(fieldValue.getValue(), 1223.0);
fieldValue.destroy();
value.destroy();
flatTuple.destroy();
result.destroy();
}
}

0 comments on commit 0b5b617

Please sign in to comment.