Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ dependencies {
implementation(libs.commons.lang3)
implementation(libs.commons.collections4)
implementation(libs.guava)
implementation(libs.jackson.annotations)
implementation(libs.jackson.databind)

testImplementation(libs.junit.jupiter.api)
testImplementation(libs.junit.jupiter.params)
Expand Down
47 changes: 47 additions & 0 deletions api/src/main/java/org/apache/gravitino/rel/GenericTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.gravitino.rel;

/** A generic table interface that extends the Table interface. */
public interface GenericTable extends Table {

/**
* Formats the table as a string representation.
*
* @return the formatted string representation of the table
*/
String format();

/**
* Gets the location of the table.
*
* @return the location of the table
*/
String location();

/**
* Indicates whether the table is external.
*
* @return true if the table is external, false otherwise
*/
default boolean external() {
return false;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can extend the current table interface, it's not good to add a new interface.

87 changes: 87 additions & 0 deletions api/src/main/java/org/apache/gravitino/rel/indexes/Indexes.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@
*/
package org.apache.gravitino.rel.indexes;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.List;
import java.util.Locale;

/** Helper methods to create index to pass into Apache Gravitino. */
public class Indexes {

Expand Down Expand Up @@ -73,10 +89,81 @@ public static Index of(Index.IndexType indexType, String name, String[][] fieldN
.build();
}

/** Custom JSON serializer for Index objects. */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not proper to add the JSON serde in the API module. Should if be in the class like IndexDTO, and then convert the IndexDTO to the IndexImpl here?

public static class IndexSerializer extends JsonSerializer<Index> {
@Override
public void serialize(Index value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
gen.writeStartObject();
gen.writeStringField("indexType", value.type().name().toUpperCase(Locale.ROOT));
if (null != value.name()) {
gen.writeStringField("name", value.name());
}
gen.writeFieldName("fieldNames");
gen.writeObject(value.fieldNames());
gen.writeEndObject();
}
}

/** Custom JSON deserializer for Index objects. */
public static class IndexDeserializer extends JsonDeserializer<Index> {

@Override
public Index deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
JsonNode node = p.getCodec().readTree(p);
Preconditions.checkArgument(
node != null && !node.isNull() && node.isObject(),
"Index must be a valid JSON object, but found: %s",
node);

IndexImpl.Builder builder = IndexImpl.builder();
Preconditions.checkArgument(
node.has("indexType"), "Cannot parse index from missing type: %s", node);
String indexType = getString("indexType", node);
builder.withIndexType(Index.IndexType.valueOf(indexType.toUpperCase(Locale.ROOT)));
if (node.has("name")) {
builder.withName(getString("name", node));
}
Preconditions.checkArgument(
node.has("fieldNames"), "Cannot parse index from missing field names: %s", node);
List<String[]> fieldNames = Lists.newArrayList();
node.get("fieldNames").forEach(field -> fieldNames.add(getStringArray((ArrayNode) field)));
builder.withFieldNames(fieldNames.toArray(new String[0][0]));
return builder.build();
}

private static String[] getStringArray(ArrayNode node) {
String[] array = new String[node.size()];
for (int i = 0; i < node.size(); i++) {
array[i] = node.get(i).asText();
}
return array;
}

private static String getString(String property, JsonNode node) {
Preconditions.checkArgument(node.has(property), "Cannot parse missing string: %s", property);
JsonNode pNode = node.get(property);
return convertToString(property, pNode);
}

private static String convertToString(String property, JsonNode pNode) {
Preconditions.checkArgument(
pNode != null && !pNode.isNull() && pNode.isTextual(),
"Cannot parse to a string value %s: %s",
property,
pNode);
return pNode.asText();
}
}

/** The user side implementation of the index. */
@JsonSerialize(using = IndexSerializer.class)
@JsonDeserialize(using = IndexDeserializer.class)
public static final class IndexImpl implements Index {
private final IndexType indexType;

private final String name;

private final String[][] fieldNames;

/**
Expand Down
57 changes: 57 additions & 0 deletions api/src/test/java/org/apache/gravitino/rel/TestIndex.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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.gravitino.rel;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.cfg.EnumFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.apache.gravitino.rel.indexes.Index;
import org.apache.gravitino.rel.indexes.Indexes;
import org.apache.gravitino.rel.indexes.Indexes.IndexImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class TestIndex {

@Test
void testIndexSerialization() throws JsonProcessingException {
String[][] fields = {{"column1"}, {"column2", "subcolumn"}};
Index index = Indexes.unique("test_index", fields);

JsonMapper jsonMapper =
JsonMapper.builder()
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(EnumFeature.WRITE_ENUMS_TO_LOWERCASE, true)
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true)
.build();

String json = jsonMapper.writeValueAsString(index);

Index deserializedIndex = jsonMapper.readValue(json, IndexImpl.class);
Assertions.assertEquals(index.type(), deserializedIndex.type());
Assertions.assertEquals(index.name(), deserializedIndex.name());
Assertions.assertArrayEquals(index.fieldNames(), deserializedIndex.fieldNames());
}
}
1 change: 1 addition & 0 deletions catalogs/catalog-generic-lakehouse/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dependencies {
implementation(libs.commons.lang3)
implementation(libs.guava)
implementation(libs.hadoop3.client.api)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think hadoop3 is not required.

implementation(libs.lance)

annotationProcessor(libs.lombok)

Expand Down
Loading