This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch branch-0.9
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-0.9 by this push:
new 18e96e41e3 [#6816] feat(server): Support update properties for model
version (#7000)
18e96e41e3 is described below
commit 18e96e41e3ed6cfbd428bfffd9d60bdb7d058f0c
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Apr 18 15:49:53 2025 +0800
[#6816] feat(server): Support update properties for model version (#7000)
### What changes were proposed in this pull request?
Support update properties for model version
### Why are the changes needed?
Fix: #6816
### Does this PR introduce _any_ user-facing change?
user now can set or remove a property from a model version.
### How was this patch tested?
local test.
#### Add property
`bin/gcli.sh model set -m demo_metalake --name
model_catalog.schema.model2 --alias test --property "new_property"
--value "new_value"`
<img width="1050" alt="image"
src="https://github.com/user-attachments/assets/646091da-896f-47b8-8673-7ec082eb4eb5"
/>
#### update property
`bin/gcli.sh model set -m demo_metalake --name
model_catalog.schema.model2 --alias test --property "new_property"
--value "update_value"`
#### Remove property
`bin/gcli.sh model remove -m demo_metalake --name
model_catalog.schema.model2 --alias test --property "new_property"`
<img width="1050" alt="image"
src="https://github.com/user-attachments/assets/a38a7bf6-f24f-4678-8a8b-005762b0b127"
/>
Co-authored-by: Lord of Abyss
<[email protected]>
---
.../apache/gravitino/model/ModelVersionChange.java | 155 +++++++++++++++
.../gravitino/model/TestModelVersionChange.java | 92 +++++++++
.../catalog/model/ModelCatalogOperations.java | 19 ++
.../catalog/model/TestModelCatalogOperations.java | 215 +++++++++++++++++++++
.../integration/test/ModelCatalogOperationsIT.java | 144 +++++++++++++-
.../org/apache/gravitino/cli/ErrorMessages.java | 1 +
.../apache/gravitino/cli/ModelCommandHandler.java | 62 ++++--
.../apache/gravitino/cli/TestableCommandLine.java | 29 +++
...operty.java => RemoveModelVersionProperty.java} | 60 +++---
.../gravitino/cli/commands/SetModelProperty.java | 5 +-
...lProperty.java => SetModelVersionProperty.java} | 52 +++--
.../apache/gravitino/cli/TestModelCommands.java | 185 +++++++++++++++++-
.../org/apache/gravitino/client/DTOConverters.java | 9 +
.../gravitino/api/model_version_change.py | 112 +++++++++++
.../gravitino/client/generic_model_catalog.py | 11 +-
.../dto/requests/model_version_update_request.py | 46 +++++
.../tests/integration/test_model_catalog.py | 47 +++++
.../dto/requests/ModelVersionUpdateRequest.java | 70 ++++++-
.../catalog/TestModelOperationDispatcher.java | 156 +++++++++++++++
.../gravitino/connector/TestCatalogOperations.java | 9 +
.../service/TestModelVersionMetaService.java | 84 ++++++++
21 files changed, 1502 insertions(+), 61 deletions(-)
diff --git
a/api/src/main/java/org/apache/gravitino/model/ModelVersionChange.java
b/api/src/main/java/org/apache/gravitino/model/ModelVersionChange.java
index 2b18bccb26..9a35c28f85 100644
--- a/api/src/main/java/org/apache/gravitino/model/ModelVersionChange.java
+++ b/api/src/main/java/org/apache/gravitino/model/ModelVersionChange.java
@@ -40,6 +40,27 @@ public interface ModelVersionChange {
return new ModelVersionChange.UpdateComment(newComment);
}
+ /**
+ * Create a ModelVersionChange for setting a property of a model version.
+ *
+ * @param property name of the property to be set
+ * @param value value to be set for the property
+ * @return A new ModelVersionChange instance for setting a property of a
model version
+ */
+ static ModelVersionChange setProperty(String property, String value) {
+ return new ModelVersionChange.SetProperty(property, value);
+ }
+
+ /**
+ * Create a ModelVersionChange for removing a property from a model version.
+ *
+ * @param property The name of the property to be removed.
+ * @return The new ModelVersionChange instance for removing a property from
a model version
+ */
+ static ModelVersionChange removeProperty(String property) {
+ return new ModelVersionChange.RemoveProperty(property);
+ }
+
/** A ModelVersionChange to update the model version comment. */
final class UpdateComment implements ModelVersionChange {
@@ -101,4 +122,138 @@ public interface ModelVersionChange {
return "UpdateComment " + newComment;
}
}
+
+ /** A ModelVersionChange to set a property of a model version. */
+ final class SetProperty implements ModelVersionChange {
+ private final String property;
+ private final String value;
+
+ /**
+ * Creates a new {@link SetProperty} instance with the specified property
name and value.
+ *
+ * @param property name of the property to be set
+ * @param value value to be set for the property
+ */
+ public SetProperty(String property, String value) {
+ this.property = property;
+ this.value = value;
+ }
+
+ /**
+ * Returns the name of the property to be set.
+ *
+ * @return the name of the property to be set
+ */
+ public String property() {
+ return property;
+ }
+
+ /**
+ * Returns the value to be set for the property.
+ *
+ * @return the value to be set for the property
+ */
+ public String value() {
+ return value;
+ }
+
+ /**
+ * Compares this SetProperty instance with another object for equality.
Two instances are
+ * considered equal if they target the same property and set the same
value.
+ *
+ * @param obj The object to compare with this instance.
+ * @return {@code true} if the given object represents the same property
set; {@code false}
+ * otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) return true;
+ if (!(obj instanceof ModelVersionChange.SetProperty)) return false;
+ ModelVersionChange.SetProperty other = (ModelVersionChange.SetProperty)
obj;
+ return Objects.equals(property, other.property) && Objects.equals(value,
other.value);
+ }
+
+ /**
+ * Generates a hash code for this SetProperty instance. The hash code is
based on the property
+ * name and value to be set.
+ *
+ * @return A hash code value for this property set operation.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(property, value);
+ }
+
+ /**
+ * Provides a string representation of the SetProperty instance. This
string format includes the
+ * class name followed by the property name and value to be set.
+ *
+ * @return A string summary of the property set operation.
+ */
+ @Override
+ public String toString() {
+ return "SETPROPERTY " + property + " " + value;
+ }
+ }
+
+ /** A ModelVersionChange to remove a property from a model version. */
+ final class RemoveProperty implements ModelVersionChange {
+ private final String property;
+
+ /**
+ * Creates a new {@link RemoveProperty} instance with the specified
property name.
+ *
+ * @param property name of the property to be removed
+ */
+ public RemoveProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * Returns the name of the property to be removed.
+ *
+ * @return the name of the property to be removed
+ */
+ public String property() {
+ return property;
+ }
+
+ /**
+ * Compares this RemoveProperty instance with another object for equality.
Two instances are
+ * considered equal if they target the same property.
+ *
+ * @param obj The object to compare with this instance.
+ * @return {@code true} if the given object represents the same property
removal; {@code false}
+ * otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) return true;
+ if (!(obj instanceof ModelVersionChange.RemoveProperty)) return false;
+ ModelVersionChange.RemoveProperty other =
(ModelVersionChange.RemoveProperty) obj;
+ return Objects.equals(property, other.property);
+ }
+
+ /**
+ * Generates a hash code for this RemoveProperty instance. The hash code
is based on the
+ * property name to be removed.
+ *
+ * @return A hash code value for this property removal operation.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(property);
+ }
+
+ /**
+ * Provides a string representation of the RemoveProperty instance. This
string format includes
+ * the class name followed by the property name to be removed.
+ *
+ * @return A string summary of the property removal operation.
+ */
+ @Override
+ public String toString() {
+ return "REMOVEPROPERTY " + property;
+ }
+ }
}
diff --git
a/api/src/test/java/org/apache/gravitino/model/TestModelVersionChange.java
b/api/src/test/java/org/apache/gravitino/model/TestModelVersionChange.java
index bc4e9bb668..f978437f27 100644
--- a/api/src/test/java/org/apache/gravitino/model/TestModelVersionChange.java
+++ b/api/src/test/java/org/apache/gravitino/model/TestModelVersionChange.java
@@ -63,4 +63,96 @@ public class TestModelVersionChange {
Assertions.assertNotEquals(modelVersionChange1.hashCode(),
modelVersionChange3.hashCode());
Assertions.assertNotEquals(modelVersionChange2.hashCode(),
modelVersionChange3.hashCode());
}
+
+ @Test
+ void testCreateSetPropertyChangeUseStaticMethod() {
+ String property = "property";
+ String value = "value";
+ ModelVersionChange modelVersionChange =
ModelVersionChange.setProperty(property, value);
+
+ Assertions.assertEquals(ModelVersionChange.SetProperty.class,
modelVersionChange.getClass());
+
+ ModelVersionChange.SetProperty setPropertyChange =
+ (ModelVersionChange.SetProperty) modelVersionChange;
+ Assertions.assertEquals(property, setPropertyChange.property());
+ Assertions.assertEquals(value, setPropertyChange.value());
+ Assertions.assertEquals("SETPROPERTY " + property + " " + value,
setPropertyChange.toString());
+ }
+
+ @Test
+ void testCreateSetPropertyChangeUseConstructor() {
+ String property = "property";
+ String value = "value";
+ ModelVersionChange modelVersionChange = new
ModelVersionChange.SetProperty(property, value);
+
+ Assertions.assertEquals(ModelVersionChange.SetProperty.class,
modelVersionChange.getClass());
+
+ ModelVersionChange.SetProperty setPropertyChange =
+ (ModelVersionChange.SetProperty) modelVersionChange;
+ Assertions.assertEquals(property, setPropertyChange.property());
+ Assertions.assertEquals(value, setPropertyChange.value());
+ Assertions.assertEquals("SETPROPERTY " + property + " " + value,
setPropertyChange.toString());
+ }
+
+ @Test
+ void testSetPropertyChangeEquals() {
+ String property1 = "property1";
+ String value1 = "value1";
+ String property2 = "property2";
+ ModelVersionChange modelVersionChange1 =
ModelVersionChange.setProperty(property1, value1);
+ ModelVersionChange modelVersionChange2 =
ModelVersionChange.setProperty(property1, value1);
+ ModelVersionChange modelVersionChange3 =
ModelVersionChange.setProperty(property2, value1);
+
+ Assertions.assertEquals(modelVersionChange1, modelVersionChange2);
+ Assertions.assertNotEquals(modelVersionChange1, modelVersionChange3);
+ Assertions.assertNotEquals(modelVersionChange2, modelVersionChange3);
+
+ Assertions.assertEquals(modelVersionChange1.hashCode(),
modelVersionChange2.hashCode());
+ Assertions.assertNotEquals(modelVersionChange1.hashCode(),
modelVersionChange3.hashCode());
+ Assertions.assertNotEquals(modelVersionChange2.hashCode(),
modelVersionChange3.hashCode());
+ }
+
+ @Test
+ void testCreateRemovePropertyChangeUseStaticMethod() {
+ String property = "property";
+ ModelVersionChange modelVersionChange =
ModelVersionChange.removeProperty(property);
+
+ Assertions.assertEquals(ModelVersionChange.RemoveProperty.class,
modelVersionChange.getClass());
+
+ ModelVersionChange.RemoveProperty removePropertyChange =
+ (ModelVersionChange.RemoveProperty) modelVersionChange;
+ Assertions.assertEquals(property, removePropertyChange.property());
+ Assertions.assertEquals("REMOVEPROPERTY " + property,
removePropertyChange.toString());
+ }
+
+ @Test
+ void testCreateRemovePropertyChangeUseConstructor() {
+ String property = "property";
+ ModelVersionChange modelVersionChange = new
ModelVersionChange.RemoveProperty(property);
+
+ Assertions.assertEquals(ModelVersionChange.RemoveProperty.class,
modelVersionChange.getClass());
+
+ ModelVersionChange.RemoveProperty removePropertyChange =
+ (ModelVersionChange.RemoveProperty) modelVersionChange;
+ Assertions.assertEquals(property, removePropertyChange.property());
+ Assertions.assertEquals("REMOVEPROPERTY " + property,
removePropertyChange.toString());
+ }
+
+ @Test
+ void testRemovePropertyChangeEquals() {
+ String property1 = "property1";
+ String property2 = "property2";
+
+ ModelVersionChange modelVersionChange1 =
ModelVersionChange.removeProperty(property1);
+ ModelVersionChange modelVersionChange2 =
ModelVersionChange.removeProperty(property1);
+ ModelVersionChange modelVersionChange3 =
ModelVersionChange.removeProperty(property2);
+
+ Assertions.assertEquals(modelVersionChange1, modelVersionChange2);
+ Assertions.assertNotEquals(modelVersionChange1, modelVersionChange3);
+ Assertions.assertNotEquals(modelVersionChange2, modelVersionChange3);
+
+ Assertions.assertEquals(modelVersionChange1.hashCode(),
modelVersionChange2.hashCode());
+ Assertions.assertNotEquals(modelVersionChange1.hashCode(),
modelVersionChange3.hashCode());
+ Assertions.assertNotEquals(modelVersionChange2.hashCode(),
modelVersionChange3.hashCode());
+ }
}
diff --git
a/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
b/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
index bba059308b..d0c9b89cb6 100644
---
a/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
+++
b/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
@@ -416,6 +416,15 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
if (change instanceof ModelVersionChange.UpdateComment) {
entityComment = ((ModelVersionChange.UpdateComment)
change).newComment();
+ } else if (change instanceof ModelVersionChange.SetProperty) {
+ ModelVersionChange.SetProperty setPropertyChange =
(ModelVersionChange.SetProperty) change;
+ doSetProperty(entityProperties, setPropertyChange);
+
+ } else if (change instanceof ModelVersionChange.RemoveProperty) {
+ ModelVersionChange.RemoveProperty removePropertyChange =
+ (ModelVersionChange.RemoveProperty) change;
+ doRemoveProperty(entityProperties, removePropertyChange);
+
} else {
throw new IllegalArgumentException(
"Unsupported model version change: " +
change.getClass().getSimpleName());
@@ -489,4 +498,14 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
private void doSetProperty(Map<String, String> entityProperties,
ModelChange.SetProperty change) {
entityProperties.put(change.property(), change.value());
}
+
+ private void doSetProperty(
+ Map<String, String> entityProperties, ModelVersionChange.SetProperty
change) {
+ entityProperties.put(change.property(), change.value());
+ }
+
+ private void doRemoveProperty(
+ Map<String, String> entityProperties, ModelVersionChange.RemoveProperty
change) {
+ entityProperties.remove(change.property());
+ }
}
diff --git
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
index 2bb88497bc..c84e2fc9cf 100644
---
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
+++
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
@@ -35,6 +35,7 @@ import static
org.apache.gravitino.Configs.VERSION_RETENTION_COUNT;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import java.io.File;
import java.io.IOException;
import java.time.Instant;
@@ -895,6 +896,220 @@ public class TestModelCatalogOperations {
Assertions.assertEquals(versionProperties,
updatedModelVersion.properties());
}
+ @Test
+ void testSetAndUpdateModelVersionProperty() {
+ String schemaName = randomSchemaName();
+ createSchema(schemaName);
+
+ String modelName = "model1";
+ String modelComment = "model1 comment";
+
+ String versionComment = "version1 comment";
+ String versionUri = "model_version_path";
+ String[] versionAliases = new String[] {"alias1", "alias2"};
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(METALAKE_NAME, CATALOG_NAME, schemaName,
modelName);
+ StringIdentifier stringId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> properties =
StringIdentifier.newPropertiesWithId(stringId, null);
+
+ ops.registerModel(modelIdent, modelComment, properties);
+ StringIdentifier versionId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> versionProperties =
+ StringIdentifier.newPropertiesWithId(
+ versionId, ImmutableMap.of("key1", "value1", "key2", "value2"));
+ Map<String, String> tmpMap = Maps.newHashMap(versionProperties);
+ tmpMap.put("key3", "value3");
+ tmpMap.put("key1", "new value");
+ Map<String, String> newProperties = ImmutableMap.copyOf(tmpMap);
+
+ ops.linkModelVersion(modelIdent, versionUri, versionAliases,
versionComment, versionProperties);
+
+ // validate loaded model
+ Model loadedModel = ops.getModel(modelIdent);
+ Assertions.assertEquals(1, loadedModel.latestVersion());
+
+ // validate loaded version
+ ModelVersion loadedVersion = ops.getModelVersion(modelIdent, 0);
+ Assertions.assertEquals(0, loadedVersion.version());
+ Assertions.assertArrayEquals(versionAliases, loadedVersion.aliases());
+ Assertions.assertEquals(versionComment, loadedVersion.comment());
+ Assertions.assertEquals(versionUri, loadedVersion.uri());
+ Assertions.assertEquals(versionProperties, loadedVersion.properties());
+
+ // set property via version and validate
+ ModelVersionChange updatePropertyChange =
ModelVersionChange.setProperty("key1", "new value");
+ ModelVersionChange addPropertyChange =
ModelVersionChange.setProperty("key3", "value3");
+
+ ModelVersion updatedModelVersion =
+ ops.alterModelVersion(modelIdent, 0, updatePropertyChange,
addPropertyChange);
+
+ Assertions.assertEquals(0, updatedModelVersion.version());
+ Assertions.assertEquals(versionUri, updatedModelVersion.uri());
+ Assertions.assertEquals(versionComment, updatedModelVersion.comment());
+ Assertions.assertArrayEquals(versionAliases,
updatedModelVersion.aliases());
+ Assertions.assertEquals(newProperties, updatedModelVersion.properties());
+ }
+
+ @Test
+ void testSetAndUpdateModelVersionPropertyByAlias() {
+ String schemaName = randomSchemaName();
+ createSchema(schemaName);
+
+ String modelName = "model1";
+ String modelComment = "model1 comment";
+
+ String versionComment = "version1 comment";
+ String versionUri = "model_version_path";
+ String[] versionAliases = new String[] {"alias1", "alias2"};
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(METALAKE_NAME, CATALOG_NAME, schemaName,
modelName);
+ StringIdentifier stringId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> properties =
StringIdentifier.newPropertiesWithId(stringId, null);
+
+ ops.registerModel(modelIdent, modelComment, properties);
+ StringIdentifier versionId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> versionProperties =
+ StringIdentifier.newPropertiesWithId(
+ versionId, ImmutableMap.of("key1", "value1", "key2", "value2"));
+ Map<String, String> tmpMap = Maps.newHashMap(versionProperties);
+ tmpMap.put("key3", "value3");
+ tmpMap.put("key1", "new value");
+ Map<String, String> newProperties = ImmutableMap.copyOf(tmpMap);
+
+ ops.linkModelVersion(modelIdent, versionUri, versionAliases,
versionComment, versionProperties);
+
+ // validate loaded model
+ Model loadedModel = ops.getModel(modelIdent);
+ Assertions.assertEquals(1, loadedModel.latestVersion());
+
+ // validate loaded version
+ ModelVersion loadedVersion = ops.getModelVersion(modelIdent,
versionAliases[0]);
+ Assertions.assertEquals(0, loadedVersion.version());
+ Assertions.assertArrayEquals(versionAliases, loadedVersion.aliases());
+ Assertions.assertEquals(versionComment, loadedVersion.comment());
+ Assertions.assertEquals(versionUri, loadedVersion.uri());
+ Assertions.assertEquals(versionProperties, loadedVersion.properties());
+
+ // set property via version and validate
+ ModelVersionChange updatePropertyChange =
ModelVersionChange.setProperty("key1", "new value");
+ ModelVersionChange addPropertyChange =
ModelVersionChange.setProperty("key3", "value3");
+
+ ModelVersion updatedModelVersion =
+ ops.alterModelVersion(
+ modelIdent, versionAliases[0], updatePropertyChange,
addPropertyChange);
+
+ Assertions.assertEquals(0, updatedModelVersion.version());
+ Assertions.assertEquals(versionUri, updatedModelVersion.uri());
+ Assertions.assertEquals(versionComment, updatedModelVersion.comment());
+ Assertions.assertArrayEquals(versionAliases,
updatedModelVersion.aliases());
+ Assertions.assertEquals(newProperties, updatedModelVersion.properties());
+ }
+
+ @Test
+ void testRemoveModelVersionProperty() {
+ String schemaName = randomSchemaName();
+ createSchema(schemaName);
+
+ String modelName = "model1";
+ String modelComment = "model1 comment";
+
+ String versionComment = "version1 comment";
+ String versionUri = "model_version_path";
+ String[] versionAliases = new String[] {"alias1", "alias2"};
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(METALAKE_NAME, CATALOG_NAME, schemaName,
modelName);
+ StringIdentifier stringId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> properties =
StringIdentifier.newPropertiesWithId(stringId, null);
+
+ ops.registerModel(modelIdent, modelComment, properties);
+ StringIdentifier versionId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> versionProperties =
+ StringIdentifier.newPropertiesWithId(
+ versionId, ImmutableMap.of("key1", "value1", "key2", "value2"));
+ Map<String, String> newVersionProperties =
+ StringIdentifier.newPropertiesWithId(versionId,
ImmutableMap.of("key1", "value1"));
+
+ ops.linkModelVersion(modelIdent, versionUri, versionAliases,
versionComment, versionProperties);
+
+ // validate loaded model
+ Model loadedModel = ops.getModel(modelIdent);
+ Assertions.assertEquals(1, loadedModel.latestVersion());
+
+ // validate loaded version
+ ModelVersion loadedVersion = ops.getModelVersion(modelIdent, 0);
+ Assertions.assertEquals(0, loadedVersion.version());
+ Assertions.assertArrayEquals(versionAliases, loadedVersion.aliases());
+ Assertions.assertEquals(versionComment, loadedVersion.comment());
+ Assertions.assertEquals(versionUri, loadedVersion.uri());
+ Assertions.assertEquals(versionProperties, loadedVersion.properties());
+
+ // set property via version and validate
+ ModelVersionChange removeProperty =
ModelVersionChange.removeProperty("key2");
+
+ ModelVersion updatedModelVersion = ops.alterModelVersion(modelIdent, 0,
removeProperty);
+
+ Assertions.assertEquals(0, updatedModelVersion.version());
+ Assertions.assertEquals(versionUri, updatedModelVersion.uri());
+ Assertions.assertEquals(versionComment, updatedModelVersion.comment());
+ Assertions.assertArrayEquals(versionAliases,
updatedModelVersion.aliases());
+ Assertions.assertEquals(newVersionProperties,
updatedModelVersion.properties());
+ }
+
+ @Test
+ void testRemoveModelVersionPropertyByAlias() {
+ String schemaName = randomSchemaName();
+ createSchema(schemaName);
+
+ String modelName = "model1";
+ String modelComment = "model1 comment";
+
+ String versionComment = "version1 comment";
+ String versionUri = "model_version_path";
+ String[] versionAliases = new String[] {"alias1", "alias2"};
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(METALAKE_NAME, CATALOG_NAME, schemaName,
modelName);
+ StringIdentifier stringId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> properties =
StringIdentifier.newPropertiesWithId(stringId, null);
+
+ ops.registerModel(modelIdent, modelComment, properties);
+ StringIdentifier versionId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> versionProperties =
+ StringIdentifier.newPropertiesWithId(
+ versionId, ImmutableMap.of("key1", "value1", "key2", "value2"));
+ Map<String, String> newVersionProperties =
+ StringIdentifier.newPropertiesWithId(versionId,
ImmutableMap.of("key1", "value1"));
+
+ ops.linkModelVersion(modelIdent, versionUri, versionAliases,
versionComment, versionProperties);
+
+ // validate loaded model
+ Model loadedModel = ops.getModel(modelIdent);
+ Assertions.assertEquals(1, loadedModel.latestVersion());
+
+ // validate loaded version
+ ModelVersion loadedVersion = ops.getModelVersion(modelIdent,
versionAliases[0]);
+ Assertions.assertEquals(0, loadedVersion.version());
+ Assertions.assertArrayEquals(versionAliases, loadedVersion.aliases());
+ Assertions.assertEquals(versionComment, loadedVersion.comment());
+ Assertions.assertEquals(versionUri, loadedVersion.uri());
+ Assertions.assertEquals(versionProperties, loadedVersion.properties());
+
+ // set property via version and validate
+ ModelVersionChange removeProperty =
ModelVersionChange.removeProperty("key2");
+
+ ModelVersion updatedModelVersion =
+ ops.alterModelVersion(modelIdent, versionAliases[0], removeProperty);
+
+ Assertions.assertEquals(0, updatedModelVersion.version());
+ Assertions.assertEquals(versionUri, updatedModelVersion.uri());
+ Assertions.assertEquals(versionComment, updatedModelVersion.comment());
+ Assertions.assertArrayEquals(versionAliases,
updatedModelVersion.aliases());
+ Assertions.assertEquals(newVersionProperties,
updatedModelVersion.properties());
+ }
+
private String randomSchemaName() {
return "schema_" + UUID.randomUUID().toString().replace("-", "");
}
diff --git
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/integration/test/ModelCatalogOperationsIT.java
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/integration/test/ModelCatalogOperationsIT.java
index 1b582672fb..d75c21f371 100644
---
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/integration/test/ModelCatalogOperationsIT.java
+++
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/integration/test/ModelCatalogOperationsIT.java
@@ -331,7 +331,7 @@ public class ModelCatalogOperationsIT extends BaseIT {
}
@Test
- void testLinkAndUpdateModelVersionCommentViaVersion() {
+ void testLinkAndUpdateModelVersionComment() {
String modelName = RandomNameUtils.genRandomName("model1");
Map<String, String> properties = ImmutableMap.of("key1", "val1", "key2",
"val2");
NameIdentifier modelIdent = NameIdentifier.of(schemaName, modelName);
@@ -482,6 +482,148 @@ public class ModelCatalogOperationsIT extends BaseIT {
Assertions.assertEquals(createdModel.comment(), alteredModel.comment());
}
+ @Test
+ void testLinkAndSetModelVersionProperties() {
+ String modelName = RandomNameUtils.genRandomName("model1");
+ String[] aliases = {"alias1"};
+ Map<String, String> properties = ImmutableMap.of("key1", "val1", "key2",
"val2");
+ NameIdentifier modelIdent = NameIdentifier.of(schemaName, modelName);
+ Map<String, String> newProperties =
+ ImmutableMap.of("key1", "new value", "key2", "val2", "key3", "val3");
+
+ gravitinoCatalog.asModelCatalog().registerModel(modelIdent, null, null);
+
+ gravitinoCatalog
+ .asModelCatalog()
+ .linkModelVersion(modelIdent, "uri", aliases, "comment", properties);
+
+ ModelVersion modelVersion =
gravitinoCatalog.asModelCatalog().getModelVersion(modelIdent, 0);
+
+ Assertions.assertEquals(0, modelVersion.version());
+ Assertions.assertEquals("uri", modelVersion.uri());
+ Assertions.assertArrayEquals(aliases, modelVersion.aliases());
+ Assertions.assertEquals("comment", modelVersion.comment());
+ Assertions.assertEquals(properties, modelVersion.properties());
+
+ ModelVersionChange[] changes = {
+ ModelVersionChange.setProperty("key1", "new value"),
+ ModelVersionChange.setProperty("key3", "val3")
+ };
+ ModelVersion updatedModelVersion =
+ gravitinoCatalog.asModelCatalog().alterModelVersion(modelIdent, 0,
changes);
+
+ Assertions.assertEquals(modelVersion.version(),
updatedModelVersion.version());
+ Assertions.assertEquals(modelVersion.uri(), updatedModelVersion.uri());
+ Assertions.assertArrayEquals(modelVersion.aliases(),
updatedModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
updatedModelVersion.comment());
+ Assertions.assertEquals(newProperties, updatedModelVersion.properties());
+ }
+
+ @Test
+ void testLinkAndSetModelVersionPropertiesByAlias() {
+ String modelName = RandomNameUtils.genRandomName("model1");
+ String[] aliases = {"alias1"};
+ Map<String, String> properties = ImmutableMap.of("key1", "val1", "key2",
"val2");
+ NameIdentifier modelIdent = NameIdentifier.of(schemaName, modelName);
+ Map<String, String> newProperties =
+ ImmutableMap.of("key1", "new value", "key2", "val2", "key3", "val3");
+
+ gravitinoCatalog.asModelCatalog().registerModel(modelIdent, null, null);
+
+ gravitinoCatalog
+ .asModelCatalog()
+ .linkModelVersion(modelIdent, "uri", aliases, "comment", properties);
+
+ ModelVersion modelVersion =
+ gravitinoCatalog.asModelCatalog().getModelVersion(modelIdent,
aliases[0]);
+
+ Assertions.assertEquals(0, modelVersion.version());
+ Assertions.assertEquals("uri", modelVersion.uri());
+ Assertions.assertArrayEquals(aliases, modelVersion.aliases());
+ Assertions.assertEquals("comment", modelVersion.comment());
+ Assertions.assertEquals(properties, modelVersion.properties());
+
+ ModelVersionChange[] changes = {
+ ModelVersionChange.setProperty("key1", "new value"),
+ ModelVersionChange.setProperty("key3", "val3")
+ };
+ ModelVersion updatedModelVersion =
+ gravitinoCatalog.asModelCatalog().alterModelVersion(modelIdent,
aliases[0], changes);
+
+ Assertions.assertEquals(modelVersion.version(),
updatedModelVersion.version());
+ Assertions.assertEquals(modelVersion.uri(), updatedModelVersion.uri());
+ Assertions.assertArrayEquals(modelVersion.aliases(),
updatedModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
updatedModelVersion.comment());
+ Assertions.assertEquals(newProperties, updatedModelVersion.properties());
+ }
+
+ @Test
+ void testLinkAndRemoveModelVersionProperties() {
+ String modelName = RandomNameUtils.genRandomName("model1");
+ String[] aliases = {"alias1"};
+ Map<String, String> properties = ImmutableMap.of("key1", "val1", "key2",
"val2");
+ NameIdentifier modelIdent = NameIdentifier.of(schemaName, modelName);
+ Map<String, String> newProperties = ImmutableMap.of("key2", "val2");
+
+ gravitinoCatalog.asModelCatalog().registerModel(modelIdent, null, null);
+
+ gravitinoCatalog
+ .asModelCatalog()
+ .linkModelVersion(modelIdent, "uri", aliases, "comment", properties);
+
+ ModelVersion modelVersion =
gravitinoCatalog.asModelCatalog().getModelVersion(modelIdent, 0);
+
+ Assertions.assertEquals(0, modelVersion.version());
+ Assertions.assertEquals("uri", modelVersion.uri());
+ Assertions.assertArrayEquals(aliases, modelVersion.aliases());
+ Assertions.assertEquals("comment", modelVersion.comment());
+ Assertions.assertEquals(properties, modelVersion.properties());
+
+ ModelVersionChange change = ModelVersionChange.removeProperty("key1");
+ ModelVersion updatedModelVersion =
+ gravitinoCatalog.asModelCatalog().alterModelVersion(modelIdent, 0,
change);
+
+ Assertions.assertEquals(modelVersion.version(),
updatedModelVersion.version());
+ Assertions.assertEquals(modelVersion.uri(), updatedModelVersion.uri());
+ Assertions.assertArrayEquals(modelVersion.aliases(),
updatedModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
updatedModelVersion.comment());
+ Assertions.assertEquals(newProperties, updatedModelVersion.properties());
+ }
+
+ @Test
+ void testLinkAndRemoveModelVersionPropertiesByAlias() {
+ String modelName = RandomNameUtils.genRandomName("model1");
+ String[] aliases = {"alias1"};
+ Map<String, String> properties = ImmutableMap.of("key1", "val1", "key2",
"val2");
+ NameIdentifier modelIdent = NameIdentifier.of(schemaName, modelName);
+ Map<String, String> newProperties = ImmutableMap.of("key2", "val2");
+
+ gravitinoCatalog.asModelCatalog().registerModel(modelIdent, null, null);
+
+ gravitinoCatalog
+ .asModelCatalog()
+ .linkModelVersion(modelIdent, "uri", aliases, "comment", properties);
+
+ ModelVersion modelVersion =
+ gravitinoCatalog.asModelCatalog().getModelVersion(modelIdent,
aliases[0]);
+
+ Assertions.assertEquals(0, modelVersion.version());
+ Assertions.assertEquals("uri", modelVersion.uri());
+ Assertions.assertArrayEquals(aliases, modelVersion.aliases());
+ Assertions.assertEquals("comment", modelVersion.comment());
+ Assertions.assertEquals(properties, modelVersion.properties());
+
+ ModelVersionChange change = ModelVersionChange.removeProperty("key1");
+ ModelVersion updatedModelVersion =
+ gravitinoCatalog.asModelCatalog().alterModelVersion(modelIdent,
aliases[0], change);
+
+ Assertions.assertEquals(modelVersion.version(),
updatedModelVersion.version());
+ Assertions.assertEquals(modelVersion.uri(), updatedModelVersion.uri());
+ Assertions.assertArrayEquals(modelVersion.aliases(),
updatedModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
updatedModelVersion.comment());
+ Assertions.assertEquals(newProperties, updatedModelVersion.properties());
+ }
+
private void createMetalake() {
GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
Assertions.assertEquals(0, gravitinoMetalakes.length);
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
index cc36dd66b7..2f937dd4be 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
@@ -83,6 +83,7 @@ public class ErrorMessages {
public static final String UNKNOWN_GROUP = "Unknown group.";
public static final String UNKNOWN_METALAKE = "Unknown metalake name.";
public static final String UNKNOWN_MODEL = "Unknown model name.";
+ public static final String UNKNOWN_MODEL_VERSION = "Unknown model version.";
public static final String UNKNOWN_PRIVILEGE = "Unknown privilege";
public static final String UNKNOWN_ROLE = "Unknown role.";
public static final String UNKNOWN_SCHEMA = "Unknown schema name.";
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/ModelCommandHandler.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/ModelCommandHandler.java
index 28cb743186..ec58f98aa6 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/ModelCommandHandler.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/ModelCommandHandler.java
@@ -185,14 +185,8 @@ public class ModelCommandHandler extends CommandHandler {
&& line.hasOption(GravitinoOptions.COMMENT)
&& (line.hasOption(GravitinoOptions.ALIAS) ||
line.hasOption(GravitinoOptions.VERSION))) {
String comment = line.getOptionValue(GravitinoOptions.COMMENT);
- Integer version =
- line.hasOption(GravitinoOptions.VERSION)
- ? Integer.parseInt(line.getOptionValue(GravitinoOptions.VERSION))
- : null;
- String alias =
- line.hasOption(GravitinoOptions.ALIAS)
- ? getOneAlias(line.getOptionValues(GravitinoOptions.ALIAS))
- : null;
+ Integer version = getVersionFromLine(line);
+ String alias = getAliasFromLine(line);
gravitinoCommandLine
.newUpdateModelVersionComment(
@@ -211,18 +205,42 @@ public class ModelCommandHandler extends CommandHandler {
private void handleSetCommand() {
String property = line.getOptionValue(GravitinoOptions.PROPERTY);
String value = line.getOptionValue(GravitinoOptions.VALUE);
- gravitinoCommandLine
- .newSetModelProperty(context, metalake, catalog, schema, model,
property, value)
- .validate()
- .handle();
+
+ if (line.hasOption(GravitinoOptions.ALIAS) ||
line.hasOption(GravitinoOptions.VERSION)) {
+ Integer version = getVersionFromLine(line);
+ String alias = getAliasFromLine(line);
+ gravitinoCommandLine
+ .newSetModelVersionProperty(
+ context, metalake, catalog, schema, model, version, alias,
property, value)
+ .validate()
+ .handle();
+ } else {
+ gravitinoCommandLine
+ .newSetModelProperty(context, metalake, catalog, schema, model,
property, value)
+ .validate()
+ .handle();
+ }
}
private void handleRemoveCommand() {
String property = line.getOptionValue(GravitinoOptions.PROPERTY);
- gravitinoCommandLine
- .newRemoveModelProperty(context, metalake, catalog, schema, model,
property)
- .validate()
- .handle();
+
+ if (line.hasOption(GravitinoOptions.ALIAS) ||
line.hasOption(GravitinoOptions.VERSION)) {
+ Integer version = getVersionFromLine(line);
+ String alias = getAliasFromLine(line);
+
+ gravitinoCommandLine
+ .newRemoveModelVersionProperty(
+ context, metalake, catalog, schema, model, version, alias,
property)
+ .validate()
+ .handle();
+ } else {
+
+ gravitinoCommandLine
+ .newRemoveModelProperty(context, metalake, catalog, schema, model,
property)
+ .validate()
+ .handle();
+ }
}
private String getOneAlias(String[] aliases) {
@@ -232,4 +250,16 @@ public class ModelCommandHandler extends CommandHandler {
}
return aliases[0];
}
+
+ private Integer getVersionFromLine(CommandLine line) {
+ return line.hasOption(GravitinoOptions.VERSION)
+ ? Integer.parseInt(line.getOptionValue(GravitinoOptions.VERSION))
+ : null;
+ }
+
+ private String getAliasFromLine(CommandLine line) {
+ return line.hasOption(GravitinoOptions.ALIAS)
+ ? getOneAlias(line.getOptionValues(GravitinoOptions.ALIAS))
+ : null;
+ }
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
index 86acd5b0de..4c25781865 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
@@ -90,6 +90,7 @@ import
org.apache.gravitino.cli.commands.RemoveCatalogProperty;
import org.apache.gravitino.cli.commands.RemoveFilesetProperty;
import org.apache.gravitino.cli.commands.RemoveMetalakeProperty;
import org.apache.gravitino.cli.commands.RemoveModelProperty;
+import org.apache.gravitino.cli.commands.RemoveModelVersionProperty;
import org.apache.gravitino.cli.commands.RemoveRoleFromGroup;
import org.apache.gravitino.cli.commands.RemoveRoleFromUser;
import org.apache.gravitino.cli.commands.RemoveSchemaProperty;
@@ -107,6 +108,7 @@ import org.apache.gravitino.cli.commands.SetCatalogProperty;
import org.apache.gravitino.cli.commands.SetFilesetProperty;
import org.apache.gravitino.cli.commands.SetMetalakeProperty;
import org.apache.gravitino.cli.commands.SetModelProperty;
+import org.apache.gravitino.cli.commands.SetModelVersionProperty;
import org.apache.gravitino.cli.commands.SetOwner;
import org.apache.gravitino.cli.commands.SetSchemaProperty;
import org.apache.gravitino.cli.commands.SetTableProperty;
@@ -910,6 +912,33 @@ public class TestableCommandLine {
return new SetModelProperty(context, metalake, catalog, schema, model,
property, value);
}
+ protected RemoveModelVersionProperty newRemoveModelVersionProperty(
+ CommandContext context,
+ String metalake,
+ String catalog,
+ String schema,
+ String model,
+ Integer version,
+ String alias,
+ String property) {
+ return new RemoveModelVersionProperty(
+ context, metalake, catalog, schema, model, version, alias, property);
+ }
+
+ protected SetModelVersionProperty newSetModelVersionProperty(
+ CommandContext context,
+ String metalake,
+ String catalog,
+ String schema,
+ String model,
+ Integer version,
+ String alias,
+ String property,
+ String value) {
+ return new SetModelVersionProperty(
+ context, metalake, catalog, schema, model, version, alias, property,
value);
+ }
+
protected RemoveModelProperty newRemoveModelProperty(
CommandContext context,
String metalake,
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveModelVersionProperty.java
similarity index 59%
copy from
clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
copy to
clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveModelVersionProperty.java
index 451e1f71c3..864e36f025 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveModelVersionProperty.java
@@ -26,55 +26,64 @@ import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.exceptions.NoSuchCatalogException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchModelException;
+import org.apache.gravitino.exceptions.NoSuchModelVersionException;
import org.apache.gravitino.exceptions.NoSuchSchemaException;
-import org.apache.gravitino.model.ModelChange;
-
-/** Set a property of a model. */
-public class SetModelProperty extends Command {
+import org.apache.gravitino.model.ModelVersionChange;
+/** Remove a property from a model version. */
+public class RemoveModelVersionProperty extends Command {
private final String metalake;
private final String catalog;
private final String schema;
private final String model;
+ private final Integer version;
+ private final String alias;
private final String property;
- private final String value;
/**
- * Construct a new {@link SetModelProperty} instance.
+ * Constructs a new {@link RemoveModelVersionProperty} instance.
*
* @param context The command context.
- * @param metalake The name of the metalake.
- * @param catalog The name of the catalog.
- * @param schema The name of the schema.
- * @param model The name of the model.
- * @param property The name of the property to set.
- * @param value The value to set the property to.
+ * @param metalake The metalake name.
+ * @param catalog The catalog name.
+ * @param schema The schema name.
+ * @param model The model name.
+ * @param version The model version, if specified, otherwise null.
+ * @param alias The alias name of the model version, if specified, otherwise
null.
+ * @param property The property name to remove.
*/
- public SetModelProperty(
+ public RemoveModelVersionProperty(
CommandContext context,
String metalake,
String catalog,
String schema,
String model,
- String property,
- String value) {
+ Integer version,
+ String alias,
+ String property) {
super(context);
this.metalake = metalake;
this.catalog = catalog;
this.schema = schema;
this.model = model;
+ this.version = version;
+ this.alias = alias;
this.property = property;
- this.value = value;
}
- /** Set a property of a model. */
+ /** Remove a property from a model version. */
@Override
public void handle() {
try {
NameIdentifier name = NameIdentifier.of(schema, model);
GravitinoClient client = buildClient(metalake);
- ModelChange change = ModelChange.setProperty(property, value);
- client.loadCatalog(catalog).asModelCatalog().alterModel(name, change);
+ ModelVersionChange change = ModelVersionChange.removeProperty(property);
+
+ if (version != null) {
+ client.loadCatalog(catalog).asModelCatalog().alterModelVersion(name,
version, change);
+ } else {
+ client.loadCatalog(catalog).asModelCatalog().alterModelVersion(name,
alias, change);
+ }
} catch (NoSuchMetalakeException err) {
exitWithError(ErrorMessages.UNKNOWN_METALAKE);
} catch (NoSuchCatalogException err) {
@@ -82,18 +91,25 @@ public class SetModelProperty extends Command {
} catch (NoSuchSchemaException err) {
exitWithError(ErrorMessages.UNKNOWN_SCHEMA);
} catch (NoSuchModelException err) {
- exitWithError(ErrorMessages.UNKNOWN_TABLE);
+ exitWithError(ErrorMessages.UNKNOWN_MODEL);
+ } catch (NoSuchModelVersionException err) {
+ exitWithError(ErrorMessages.UNKNOWN_MODEL_VERSION);
} catch (Exception exp) {
exitWithError(exp.getMessage());
}
- printInformation(model + " property set.");
+ if (alias != null) {
+ printInformation(model + " version " + alias + "property " + property +
" property removed.");
+ } else {
+ printInformation(
+ model + " version " + version + "property " + property + " property
removed.");
+ }
}
/** {@inheritDoc} */
@Override
public Command validate() {
- validatePropertyAndValue(property, value);
+ validateProperty(property);
return super.validate();
}
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
index 451e1f71c3..10f6f692f2 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
@@ -26,6 +26,7 @@ import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.exceptions.NoSuchCatalogException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchModelException;
+import org.apache.gravitino.exceptions.NoSuchModelVersionException;
import org.apache.gravitino.exceptions.NoSuchSchemaException;
import org.apache.gravitino.model.ModelChange;
@@ -82,7 +83,9 @@ public class SetModelProperty extends Command {
} catch (NoSuchSchemaException err) {
exitWithError(ErrorMessages.UNKNOWN_SCHEMA);
} catch (NoSuchModelException err) {
- exitWithError(ErrorMessages.UNKNOWN_TABLE);
+ exitWithError(ErrorMessages.UNKNOWN_MODEL);
+ } catch (NoSuchModelVersionException err) {
+ exitWithError(ErrorMessages.UNKNOWN_MODEL_VERSION);
} catch (Exception exp) {
exitWithError(exp.getMessage());
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelVersionProperty.java
similarity index 62%
copy from
clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
copy to
clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelVersionProperty.java
index 451e1f71c3..568ebeee68 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelProperty.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/SetModelVersionProperty.java
@@ -26,36 +26,42 @@ import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.exceptions.NoSuchCatalogException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchModelException;
+import org.apache.gravitino.exceptions.NoSuchModelVersionException;
import org.apache.gravitino.exceptions.NoSuchSchemaException;
-import org.apache.gravitino.model.ModelChange;
-
-/** Set a property of a model. */
-public class SetModelProperty extends Command {
+import org.apache.gravitino.model.ModelVersionChange;
+/** Set or update a property of a model version. */
+public class SetModelVersionProperty extends Command {
private final String metalake;
private final String catalog;
private final String schema;
private final String model;
+ private final Integer version;
+ private final String alias;
private final String property;
private final String value;
/**
- * Construct a new {@link SetModelProperty} instance.
+ * Constructs a new {@link SetModelVersionProperty} instance.
*
* @param context The command context.
- * @param metalake The name of the metalake.
- * @param catalog The name of the catalog.
- * @param schema The name of the schema.
- * @param model The name of the model.
- * @param property The name of the property to set.
- * @param value The value to set the property to.
+ * @param metalake The metalake name.
+ * @param catalog The catalog name.
+ * @param schema The schema name.
+ * @param model The model name.
+ * @param version The model version, if specified, otherwise null.
+ * @param alias The model alias, if specified, otherwise null.
+ * @param property The property name.
+ * @param value The property value.
*/
- public SetModelProperty(
+ public SetModelVersionProperty(
CommandContext context,
String metalake,
String catalog,
String schema,
String model,
+ Integer version,
+ String alias,
String property,
String value) {
super(context);
@@ -63,18 +69,24 @@ public class SetModelProperty extends Command {
this.catalog = catalog;
this.schema = schema;
this.model = model;
+ this.version = version;
+ this.alias = alias;
this.property = property;
this.value = value;
}
- /** Set a property of a model. */
+ /** Set or update a property of a model version. */
@Override
public void handle() {
try {
NameIdentifier name = NameIdentifier.of(schema, model);
GravitinoClient client = buildClient(metalake);
- ModelChange change = ModelChange.setProperty(property, value);
- client.loadCatalog(catalog).asModelCatalog().alterModel(name, change);
+ ModelVersionChange change = ModelVersionChange.setProperty(property,
value);
+ if (version != null) {
+ client.loadCatalog(catalog).asModelCatalog().alterModelVersion(name,
version, change);
+ } else {
+ client.loadCatalog(catalog).asModelCatalog().alterModelVersion(name,
alias, change);
+ }
} catch (NoSuchMetalakeException err) {
exitWithError(ErrorMessages.UNKNOWN_METALAKE);
} catch (NoSuchCatalogException err) {
@@ -82,12 +94,18 @@ public class SetModelProperty extends Command {
} catch (NoSuchSchemaException err) {
exitWithError(ErrorMessages.UNKNOWN_SCHEMA);
} catch (NoSuchModelException err) {
- exitWithError(ErrorMessages.UNKNOWN_TABLE);
+ exitWithError(ErrorMessages.UNKNOWN_MODEL);
+ } catch (NoSuchModelVersionException err) {
+ exitWithError(ErrorMessages.UNKNOWN_MODEL_VERSION);
} catch (Exception exp) {
exitWithError(exp.getMessage());
}
- printInformation(model + " property set.");
+ if (alias != null) {
+ printInformation(model + " version " + alias + " property set.");
+ } else {
+ printInformation(model + " version " + version + " property set.");
+ }
}
/** {@inheritDoc} */
diff --git
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestModelCommands.java
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestModelCommands.java
index 35b9f62698..e4014e1c1b 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestModelCommands.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestModelCommands.java
@@ -47,7 +47,9 @@ import org.apache.gravitino.cli.commands.ModelAudit;
import org.apache.gravitino.cli.commands.ModelDetails;
import org.apache.gravitino.cli.commands.RegisterModel;
import org.apache.gravitino.cli.commands.RemoveModelProperty;
+import org.apache.gravitino.cli.commands.RemoveModelVersionProperty;
import org.apache.gravitino.cli.commands.SetModelProperty;
+import org.apache.gravitino.cli.commands.SetModelVersionProperty;
import org.apache.gravitino.cli.commands.UpdateModelName;
import org.apache.gravitino.cli.commands.UpdateModelVersionComment;
import org.junit.jupiter.api.AfterEach;
@@ -734,7 +736,6 @@ public class TestModelCommands {
@Test
void testUpdateModelVersionCommentCommandByAliasAndVersion() {
- Main.useExit = false;
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.ALIAS)).thenReturn(true);
@@ -752,4 +753,186 @@ public class TestModelCommands {
Assertions.assertThrows(RuntimeException.class,
commandLine::handleCommandLine);
}
+
+ @Test
+ void testSetModelVersionProperty() {
+ SetModelVersionProperty mockSetProperty =
mock(SetModelVersionProperty.class);
+
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+ when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema.model");
+ when(mockCommandLine.hasOption(GravitinoOptions.VERSION)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.VERSION)).thenReturn("1");
+
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn("key");
+ when(mockCommandLine.hasOption(GravitinoOptions.VALUE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.VALUE)).thenReturn("value");
+
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.MODEL,
CommandActions.SET));
+
+ doReturn(mockSetProperty).when(mockSetProperty).validate();
+ doReturn(mockSetProperty)
+ .when(commandLine)
+ .newSetModelVersionProperty(
+ any(CommandContext.class),
+ eq("metalake_demo"),
+ eq("catalog"),
+ eq("schema"),
+ eq("model"),
+ any(),
+ any(),
+ eq("key"),
+ eq("value"));
+ commandLine.handleCommandLine();
+ verify(mockSetProperty).handle();
+ }
+
+ @Test
+ void testSetModelVersionPropertyByAlias() {
+ SetModelVersionProperty mockSetProperty =
mock(SetModelVersionProperty.class);
+
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+ when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema.model");
+ when(mockCommandLine.hasOption(GravitinoOptions.ALIAS)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.ALIAS))
+ .thenReturn(new String[] {"aliasA"});
+
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn("key");
+ when(mockCommandLine.hasOption(GravitinoOptions.VALUE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.VALUE)).thenReturn("value");
+
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.MODEL,
CommandActions.SET));
+
+ doReturn(mockSetProperty).when(mockSetProperty).validate();
+ doReturn(mockSetProperty)
+ .when(commandLine)
+ .newSetModelVersionProperty(
+ any(CommandContext.class),
+ eq("metalake_demo"),
+ eq("catalog"),
+ eq("schema"),
+ eq("model"),
+ any(),
+ any(),
+ eq("key"),
+ eq("value"));
+ commandLine.handleCommandLine();
+ verify(mockSetProperty).handle();
+ }
+
+ @Test
+ void testSetModelVersionPropertyByAliasAndVersion() {
+
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+ when(mockCommandLine.hasOption(GravitinoOptions.ALIAS)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.ALIAS))
+ .thenReturn(new String[] {"aliasA"});
+ when(mockCommandLine.hasOption(GravitinoOptions.VERSION)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.VERSION)).thenReturn("1");
+
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn("key");
+ when(mockCommandLine.hasOption(GravitinoOptions.VALUE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.VALUE)).thenReturn("value");
+
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.MODEL,
CommandActions.SET));
+
+ Assertions.assertThrows(RuntimeException.class,
commandLine::handleCommandLine);
+ }
+
+ @Test
+ void testRemoveModelVersionProperty() {
+ RemoveModelVersionProperty mockRemoveProperty =
mock(RemoveModelVersionProperty.class);
+
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+ when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema.model");
+ when(mockCommandLine.hasOption(GravitinoOptions.VERSION)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.VERSION)).thenReturn("1");
+
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn("key");
+
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.MODEL,
CommandActions.REMOVE));
+
+ doReturn(mockRemoveProperty).when(mockRemoveProperty).validate();
+ doReturn(mockRemoveProperty)
+ .when(commandLine)
+ .newRemoveModelVersionProperty(
+ any(CommandContext.class),
+ eq("metalake_demo"),
+ eq("catalog"),
+ eq("schema"),
+ eq("model"),
+ any(),
+ any(),
+ eq("key"));
+ commandLine.handleCommandLine();
+ verify(mockRemoveProperty).handle();
+ }
+
+ @Test
+ void testRemoveModelVersionPropertyByAlias() {
+ RemoveModelVersionProperty mockRemoveProperty =
mock(RemoveModelVersionProperty.class);
+
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+ when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema.model");
+ when(mockCommandLine.hasOption(GravitinoOptions.ALIAS)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.ALIAS))
+ .thenReturn(new String[] {"aliasA"});
+
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn("key");
+
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.MODEL,
CommandActions.REMOVE));
+
+ doReturn(mockRemoveProperty).when(mockRemoveProperty).validate();
+ doReturn(mockRemoveProperty)
+ .when(commandLine)
+ .newRemoveModelVersionProperty(
+ any(CommandContext.class),
+ eq("metalake_demo"),
+ eq("catalog"),
+ eq("schema"),
+ eq("model"),
+ any(),
+ any(),
+ eq("key"));
+ commandLine.handleCommandLine();
+ verify(mockRemoveProperty).handle();
+ }
+
+ @Test
+ void testRemoveModelVersionPropertyByAliasAndVersion() {
+
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+ when(mockCommandLine.hasOption(GravitinoOptions.ALIAS)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.ALIAS))
+ .thenReturn(new String[] {"aliasA"});
+ when(mockCommandLine.hasOption(GravitinoOptions.VERSION)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.VERSION)).thenReturn("1");
+
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn("key");
+
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.MODEL,
CommandActions.REMOVE));
+
+ Assertions.assertThrows(RuntimeException.class,
commandLine::handleCommandLine);
+ }
}
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/DTOConverters.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/DTOConverters.java
index ceffd38a94..48ddd9df6e 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/DTOConverters.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/DTOConverters.java
@@ -391,6 +391,15 @@ class DTOConverters {
return new ModelVersionUpdateRequest.UpdateModelVersionComment(
((ModelVersionChange.UpdateComment) change).newComment());
+ } else if (change instanceof ModelVersionChange.SetProperty) {
+ return new ModelVersionUpdateRequest.SetModelVersionPropertyRequest(
+ ((ModelVersionChange.SetProperty) change).property(),
+ ((ModelVersionChange.SetProperty) change).value());
+
+ } else if (change instanceof ModelVersionChange.RemoveProperty) {
+ return new ModelVersionUpdateRequest.RemoveModelVersionPropertyRequest(
+ ((ModelVersionChange.RemoveProperty) change).property());
+
} else {
throw new IllegalArgumentException(
"Unknown model version change type: " +
change.getClass().getSimpleName());
diff --git a/clients/client-python/gravitino/api/model_version_change.py
b/clients/client-python/gravitino/api/model_version_change.py
index 0c555e983b..56550df874 100644
--- a/clients/client-python/gravitino/api/model_version_change.py
+++ b/clients/client-python/gravitino/api/model_version_change.py
@@ -34,6 +34,27 @@ class ModelVersionChange(ABC):
"""
return ModelVersionChange.UpdateComment(comment)
+ @staticmethod
+ def set_property(key, value):
+ """Creates a new model version change to set a property and value pair
for the model version.
+ Args:
+ key: The key of the property.
+ value: The value of the property.
+ Returns:
+ The model version change.
+ """
+ return ModelVersionChange.SetProperty(key, value)
+
+ @staticmethod
+ def remove_property(key: str):
+ """Creates a new model version change to remove a property from the
model version.
+ Args:
+ key: The key of the property.
+ Returns:
+ The model version change.
+ """
+ return ModelVersionChange.RemoveProperty(key)
+
class UpdateComment:
"""A model version change to update the comment of the model
version."""
@@ -76,3 +97,94 @@ class ModelVersionChange(ABC):
A string summary of this comment update operation.
"""
return f"UpdateComment {self._new_comment}"
+
+ class SetProperty:
+ """A model version change to set a property and value pair for the
model version."""
+
+ def __init__(self, key: str, value: str):
+ self._key = key
+ self._value = value
+
+ def property(self) -> str:
+ """Retrieves the name of the property.
+ Returns:
+ The name of the property.
+ """
+ return self._key
+
+ def value(self) -> str:
+ """Retrieves the value of the property.
+ Returns:
+ The value of the property.
+ """
+ return self._value
+
+ def __eq__(self, other):
+ """Compares this SetProperty instance with another object for
equality. Two instances are
+ considered equal if they designate the same key and value pair for
the model version.
+ Args:
+ other: The object to compare with this instance.
+ Returns:
+ true if the given object represents an identical model version
property set operation; false otherwise.
+ """
+ if not isinstance(other, ModelVersionChange.SetProperty):
+ return False
+ return self.key() == other.key() and self.value() == other.value()
+
+ def __hash__(self):
+ """Generates a hash code for this SetProperty instance. The hash
code is primarily based on
+ the key and value pair for the model version.
+ Returns:
+ A hash code value for this property set operation.
+ """
+ return hash((self.key(), self.value()))
+
+ def __str__(self):
+ """Provides a string representation of the SetProperty instance.
This string includes the
+ class name followed by the key and value pair for the model
version.
+ Returns:
+ A string summary of this property set operation.
+ """
+ return f"SetProperty {self.key()}={self.value()}"
+
+ class RemoveProperty:
+ """A model version change to remove a property from the model
version."""
+
+ def __init__(self, key: str):
+ self._key = key
+
+ def property(self) -> str:
+ """Retrieves the name of the property.
+ Returns:
+ The name of the property.
+ """
+ return self._key
+
+ def __eq__(self, other):
+ """Compares this RemoveProperty instance with another object for
equality. Two instances are
+ considered equal if they designate the same key for the model
version.
+ Args:
+ other: The object to compare with this instance.
+ Returns:
+ true if the given object represents an identical model version
property remove operation;
+ false otherwise.
+ """
+ if not isinstance(other, ModelVersionChange.RemoveProperty):
+ return False
+ return self.key() == other.key()
+
+ def __hash__(self):
+ """Generates a hash code for this RemoveProperty instance. The
hash code is primarily based on
+ the key for the model version.
+ Returns:
+ A hash code value for this property remove operation.
+ """
+ return hash(self.key())
+
+ def __str__(self):
+ """Provides a string representation of the RemoveProperty
instance. This string includes the
+ class name followed by the key for the model version.
+ Returns:
+ A string summary of this property remove operation.
+ """
+ return f"RemoveProperty {self.key()}"
diff --git a/clients/client-python/gravitino/client/generic_model_catalog.py
b/clients/client-python/gravitino/client/generic_model_catalog.py
index 554b788e54..b304c5115d 100644
--- a/clients/client-python/gravitino/client/generic_model_catalog.py
+++ b/clients/client-python/gravitino/client/generic_model_catalog.py
@@ -532,8 +532,15 @@ class GenericModelCatalog(BaseSchemaCatalog):
change.new_comment()
)
- if isinstance(change, ModelVersionChange.RemoveComment):
- return ModelVersionUpdateRequest.RemoveModelVersionComment()
+ if isinstance(change, ModelVersionChange.SetProperty):
+ return ModelVersionUpdateRequest.SetModelVersionPropertyRequest(
+ change.property(), change.value()
+ )
+
+ if isinstance(change, ModelVersionChange.RemoveProperty):
+ return ModelVersionUpdateRequest.RemoveModelVersionPropertyRequest(
+ change.property()
+ )
raise ValueError(f"Unknown change type: {type(change).__name__}")
diff --git
a/clients/client-python/gravitino/dto/requests/model_version_update_request.py
b/clients/client-python/gravitino/dto/requests/model_version_update_request.py
index a9a5b9c1c4..948f92359b 100644
---
a/clients/client-python/gravitino/dto/requests/model_version_update_request.py
+++
b/clients/client-python/gravitino/dto/requests/model_version_update_request.py
@@ -61,3 +61,49 @@ class ModelVersionUpdateRequest:
def model_version_change(self):
return ModelVersionChange.update_comment(self._new_comment)
+
+ @dataclass
+ class SetModelVersionPropertyRequest(ModelVersionUpdateRequestBase):
+ """Request to update model version properties"""
+
+ _property: Optional[str] =
field(metadata=config(field_name="property"))
+ _value: Optional[str] = field(metadata=config(field_name="value"))
+
+ def __init__(self, pro: str, value: str):
+ super().__init__("setProperty")
+ self._property = pro
+ self._value = value
+
+ def validate(self):
+ if not self._property:
+ raise ValueError('"property" field is required')
+ if not self._value:
+ raise ValueError('"value" field is required')
+
+ def model_version_change(self) -> ModelVersionChange:
+ return ModelVersionChange.set_property(self._property, self._value)
+
+ @dataclass
+ class RemoveModelVersionPropertyRequest(ModelVersionUpdateRequestBase):
+ """Request to remove model version properties"""
+
+ _property: Optional[str] =
field(metadata=config(field_name="property"))
+
+ def __init__(self, pro: str):
+ super().__init__("removeProperty")
+ self._property = pro
+
+ def key(self):
+ """
+ Returns the key of the property to remove.
+ Returns:
+ str: The key of the property to remove.
+ """
+ return self._property
+
+ def validate(self):
+ if not self._property:
+ raise ValueError('"property" field is required')
+
+ def model_version_change(self):
+ return ModelVersionChange.remove_property(self._property)
diff --git a/clients/client-python/tests/integration/test_model_catalog.py
b/clients/client-python/tests/integration/test_model_catalog.py
index 85211ccd65..54392ae9f3 100644
--- a/clients/client-python/tests/integration/test_model_catalog.py
+++ b/clients/client-python/tests/integration/test_model_catalog.py
@@ -328,6 +328,53 @@ class TestModelCatalog(IntegrationTestEnv):
self.assertEqual({"k1": "v1", "k2": "v2"},
updated_model_version.properties())
self.assertEqual("uri", updated_model_version.uri())
+ def test_link_update_model_version_property(self):
+ model_name = f"model_it_model{str(randint(0, 1000))}"
+ model_ident = NameIdentifier.of(self._schema_name, model_name)
+ aliases = ["alias1", "alias2"]
+ comment = "comment"
+ properties = {"k1": "v1", "k2": "v2"}
+ self._catalog.as_model_catalog().register_model(
+ model_ident, comment, properties
+ )
+
+ self._catalog.as_model_catalog().link_model_version(
+ model_ident,
+ uri="uri",
+ aliases=aliases,
+ comment="comment",
+ properties={"k1": "v1", "k2": "v2"},
+ )
+
+ original_model_version =
self._catalog.as_model_catalog().get_model_version(
+ model_ident, 0
+ )
+
+ self.assertEqual(0, original_model_version.version())
+ self.assertEqual("uri", original_model_version.uri())
+ self.assertEqual(["alias1", "alias2"],
original_model_version.aliases())
+ self.assertEqual("comment", original_model_version.comment())
+ self.assertEqual({"k1": "v1", "k2": "v2"},
original_model_version.properties())
+
+ changes = [
+ ModelVersionChange.set_property("k1", "v11"),
+ ModelVersionChange.set_property("k3", "v3"),
+ ModelVersionChange.remove_property("k2"),
+ ]
+
+ self._catalog.as_model_catalog().alter_model_version(model_ident, 0,
*changes)
+ update_property_model = (
+ self._catalog.as_model_catalog().get_model_version_by_alias(
+ model_ident, aliases[0]
+ )
+ )
+
+ self.assertEqual(update_property_model.version(), 0)
+ self.assertEqual(update_property_model.uri(), "uri")
+ self.assertEqual(update_property_model.comment(), comment)
+ self.assertEqual(update_property_model.aliases(), aliases)
+ self.assertEqual(update_property_model.properties(), {"k1": "v11",
"k3": "v3"})
+
def test_link_get_model_version(self):
model_name = "model_it_model" + str(randint(0, 1000))
model_ident = NameIdentifier.of(self._schema_name, model_name)
diff --git
a/common/src/main/java/org/apache/gravitino/dto/requests/ModelVersionUpdateRequest.java
b/common/src/main/java/org/apache/gravitino/dto/requests/ModelVersionUpdateRequest.java
index 1026bd82ae..364d0bc9c4 100644
---
a/common/src/main/java/org/apache/gravitino/dto/requests/ModelVersionUpdateRequest.java
+++
b/common/src/main/java/org/apache/gravitino/dto/requests/ModelVersionUpdateRequest.java
@@ -23,11 +23,13 @@ import
com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.google.common.base.Preconditions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
import org.apache.gravitino.model.ModelVersionChange;
import org.apache.gravitino.rest.RESTRequest;
@@ -37,7 +39,13 @@ import org.apache.gravitino.rest.RESTRequest;
@JsonSubTypes({
@JsonSubTypes.Type(
value = ModelVersionUpdateRequest.UpdateModelVersionComment.class,
- name = "updateComment")
+ name = "updateComment"),
+ @JsonSubTypes.Type(
+ value = ModelVersionUpdateRequest.SetModelVersionPropertyRequest.class,
+ name = "setProperty"),
+ @JsonSubTypes.Type(
+ value =
ModelVersionUpdateRequest.RemoveModelVersionPropertyRequest.class,
+ name = "removeProperty")
})
public interface ModelVersionUpdateRequest extends RESTRequest {
@@ -68,4 +76,64 @@ public interface ModelVersionUpdateRequest extends
RESTRequest {
@Override
public void validate() throws IllegalArgumentException {}
}
+
+ /** Request to set Properties of a model version */
+ @EqualsAndHashCode
+ @AllArgsConstructor
+ @NoArgsConstructor(force = true)
+ @ToString
+ @Getter
+ class SetModelVersionPropertyRequest implements ModelVersionUpdateRequest {
+ @JsonProperty("property")
+ private final String property;
+
+ @JsonProperty("value")
+ private final String value;
+
+ /** {@inheritDoc} */
+ @Override
+ public ModelVersionChange modelVersionChange() {
+ return ModelVersionChange.setProperty(property, value);
+ }
+
+ /**
+ * Validates the request, i.e., checks if the property and value are not
empty and not null.
+ *
+ * @throws IllegalArgumentException If the request is invalid, this
exception is thrown.
+ */
+ @Override
+ public void validate() throws IllegalArgumentException {
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(property), "\"property\" field is required
and cannot be empty");
+ Preconditions.checkArgument(value != null, "\"value\" field is required
and cannot be null");
+ }
+ }
+
+ /** Request to remove a property from a model version. */
+ @EqualsAndHashCode
+ @AllArgsConstructor
+ @NoArgsConstructor(force = true)
+ @ToString
+ @Getter
+ class RemoveModelVersionPropertyRequest implements ModelVersionUpdateRequest
{
+ @JsonProperty("property")
+ private final String property;
+
+ /** {@inheritDoc} */
+ @Override
+ public ModelVersionChange modelVersionChange() {
+ return ModelVersionChange.removeProperty(property);
+ }
+
+ /**
+ * Validates the request, i.e., checks if the property is not empty.
+ *
+ * @throws IllegalArgumentException If the request is invalid, this
exception is thrown.
+ */
+ @Override
+ public void validate() throws IllegalArgumentException {
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(property), "\"property\" field is required
and cannot be empty");
+ }
+ }
}
diff --git
a/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
b/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
index 115350dbcc..5e27a77ef1 100644
---
a/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
+++
b/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
@@ -398,6 +398,162 @@ public class TestModelOperationDispatcher extends
TestOperationDispatcher {
Assertions.assertEquals(modelVersion.properties(),
alteredModelVersion.properties());
}
+ @Test
+ void testUpdateAndAddModelVersionProperty() {
+ String schemaName = randomSchemaName();
+ String schemaComment = "schema which tests update";
+
+ String modelName = randomModelName();
+ String modelComment = "model which tests update";
+ Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
+ Map<String, String> newProps = ImmutableMap.of("k1", "new value", "k2",
"v2", "k3", "v3");
+
+ String versionUri = "s3://test-bucket/test-path/model.json";
+ String[] versionAliases = {"alias1", "alias2"};
+ String versionComment = "version which tests update";
+
+ NameIdentifier schemaIdent = NameIdentifier.of(metalake, catalog,
schemaName);
+ schemaOperationDispatcher.createSchema(schemaIdent, schemaComment, props);
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(metalake, catalog, schemaName, modelName);
+ modelOperationDispatcher.registerModel(modelIdent, modelComment, props);
+
+ modelOperationDispatcher.linkModelVersion(
+ modelIdent, versionUri, versionAliases, versionComment, props);
+
+ ModelVersionChange[] changes =
+ new ModelVersionChange[] {
+ ModelVersionChange.setProperty("k1", "new value"),
+ ModelVersionChange.setProperty("k3", "v3")
+ };
+ ModelVersion modelVersion =
modelOperationDispatcher.getModelVersion(modelIdent, 0);
+ ModelVersion alteredModelVersion =
+ modelOperationDispatcher.alterModelVersion(modelIdent, 0, changes);
+
+ Assertions.assertEquals(modelVersion.uri(), alteredModelVersion.uri());
+ Assertions.assertEquals(modelVersion.version(),
alteredModelVersion.version());
+ Assertions.assertEquals(modelVersion.aliases(),
alteredModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
alteredModelVersion.comment());
+ Assertions.assertEquals(newProps, alteredModelVersion.properties());
+ }
+
+ @Test
+ void testUpdateAndAddModelVersionPropertyByAlias() {
+ String schemaName = randomSchemaName();
+ String schemaComment = "schema which tests update";
+
+ String modelName = randomModelName();
+ String modelComment = "model which tests update";
+ Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
+ Map<String, String> newProps = ImmutableMap.of("k1", "new value", "k2",
"v2", "k3", "v3");
+
+ String versionUri = "s3://test-bucket/test-path/model.json";
+ String[] versionAliases = {"alias1", "alias2"};
+ String versionComment = "version which tests update";
+
+ NameIdentifier schemaIdent = NameIdentifier.of(metalake, catalog,
schemaName);
+ schemaOperationDispatcher.createSchema(schemaIdent, schemaComment, props);
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(metalake, catalog, schemaName, modelName);
+ modelOperationDispatcher.registerModel(modelIdent, modelComment, props);
+
+ modelOperationDispatcher.linkModelVersion(
+ modelIdent, versionUri, versionAliases, versionComment, props);
+
+ ModelVersionChange[] changes =
+ new ModelVersionChange[] {
+ ModelVersionChange.setProperty("k1", "new value"),
+ ModelVersionChange.setProperty("k3", "v3")
+ };
+ ModelVersion modelVersion =
+ modelOperationDispatcher.getModelVersion(modelIdent,
versionAliases[0]);
+ ModelVersion alteredModelVersion =
+ modelOperationDispatcher.alterModelVersion(modelIdent,
versionAliases[0], changes);
+
+ Assertions.assertEquals(modelVersion.uri(), alteredModelVersion.uri());
+ Assertions.assertEquals(modelVersion.version(),
alteredModelVersion.version());
+ Assertions.assertEquals(modelVersion.aliases(),
alteredModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
alteredModelVersion.comment());
+ Assertions.assertEquals(newProps, alteredModelVersion.properties());
+ }
+
+ @Test
+ void testRemoveModelVersionProperty() {
+ String schemaName = randomSchemaName();
+ String schemaComment = "schema which tests update";
+
+ String modelName = randomModelName();
+ String modelComment = "model which tests update";
+ Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
+ Map<String, String> newProps = ImmutableMap.of("k2", "v2");
+
+ String versionUri = "s3://test-bucket/test-path/model.json";
+ String[] versionAliases = {"alias1", "alias2"};
+ String versionComment = "version which tests update";
+
+ NameIdentifier schemaIdent = NameIdentifier.of(metalake, catalog,
schemaName);
+ schemaOperationDispatcher.createSchema(schemaIdent, schemaComment, props);
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(metalake, catalog, schemaName, modelName);
+ modelOperationDispatcher.registerModel(modelIdent, modelComment, props);
+
+ modelOperationDispatcher.linkModelVersion(
+ modelIdent, versionUri, versionAliases, versionComment, props);
+
+ ModelVersionChange change = ModelVersionChange.removeProperty("k1");
+
+ ModelVersion modelVersion =
modelOperationDispatcher.getModelVersion(modelIdent, 0);
+ ModelVersion alteredModelVersion =
+ modelOperationDispatcher.alterModelVersion(modelIdent, 0, change);
+
+ Assertions.assertEquals(modelVersion.uri(), alteredModelVersion.uri());
+ Assertions.assertEquals(modelVersion.version(),
alteredModelVersion.version());
+ Assertions.assertEquals(modelVersion.aliases(),
alteredModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
alteredModelVersion.comment());
+ Assertions.assertEquals(newProps, alteredModelVersion.properties());
+ }
+
+ @Test
+ void testRemoveModelVersionPropertyByAlias() {
+ String schemaName = randomSchemaName();
+ String schemaComment = "schema which tests update";
+
+ String modelName = randomModelName();
+ String modelComment = "model which tests update";
+ Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
+ Map<String, String> newProps = ImmutableMap.of("k2", "v2");
+
+ String versionUri = "s3://test-bucket/test-path/model.json";
+ String[] versionAliases = {"alias1", "alias2"};
+ String versionComment = "version which tests update";
+
+ NameIdentifier schemaIdent = NameIdentifier.of(metalake, catalog,
schemaName);
+ schemaOperationDispatcher.createSchema(schemaIdent, schemaComment, props);
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(metalake, catalog, schemaName, modelName);
+ modelOperationDispatcher.registerModel(modelIdent, modelComment, props);
+
+ modelOperationDispatcher.linkModelVersion(
+ modelIdent, versionUri, versionAliases, versionComment, props);
+
+ ModelVersionChange change = ModelVersionChange.removeProperty("k1");
+
+ ModelVersion modelVersion =
+ modelOperationDispatcher.getModelVersion(modelIdent,
versionAliases[0]);
+ ModelVersion alteredModelVersion =
+ modelOperationDispatcher.alterModelVersion(modelIdent,
versionAliases[0], change);
+
+ Assertions.assertEquals(modelVersion.uri(), alteredModelVersion.uri());
+ Assertions.assertEquals(modelVersion.version(),
alteredModelVersion.version());
+ Assertions.assertEquals(modelVersion.aliases(),
alteredModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
alteredModelVersion.comment());
+ Assertions.assertEquals(newProps, alteredModelVersion.properties());
+ }
+
private String randomSchemaName() {
return "schema_" + UUID.randomUUID().toString().replace("-", "");
}
diff --git
a/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
b/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
index 1588ba0e27..94dcd420f0 100644
---
a/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
+++
b/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
@@ -1046,6 +1046,15 @@ public class TestCatalogOperations
if (change instanceof ModelVersionChange.UpdateComment) {
newComment = ((ModelVersionChange.UpdateComment) change).newComment();
+ } else if (change instanceof ModelVersionChange.RemoveProperty) {
+ ModelVersionChange.RemoveProperty removeProperty =
+ (ModelVersionChange.RemoveProperty) change;
+ newProps.remove(removeProperty.property());
+
+ } else if (change instanceof ModelVersionChange.SetProperty) {
+ ModelVersionChange.SetProperty setProperty =
(ModelVersionChange.SetProperty) change;
+ newProps.put(setProperty.property(), setProperty.value());
+
} else {
throw new IllegalArgumentException("Unsupported model change: " +
change);
}
diff --git
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelVersionMetaService.java
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelVersionMetaService.java
index f1206f7959..4284537d76 100644
---
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelVersionMetaService.java
+++
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelVersionMetaService.java
@@ -548,6 +548,90 @@ public class TestModelVersionMetaService extends
TestJDBCBackend {
updateCommentUpdater));
}
+ @Test
+ void testAlterModelVersionProperties() throws IOException {
+ createParentEntities(METALAKE_NAME, CATALOG_NAME, SCHEMA_NAME, auditInfo);
+
+ Map<String, String> properties = ImmutableMap.of("k1", "v1", "k2", "v2");
+ String modelName = randomModelName();
+ String modelComment = "model1 comment";
+ String modelVersionUri = "S3://test/path/to/model/version";
+ List<String> modelVersionAliases = ImmutableList.of("alias1", "alias2");
+ String modelVersionComment = "test comment";
+ Map<String, String> updatedProperties =
+ ImmutableMap.of("k1", "new value", "k2", "v2", "k3", "v3");
+ int version = 0;
+
+ ModelEntity modelEntity =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ MODEL_NS,
+ modelName,
+ modelComment,
+ 0,
+ properties,
+ auditInfo);
+
+ ModelVersionEntity modelVersionEntity =
+ createModelVersionEntity(
+ modelEntity.nameIdentifier(),
+ version,
+ modelVersionUri,
+ modelVersionAliases,
+ modelVersionComment,
+ properties,
+ auditInfo);
+
+ ModelVersionEntity updatedModelVersionEntity =
+ createModelVersionEntity(
+ modelVersionEntity.modelIdentifier(),
+ modelVersionEntity.version(),
+ modelVersionEntity.uri(),
+ modelVersionEntity.aliases(),
+ modelVersionEntity.comment(),
+ updatedProperties,
+ modelVersionEntity.auditInfo());
+
+ Assertions.assertDoesNotThrow(
+ () -> ModelMetaService.getInstance().insertModel(modelEntity, false));
+
+ Assertions.assertDoesNotThrow(
+ () ->
ModelVersionMetaService.getInstance().insertModelVersion(modelVersionEntity));
+
+ Function<ModelVersionEntity, ModelVersionEntity> updatePropertiesUpdater =
+ oldModelVersionEntity -> updatedModelVersionEntity;
+
+ ModelVersionEntity alteredModelVersionEntity =
+ ModelVersionMetaService.getInstance()
+ .updateModelVersion(modelVersionEntity.nameIdentifier(),
updatePropertiesUpdater);
+
+ Assertions.assertEquals(updatedModelVersionEntity,
alteredModelVersionEntity);
+
+ // Test update a non-exist model
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () ->
+ ModelVersionMetaService.getInstance()
+ .updateModelVersion(
+ NameIdentifierUtil.ofModelVersion(
+ METALAKE_NAME,
+ CATALOG_NAME,
+ SCHEMA_NAME,
+ "non_exist_model",
+ "non_exist_version"),
+ updatePropertiesUpdater));
+
+ // Test update a non-exist model version
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () ->
+ ModelVersionMetaService.getInstance()
+ .updateModelVersion(
+ NameIdentifierUtil.ofModelVersion(
+ METALAKE_NAME, CATALOG_NAME, SCHEMA_NAME, modelName,
"non_exist_version"),
+ updatePropertiesUpdater));
+ }
+
private NameIdentifier getModelVersionIdent(NameIdentifier modelIdent, int
version) {
List<String> parts = Lists.newArrayList(modelIdent.namespace().levels());
parts.add(modelIdent.name());