From 60478c0e077f9d0b32341ed6bca5e8b2a6f89be5 Mon Sep 17 00:00:00 2001 From: yxk485490 <1320342065@qq.com> Date: Mon, 29 Sep 2025 14:53:02 +0800 Subject: [PATCH 1/2] add multi property --- .../org/apache/graphar/info/EdgeInfo.java | 11 + .../org/apache/graphar/info/GraphInfo.java | 3 + .../org/apache/graphar/info/Property.java | 23 +- .../apache/graphar/info/PropertyGroup.java | 9 + .../org/apache/graphar/info/VertexInfo.java | 7 + .../info/saver/BaseGraphInfoSaver.java | 14 + .../apache/graphar/info/type/Cardinality.java | 59 ++++ .../graphar/info/yaml/PropertyYaml.java | 22 +- .../apache/graphar/info/yaml/VertexYaml.java | 13 + .../apache/graphar/info/GraphInfoTest.java | 8 + .../graphar/info/MultiPropertyTest.java | 325 ++++++++++++++++++ .../apache/graphar/info/TestDataFactory.java | 11 + .../org/apache/graphar/info/TestUtil.java | 9 + .../graphar/info/TestVerificationUtils.java | 4 + 14 files changed, 511 insertions(+), 7 deletions(-) create mode 100644 maven-projects/info/src/main/java/org/apache/graphar/info/type/Cardinality.java create mode 100644 maven-projects/info/src/test/java/org/apache/graphar/info/MultiPropertyTest.java diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/EdgeInfo.java b/maven-projects/info/src/main/java/org/apache/graphar/info/EdgeInfo.java index cd18c16dc..0a10ad0c4 100644 --- a/maven-projects/info/src/main/java/org/apache/graphar/info/EdgeInfo.java +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/EdgeInfo.java @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.graphar.info.type.AdjListType; +import org.apache.graphar.info.type.Cardinality; import org.apache.graphar.info.type.DataType; import org.apache.graphar.info.yaml.EdgeYaml; import org.apache.graphar.info.yaml.GraphYaml; @@ -429,6 +430,10 @@ public boolean hasProperty(String propertyName) { return propertyGroups.hasProperty(propertyName); } + public Cardinality getCardinality(String propertyName) { + return propertyGroups.getCardinality(propertyName); + } + public DataType getPropertyType(String propertyName) { return propertyGroups.getPropertyType(propertyName); } @@ -496,12 +501,14 @@ public URI getEdgesNumFileUri(AdjListType adjListType, long vertexChunkIndex) { } public void dump(Writer output) { + isValidated(); Yaml yaml = new Yaml(GraphYaml.getRepresenter(), GraphYaml.getDumperOptions()); EdgeYaml edgeYaml = new EdgeYaml(this); yaml.dump(edgeYaml, output); } public String dump() { + isValidated(); Yaml yaml = new Yaml(GraphYaml.getRepresenter(), GraphYaml.getDumperOptions()); EdgeYaml edgeYaml = new EdgeYaml(this); return yaml.dump(edgeYaml); @@ -626,6 +633,10 @@ public boolean isValidated() { } for (Property p : pg.getPropertyList()) { + if (p.getCardinality() != Cardinality.SINGLE) { + // edge property only supports single cardinality + return false; + } if (propertyNameSet.contains(p.getName())) { return false; } diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/GraphInfo.java b/maven-projects/info/src/main/java/org/apache/graphar/info/GraphInfo.java index e0ed2bfa3..ac7754d17 100644 --- a/maven-projects/info/src/main/java/org/apache/graphar/info/GraphInfo.java +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/GraphInfo.java @@ -128,18 +128,21 @@ private GraphInfo( } public void dump(URI storeUri, Writer output) { + isValidated(); Yaml yaml = new Yaml(GraphYaml.getRepresenter(), GraphYaml.getDumperOptions()); GraphYaml graphYaml = new GraphYaml(storeUri, this); yaml.dump(graphYaml, output); } public String dump(URI storeUri) { + isValidated(); Yaml yaml = new Yaml(GraphYaml.getRepresenter(), GraphYaml.getDumperOptions()); GraphYaml graphYaml = new GraphYaml(storeUri, this); return yaml.dump(graphYaml); } public String dump() { + isValidated(); Yaml yaml = new Yaml(GraphYaml.getRepresenter(), GraphYaml.getDumperOptions()); GraphYaml graphYaml = new GraphYaml(this); return yaml.dump(graphYaml); diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/Property.java b/maven-projects/info/src/main/java/org/apache/graphar/info/Property.java index 1221ebc13..2d63d29fd 100644 --- a/maven-projects/info/src/main/java/org/apache/graphar/info/Property.java +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/Property.java @@ -19,27 +19,44 @@ package org.apache.graphar.info; +import org.apache.graphar.info.type.Cardinality; import org.apache.graphar.info.type.DataType; import org.apache.graphar.info.yaml.PropertyYaml; public class Property { private final String name; private final DataType dataType; + private final Cardinality cardinality; private final boolean primary; private final boolean nullable; public Property(String name, DataType dataType, boolean primary, boolean nullable) { + this(name, dataType, Cardinality.SINGLE, primary, nullable); + } + + public Property( + String name, + DataType dataType, + Cardinality cardinality, + boolean primary, + boolean nullable) { this.name = name; this.dataType = dataType; + this.cardinality = cardinality; this.primary = primary; this.nullable = nullable; } - Property(PropertyYaml yamlParser) { + public Property(PropertyYaml yamlParser) { this.name = yamlParser.getName(); this.dataType = DataType.fromString(yamlParser.getData_type()); this.primary = yamlParser.getIs_primary(); this.nullable = yamlParser.getIs_nullable(); + Cardinality cardinality = Cardinality.SINGLE; + if (yamlParser.getCardinality() != null && !yamlParser.getCardinality().isEmpty()) { + cardinality = Cardinality.fromString(yamlParser.getCardinality()); + } + this.cardinality = cardinality; } public String getName() { @@ -57,4 +74,8 @@ public boolean isPrimary() { public boolean isNullable() { return nullable; } + + public Cardinality getCardinality() { + return cardinality; + } } diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/PropertyGroup.java b/maven-projects/info/src/main/java/org/apache/graphar/info/PropertyGroup.java index ae1fdc4ef..2aceacea6 100644 --- a/maven-projects/info/src/main/java/org/apache/graphar/info/PropertyGroup.java +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/PropertyGroup.java @@ -28,6 +28,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.graphar.info.type.Cardinality; import org.apache.graphar.info.type.DataType; import org.apache.graphar.info.type.FileType; import org.apache.graphar.util.GeneralParams; @@ -137,6 +138,9 @@ public boolean isValidated() { if (property.getDataType() == DataType.LIST && fileType == FileType.CSV) { return false; } + if (property.getCardinality() != Cardinality.SINGLE && fileType == FileType.CSV) { + return false; + } } return true; @@ -216,6 +220,11 @@ int getPropertyGroupNum() { return propertyGroupList.size(); } + public Cardinality getCardinality(String propertyName) { + checkPropertyExist(propertyName); + return properties.get(propertyName).getCardinality(); + } + DataType getPropertyType(String propertyName) { checkPropertyExist(propertyName); return properties.get(propertyName).getDataType(); diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/VertexInfo.java b/maven-projects/info/src/main/java/org/apache/graphar/info/VertexInfo.java index b4a14c5ee..27150df3c 100644 --- a/maven-projects/info/src/main/java/org/apache/graphar/info/VertexInfo.java +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/VertexInfo.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import org.apache.graphar.info.type.Cardinality; import org.apache.graphar.info.type.DataType; import org.apache.graphar.info.yaml.GraphYaml; import org.apache.graphar.info.yaml.VertexYaml; @@ -85,6 +86,10 @@ public int getPropertyGroupNum() { return propertyGroups.getPropertyGroupNum(); } + public Cardinality getCardinality(String propertyName) { + return propertyGroups.getCardinality(propertyName); + } + public DataType getPropertyType(String propertyName) { return propertyGroups.getPropertyType(propertyName); } @@ -124,12 +129,14 @@ public URI getVerticesNumFileUri() { } public void dump(Writer output) { + isValidated(); Yaml yaml = new Yaml(GraphYaml.getRepresenter(), GraphYaml.getDumperOptions()); VertexYaml vertexYaml = new VertexYaml(this); yaml.dump(vertexYaml, output); } public String dump() { + isValidated(); Yaml yaml = new Yaml(GraphYaml.getRepresenter(), GraphYaml.getDumperOptions()); VertexYaml vertexYaml = new VertexYaml(this); return yaml.dump(vertexYaml); diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/saver/BaseGraphInfoSaver.java b/maven-projects/info/src/main/java/org/apache/graphar/info/saver/BaseGraphInfoSaver.java index a59bdcc7b..5da7f3454 100644 --- a/maven-projects/info/src/main/java/org/apache/graphar/info/saver/BaseGraphInfoSaver.java +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/saver/BaseGraphInfoSaver.java @@ -32,6 +32,11 @@ public abstract class BaseGraphInfoSaver implements GraphInfoSaver { @Override public void save(URI graphInfoUri, GraphInfo graphInfo) throws IOException { + // if graphInfoUri is a directory then save to ${graphInfo.name}.graph.yml + if (graphInfoUri.getPath().endsWith("/")) { + graphInfoUri = + URI.create(graphInfoUri.toString() + graphInfo.getName() + ".graph.yaml"); + } Writer writer = writeYaml(graphInfoUri); graphInfo.dump(graphInfoUri, writer); writer.close(); @@ -48,6 +53,11 @@ public void save(URI graphInfoUri, GraphInfo graphInfo) throws IOException { @Override public void save(URI vertexInfoUri, VertexInfo vertexInfo) throws IOException { + // if vertexInfoUri is a directory then save to ${vertexInfo.type}.vertex.yml + if (vertexInfoUri.getPath().endsWith("/")) { + vertexInfoUri = + URI.create(vertexInfoUri.toString() + vertexInfo.getType() + ".vertex.yaml"); + } Writer vertexWriter = writeYaml(vertexInfoUri); vertexInfo.dump(vertexWriter); vertexWriter.close(); @@ -55,6 +65,10 @@ public void save(URI vertexInfoUri, VertexInfo vertexInfo) throws IOException { @Override public void save(URI edgeInfoUri, EdgeInfo edgeInfo) throws IOException { + // if edgeInfoUri is a directory then save to ${edgeInfo.getConcat()}.edge.yml + if (edgeInfoUri.getPath().endsWith("/")) { + edgeInfoUri = URI.create(edgeInfoUri.toString() + edgeInfo.getConcat() + ".edge.yaml"); + } Writer edgeWriter = writeYaml(edgeInfoUri); edgeInfo.dump(edgeWriter); edgeWriter.close(); diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/type/Cardinality.java b/maven-projects/info/src/main/java/org/apache/graphar/info/type/Cardinality.java new file mode 100644 index 000000000..c446919d5 --- /dev/null +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/type/Cardinality.java @@ -0,0 +1,59 @@ +/* + * 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.graphar.info.type; + +/** Defines how multiple values are handled for a given property key. */ +public enum Cardinality { + /** Single value property */ + SINGLE, + /** List of values property */ + LIST, + /** Set of values property (no duplicates) */ + SET; + + public String toString() { + switch (this) { + case SINGLE: + return "single"; + case LIST: + return "list"; + case SET: + return "set"; + default: + throw new IllegalArgumentException("Unknown cardinality: " + this); + } + } + + public static Cardinality fromString(String cardinality) { + if (cardinality == null) { + return null; + } + switch (cardinality) { + case "single": + return SINGLE; + case "list": + return LIST; + case "set": + return SET; + default: + throw new IllegalArgumentException("Unknown cardinality: " + cardinality); + } + } +} diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/yaml/PropertyYaml.java b/maven-projects/info/src/main/java/org/apache/graphar/info/yaml/PropertyYaml.java index ee98a339d..afa25a7d2 100644 --- a/maven-projects/info/src/main/java/org/apache/graphar/info/yaml/PropertyYaml.java +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/yaml/PropertyYaml.java @@ -21,19 +21,21 @@ import java.util.Optional; import org.apache.graphar.info.Property; -import org.apache.graphar.info.type.DataType; +import org.apache.graphar.info.type.Cardinality; public class PropertyYaml { private String name; private String data_type; private boolean is_primary; private Optional is_nullable; + private String cardinality; public PropertyYaml() { this.name = ""; this.data_type = ""; this.is_primary = false; this.is_nullable = Optional.empty(); + this.cardinality = "single"; // Default to single } public PropertyYaml(Property property) { @@ -41,14 +43,11 @@ public PropertyYaml(Property property) { this.data_type = property.getDataType().toString(); this.is_primary = property.isPrimary(); this.is_nullable = Optional.of(property.isNullable()); + this.cardinality = property.getCardinality().toString(); } Property toProperty() { - return new Property( - name, - DataType.fromString(data_type), - is_primary, - is_nullable.orElseGet(() -> !is_primary)); + return new Property(this); } public String getName() { @@ -82,4 +81,15 @@ public boolean getIs_nullable() { public void setIs_nullable(boolean is_nullable) { this.is_nullable = Optional.of(is_nullable); } + + public String getCardinality() { + if (cardinality == null) { + return null; + } + return Cardinality.SINGLE.toString().equals(cardinality) ? null : cardinality; + } + + public void setCardinality(String cardinality) { + this.cardinality = cardinality; + } } diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/yaml/VertexYaml.java b/maven-projects/info/src/main/java/org/apache/graphar/info/yaml/VertexYaml.java index 345e0369e..42d5a3fc5 100644 --- a/maven-projects/info/src/main/java/org/apache/graphar/info/yaml/VertexYaml.java +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/yaml/VertexYaml.java @@ -30,6 +30,7 @@ public class VertexYaml { private String type; private long chunk_size; private List property_groups; + private List labels; private String prefix; private String version; @@ -37,6 +38,7 @@ public VertexYaml() { this.type = ""; this.chunk_size = 0; this.property_groups = new ArrayList<>(); + this.labels = null; this.prefix = ""; this.version = ""; } @@ -80,6 +82,17 @@ public void setProperty_groups(List property_groups) { this.property_groups = property_groups; } + public List getLabels() { + if (labels == null) { + return null; + } + return labels.isEmpty() ? null : labels; + } + + public void setLabels(List labels) { + this.labels = labels; + } + public String getPrefix() { return prefix; } diff --git a/maven-projects/info/src/test/java/org/apache/graphar/info/GraphInfoTest.java b/maven-projects/info/src/test/java/org/apache/graphar/info/GraphInfoTest.java index b4ade7188..9d9303733 100644 --- a/maven-projects/info/src/test/java/org/apache/graphar/info/GraphInfoTest.java +++ b/maven-projects/info/src/test/java/org/apache/graphar/info/GraphInfoTest.java @@ -27,6 +27,7 @@ import org.apache.graphar.info.loader.GraphInfoLoader; import org.apache.graphar.info.loader.impl.LocalFileSystemStreamGraphInfoLoader; import org.apache.graphar.info.type.AdjListType; +import org.apache.graphar.info.type.Cardinality; import org.apache.graphar.info.type.DataType; import org.apache.graphar.info.type.FileType; import org.junit.AfterClass; @@ -169,6 +170,8 @@ public void testPersonVertexPropertyGroup() { Assert.assertEquals("id", idProperty.getName()); Assert.assertEquals(DataType.INT64, idProperty.getDataType()); Assert.assertEquals(DataType.INT64, personVertexInfo.getPropertyType("id")); + Assert.assertEquals(Cardinality.SINGLE, idProperty.getCardinality()); + Assert.assertEquals(Cardinality.SINGLE, personVertexInfo.getCardinality("id")); Assert.assertTrue(idProperty.isPrimary()); Assert.assertTrue(personVertexInfo.isPrimaryKey("id")); Assert.assertFalse(idProperty.isNullable()); @@ -196,18 +199,21 @@ public void testPersonVertexPropertyGroup() { Assert.assertTrue(personVertexInfo.hasProperty("firstName")); Assert.assertEquals("firstName", firstNameProperty.getName()); Assert.assertEquals(DataType.STRING, firstNameProperty.getDataType()); + Assert.assertEquals(Cardinality.SINGLE, firstNameProperty.getCardinality()); Assert.assertFalse(firstNameProperty.isPrimary()); Assert.assertTrue(firstNameProperty.isNullable()); Property lastNameProperty = firstName_lastName_gender.getPropertyList().get(1); Assert.assertTrue(personVertexInfo.hasProperty("lastName")); Assert.assertEquals("lastName", lastNameProperty.getName()); Assert.assertEquals(DataType.STRING, lastNameProperty.getDataType()); + Assert.assertEquals(Cardinality.SINGLE, lastNameProperty.getCardinality()); Assert.assertFalse(lastNameProperty.isPrimary()); Assert.assertTrue(lastNameProperty.isNullable()); Property genderProperty = firstName_lastName_gender.getPropertyList().get(2); Assert.assertTrue(personVertexInfo.hasProperty("gender")); Assert.assertEquals("gender", genderProperty.getName()); Assert.assertEquals(DataType.STRING, genderProperty.getDataType()); + Assert.assertEquals(Cardinality.SINGLE, genderProperty.getCardinality()); Assert.assertFalse(genderProperty.isPrimary()); Assert.assertTrue(genderProperty.isNullable()); } @@ -351,6 +357,8 @@ public void testKnowsEdgePropertyGroup() { Assert.assertEquals("creationDate", property.getName()); Assert.assertEquals(DataType.STRING, property.getDataType()); Assert.assertEquals(DataType.STRING, knowsEdgeInfo.getPropertyType("creationDate")); + Assert.assertEquals(Cardinality.SINGLE, property.getCardinality()); + Assert.assertEquals(Cardinality.SINGLE, knowsEdgeInfo.getCardinality("creationDate")); Assert.assertFalse(property.isPrimary()); Assert.assertFalse(knowsEdgeInfo.isPrimaryKey("creationDate")); Assert.assertTrue(property.isNullable()); diff --git a/maven-projects/info/src/test/java/org/apache/graphar/info/MultiPropertyTest.java b/maven-projects/info/src/test/java/org/apache/graphar/info/MultiPropertyTest.java new file mode 100644 index 000000000..afba01d31 --- /dev/null +++ b/maven-projects/info/src/test/java/org/apache/graphar/info/MultiPropertyTest.java @@ -0,0 +1,325 @@ +/* + * 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.graphar.info; + +import java.io.IOException; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import org.apache.graphar.info.loader.GraphInfoLoader; +import org.apache.graphar.info.loader.impl.LocalFileSystemStringGraphInfoLoader; +import org.apache.graphar.info.saver.GraphInfoSaver; +import org.apache.graphar.info.saver.impl.LocalFileSystemYamlGraphSaver; +import org.apache.graphar.info.type.AdjListType; +import org.apache.graphar.info.type.Cardinality; +import org.apache.graphar.info.type.DataType; +import org.apache.graphar.info.type.FileType; +import org.apache.graphar.info.yaml.PropertyYaml; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class MultiPropertyTest extends BaseFileSystemTest { + + private String testSaveDirectory; + private GraphInfoLoader graphInfoLoader; + private GraphInfoSaver graphInfoSaver; + private Property singleProperty; + private Property listProperty; + private Property setProperty; + + @Before + public void setUp() { + testSaveDirectory = createCleanTestDirectory("ldbc_multi_property_sample/"); + graphInfoLoader = new LocalFileSystemStringGraphInfoLoader(); + graphInfoSaver = new LocalFileSystemYamlGraphSaver(); + singleProperty = + TestDataFactory.createProperty("single_email", DataType.STRING, false, true); + listProperty = + TestDataFactory.createProperty( + "list_email", DataType.STRING, Cardinality.LIST, false, true); + setProperty = + TestDataFactory.createProperty( + "set_email", DataType.STRING, Cardinality.SET, false, true); + } + + @After + public void tearDown() { + // Test data will be preserved for debugging - cleanup happens before next test run + System.out.println("Test data saved in: " + testSaveDirectory); + } + + @Test + public void testPropertyWithCardinality() { + // Test single cardinality property + Assert.assertEquals("single_email", singleProperty.getName()); + Assert.assertEquals(DataType.STRING, singleProperty.getDataType()); + Assert.assertFalse(singleProperty.isPrimary()); + Assert.assertTrue(singleProperty.isNullable()); + Assert.assertEquals(Cardinality.SINGLE, singleProperty.getCardinality()); + + // Test list cardinality property + Assert.assertEquals("list_email", listProperty.getName()); + Assert.assertEquals(DataType.STRING, listProperty.getDataType()); + Assert.assertFalse(listProperty.isPrimary()); + Assert.assertTrue(listProperty.isNullable()); + Assert.assertEquals(Cardinality.LIST, listProperty.getCardinality()); + + // Test set cardinality property + Assert.assertEquals("set_email", setProperty.getName()); + Assert.assertEquals(DataType.STRING, setProperty.getDataType()); + Assert.assertFalse(setProperty.isPrimary()); + Assert.assertTrue(setProperty.isNullable()); + Assert.assertEquals(Cardinality.SET, setProperty.getCardinality()); + } + + @Test + public void testPropertyGroupWithMultiProperties() { + // Create property group with different cardinality properties + PropertyGroup propertyGroup = + TestDataFactory.createPropertyGroup( + Arrays.asList(singleProperty, listProperty, setProperty), + FileType.PARQUET, + "emails/"); + + Assert.assertEquals(3, propertyGroup.size()); + Assert.assertEquals(FileType.PARQUET, propertyGroup.getFileType()); + Assert.assertEquals("emails/", propertyGroup.getPrefix()); + + // Test iteration over properties + int count = 0; + for (Property property : propertyGroup) { + count++; + if ("single_email".equals(property.getName())) { + Assert.assertEquals(Cardinality.SINGLE, property.getCardinality()); + } else if ("list_email".equals(property.getName())) { + Assert.assertEquals(Cardinality.LIST, property.getCardinality()); + } else if ("set_email".equals(property.getName())) { + Assert.assertEquals(Cardinality.SET, property.getCardinality()); + } + } + Assert.assertEquals(3, count); + } + + @Test + public void testPropertyYamlWithCardinality() { + // Test converting Property to PropertyYaml and back + PropertyYaml singleYaml = new PropertyYaml(singleProperty); + PropertyYaml listYaml = new PropertyYaml(listProperty); + PropertyYaml setYaml = new PropertyYaml(setProperty); + + // Check YAML representations + Assert.assertEquals("single_email", singleYaml.getName()); + Assert.assertNull(singleYaml.getCardinality()); + + Assert.assertEquals("list_email", listYaml.getName()); + Assert.assertEquals("list", listYaml.getCardinality()); + + Assert.assertEquals("set_email", setYaml.getName()); + Assert.assertEquals("set", setYaml.getCardinality()); + + // Convert back to Property objects + Property singlePropFromYaml = new Property(singleYaml); + Property listPropFromYaml = new Property(listYaml); + Property setPropFromYaml = new Property(setYaml); + + // Verify properties are correctly restored + Assert.assertEquals(singleProperty.getName(), singlePropFromYaml.getName()); + Assert.assertEquals(singleProperty.getDataType(), singlePropFromYaml.getDataType()); + Assert.assertEquals(singleProperty.getCardinality(), singlePropFromYaml.getCardinality()); + + Assert.assertEquals(listProperty.getName(), listPropFromYaml.getName()); + Assert.assertEquals(listProperty.getDataType(), listPropFromYaml.getDataType()); + Assert.assertEquals(listProperty.getCardinality(), listPropFromYaml.getCardinality()); + + Assert.assertEquals(setProperty.getName(), setPropFromYaml.getName()); + Assert.assertEquals(setProperty.getDataType(), setPropFromYaml.getDataType()); + Assert.assertEquals(setProperty.getCardinality(), setPropFromYaml.getCardinality()); + } + + @Test + public void testDefaultCardinality() { + // Test that properties created without specifying cardinality default to SINGLE + Property defaultProperty = + TestDataFactory.createProperty("default_prop", DataType.STRING, false, true); + Assert.assertEquals(Cardinality.SINGLE, defaultProperty.getCardinality()); + + // Test YAML conversion with default cardinality + PropertyYaml defaultYaml = new PropertyYaml(defaultProperty); + Assert.assertNull(defaultYaml.getCardinality()); + + Property defaultPropFromYaml = new Property(defaultYaml); + Assert.assertEquals(Cardinality.SINGLE, defaultPropFromYaml.getCardinality()); + } + + @Test + public void testInvalidCardinalityHandling() { + // Test handling of invalid cardinality in YAML + PropertyYaml invalidYaml = new PropertyYaml(); + invalidYaml.setName("invalid_prop"); + invalidYaml.setData_type("string"); + invalidYaml.setCardinality("INVALID"); // This should default to SINGLE + IllegalArgumentException illegalArgumentException = + Assert.assertThrows( + IllegalArgumentException.class, () -> new Property(invalidYaml)); + Assert.assertEquals("Unknown cardinality: INVALID", illegalArgumentException.getMessage()); + } + + @Test + public void testEdgeInfoWithMultiProperty() throws IOException { + Property weights = + TestDataFactory.createProperty( + "weights", DataType.DOUBLE, Cardinality.LIST, false, true); + PropertyGroup weights_pg = + TestDataFactory.createPropertyGroup( + Arrays.asList(weights), FileType.CSV, "weights/"); + AdjacentList orderedBySource = + TestDataFactory.createAdjacentList( + AdjListType.ordered_by_source, FileType.PARQUET, "ordered_by_source/"); + EdgeInfo knowsEdgeInfo = + EdgeInfo.builder() + .srcType("person") + .edgeType("knows") + .dstType("person") + .chunkSize(1024) + .srcChunkSize(100) + .dstChunkSize(100) + .directed(false) + .prefix("edge/person_knows_person/") + .version("gar/v1") + .addAdjacentList(orderedBySource) + .addPropertyGroups(List.of(weights_pg)) + .build(); + Assert.assertFalse(knowsEdgeInfo.isValidated()); + } + + @Test + public void testListPropertyInCsv() { + Property emails = + TestDataFactory.createProperty( + "emails", DataType.STRING, Cardinality.LIST, false, true); + PropertyGroup emailInCsv = + TestDataFactory.createPropertyGroup(List.of(emails), FileType.CSV, "emails_csv/"); + VertexInfo personVertexInfo = + new VertexInfo("person", 1000, List.of(emailInCsv), "vertex/person/", "gar/v1"); + Assert.assertFalse(personVertexInfo.isValidated()); + } + + @Test + public void testSetPropertyInCsv() { + Property emails = + TestDataFactory.createProperty( + "emails", DataType.STRING, Cardinality.SET, false, true); + PropertyGroup emailInCsv = + TestDataFactory.createPropertyGroup(List.of(emails), FileType.CSV, "emails_csv/"); + VertexInfo personVertexInfo = + new VertexInfo("person", 1000, List.of(emailInCsv), "vertex/person/", "gar/v1"); + Assert.assertFalse(personVertexInfo.isValidated()); + } + + @Test + public void testGraphInfoWithMultiPropertyYamlLoadAndSave() throws IOException { + // Create a GraphInfo with multi-property support + Property id = TestDataFactory.createProperty("id", DataType.INT64, true, false); + Property name = TestDataFactory.createProperty("name", DataType.STRING, false, true); + Property emails = + TestDataFactory.createProperty( + "emails", DataType.STRING, Cardinality.LIST, false, true); + Property tags = + TestDataFactory.createProperty( + "tags", DataType.STRING, Cardinality.SET, false, true); + Property creationDate = + TestDataFactory.createProperty("creationDate", DataType.STRING, false, false); + PropertyGroup personIdGroup = + TestDataFactory.createPropertyGroup(List.of(id), FileType.PARQUET, "id/"); + PropertyGroup personInfoGroup = + TestDataFactory.createPropertyGroup( + Arrays.asList(name, emails, tags), FileType.PARQUET, "info/"); + + PropertyGroup edgePropertyGroup = + TestDataFactory.createPropertyGroup( + List.of(creationDate), FileType.PARQUET, "properties/"); + + // Create adjacent lists + AdjacentList orderedBySource = + TestDataFactory.createAdjacentList( + AdjListType.ordered_by_source, FileType.PARQUET, "ordered_by_source/"); + AdjacentList unorderedByDest = + TestDataFactory.createAdjacentList( + AdjListType.unordered_by_dest, FileType.PARQUET, "unordered_by_dest/"); + + VertexInfo personVertexInfo = + new VertexInfo( + "person", + 1000, + Arrays.asList(personIdGroup, personInfoGroup), + "vertex/person/", + "gar/v1"); + + EdgeInfo knowsEdgeInfo = + EdgeInfo.builder() + .srcType("person") + .edgeType("knows") + .dstType("person") + .chunkSize(1024) + .srcChunkSize(100) + .dstChunkSize(100) + .directed(false) + .prefix("edge/person_knows_person/") + .version("gar/v1") + .addAdjacentList(orderedBySource) + .addAdjacentList(unorderedByDest) + .addPropertyGroups(Arrays.asList(edgePropertyGroup)) + .build(); + + GraphInfo graphInfo = + new GraphInfo( + "test_graph", + Arrays.asList(personVertexInfo), + Arrays.asList(knowsEdgeInfo), + "/tmp/", + "gar/v1"); + + // Check that the generated YAML files contain cardinality information + String graphYamlFilePath = testSaveDirectory + "/test_graph.graph.yaml"; + + // Save GraphInfo to YAML files + graphInfoSaver.save(URI.create(testSaveDirectory), graphInfo); + + // Load GraphInfo from YAML files + GraphInfo loadedGraphInfo = graphInfoLoader.loadGraphInfo(URI.create(graphYamlFilePath)); + Assert.assertTrue(TestVerificationUtils.equalsGraphInfo(graphInfo, loadedGraphInfo)); + } + + @Test + public void testLoadFromTestData() throws IOException { + URI GRAPH_PATH_URI = TestUtil.getLdbcGraphURI(); + GraphInfo graphInfo = graphInfoLoader.loadGraphInfo(GRAPH_PATH_URI); + VertexInfo personInfo = graphInfo.getVertexInfo("person"); + Assert.assertEquals(Cardinality.SINGLE, personInfo.getCardinality("firstName")); + Assert.assertEquals(Cardinality.SINGLE, personInfo.getCardinality("lastName")); + Assert.assertEquals(Cardinality.SINGLE, personInfo.getCardinality("gender")); + Assert.assertEquals(Cardinality.SINGLE, personInfo.getCardinality("locationIP")); + Assert.assertEquals(Cardinality.SINGLE, personInfo.getCardinality("browserUsed")); + Assert.assertEquals(Cardinality.SINGLE, personInfo.getCardinality("id")); + Assert.assertEquals(Cardinality.LIST, personInfo.getCardinality("emails")); + } +} diff --git a/maven-projects/info/src/test/java/org/apache/graphar/info/TestDataFactory.java b/maven-projects/info/src/test/java/org/apache/graphar/info/TestDataFactory.java index b29434884..66b14e13b 100644 --- a/maven-projects/info/src/test/java/org/apache/graphar/info/TestDataFactory.java +++ b/maven-projects/info/src/test/java/org/apache/graphar/info/TestDataFactory.java @@ -21,6 +21,7 @@ import java.util.List; import org.apache.graphar.info.type.AdjListType; +import org.apache.graphar.info.type.Cardinality; import org.apache.graphar.info.type.DataType; import org.apache.graphar.info.type.FileType; @@ -100,6 +101,16 @@ public static Property createProperty( return new Property(name, dataType, isPrimary, isNullable); } + /** Creates a property with given parameters including cardinality. */ + public static Property createProperty( + String name, + DataType dataType, + Cardinality cardinality, + boolean isPrimary, + boolean isNullable) { + return new Property(name, dataType, cardinality, isPrimary, isNullable); + } + /** Creates a property group with given parameters. */ public static PropertyGroup createPropertyGroup( List properties, FileType fileType, String prefix) { diff --git a/maven-projects/info/src/test/java/org/apache/graphar/info/TestUtil.java b/maven-projects/info/src/test/java/org/apache/graphar/info/TestUtil.java index 880c60053..0b7033437 100644 --- a/maven-projects/info/src/test/java/org/apache/graphar/info/TestUtil.java +++ b/maven-projects/info/src/test/java/org/apache/graphar/info/TestUtil.java @@ -38,6 +38,7 @@ public class TestUtil { System.getProperty("test.output.dir", "target/test-output") + "/"; private static final String LDBC_SAMPLE_GRAPH_PATH = "/ldbc_sample/csv/ldbc_sample.graph.yml"; + private static final String LDBC_GRAPH_PATH = "/ldbc/parquet/ldbc.graph.yml"; public static String getTestData() { return GAR_TEST_DATA; @@ -56,6 +57,14 @@ public static URI getLdbcSampleGraphURI() { return URI.create(getLdbcSampleGraphPath()); } + public static String getLdbcGraphPath() { + return getTestData() + "/" + LDBC_GRAPH_PATH; + } + + public static URI getLdbcGraphURI() { + return URI.create(getLdbcGraphPath()); + } + public static VertexInfo buildVertexInfoFromYaml(VertexYaml vertexYaml) { return new VertexInfo( vertexYaml.getType(), diff --git a/maven-projects/info/src/test/java/org/apache/graphar/info/TestVerificationUtils.java b/maven-projects/info/src/test/java/org/apache/graphar/info/TestVerificationUtils.java index fe09e6e62..ddf660676 100644 --- a/maven-projects/info/src/test/java/org/apache/graphar/info/TestVerificationUtils.java +++ b/maven-projects/info/src/test/java/org/apache/graphar/info/TestVerificationUtils.java @@ -389,6 +389,10 @@ private static boolean equalsProperty(Property expected, Property actual) { "Property nullable flag mismatch", expected.isNullable(), actual.isNullable()); Assert.assertEquals( "Property data type mismatch", expected.getDataType(), actual.getDataType()); + Assert.assertEquals( + "Property cardinality mismatch", + expected.getCardinality(), + actual.getCardinality()); return true; } From 2f66ddeb47ef9dbe29c1892c6d72cb70f9bae9c8 Mon Sep 17 00:00:00 2001 From: yxk485490 <1320342065@qq.com> Date: Mon, 29 Sep 2025 15:09:06 +0800 Subject: [PATCH 2/2] remove cardinality in edgeInfo --- .../info/src/main/java/org/apache/graphar/info/EdgeInfo.java | 4 ---- .../src/test/java/org/apache/graphar/info/GraphInfoTest.java | 1 - 2 files changed, 5 deletions(-) diff --git a/maven-projects/info/src/main/java/org/apache/graphar/info/EdgeInfo.java b/maven-projects/info/src/main/java/org/apache/graphar/info/EdgeInfo.java index 0a10ad0c4..2962a1567 100644 --- a/maven-projects/info/src/main/java/org/apache/graphar/info/EdgeInfo.java +++ b/maven-projects/info/src/main/java/org/apache/graphar/info/EdgeInfo.java @@ -430,10 +430,6 @@ public boolean hasProperty(String propertyName) { return propertyGroups.hasProperty(propertyName); } - public Cardinality getCardinality(String propertyName) { - return propertyGroups.getCardinality(propertyName); - } - public DataType getPropertyType(String propertyName) { return propertyGroups.getPropertyType(propertyName); } diff --git a/maven-projects/info/src/test/java/org/apache/graphar/info/GraphInfoTest.java b/maven-projects/info/src/test/java/org/apache/graphar/info/GraphInfoTest.java index 9d9303733..77efdc6b7 100644 --- a/maven-projects/info/src/test/java/org/apache/graphar/info/GraphInfoTest.java +++ b/maven-projects/info/src/test/java/org/apache/graphar/info/GraphInfoTest.java @@ -358,7 +358,6 @@ public void testKnowsEdgePropertyGroup() { Assert.assertEquals(DataType.STRING, property.getDataType()); Assert.assertEquals(DataType.STRING, knowsEdgeInfo.getPropertyType("creationDate")); Assert.assertEquals(Cardinality.SINGLE, property.getCardinality()); - Assert.assertEquals(Cardinality.SINGLE, knowsEdgeInfo.getCardinality("creationDate")); Assert.assertFalse(property.isPrimary()); Assert.assertFalse(knowsEdgeInfo.isPrimaryKey("creationDate")); Assert.assertTrue(property.isNullable());