-
Notifications
You must be signed in to change notification settings - Fork 818
[#8838] feat(catalogs): Support create/load/list table operation for lance table. #8879
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f2de31b
30921b0
a52d9fb
95c7c18
a2b0735
f0da361
a64ab69
e9fb50d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 { | ||
|
|
||
|
|
@@ -73,10 +89,81 @@ public static Index of(Index.IndexType indexType, String name, String[][] fieldN | |
| .build(); | ||
| } | ||
|
|
||
| /** Custom JSON serializer for Index objects. */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| 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; | ||
|
|
||
| /** | ||
|
|
||
| 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()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,7 @@ dependencies { | |
| implementation(libs.commons.lang3) | ||
| implementation(libs.guava) | ||
| implementation(libs.hadoop3.client.api) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think hadoop3 is not required. |
||
| implementation(libs.lance) | ||
|
|
||
| annotationProcessor(libs.lombok) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
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.