Skip to content
Permalink
Browse files
Add name for Edge (#121)
* Add name for Edge
Change-Id: I90382e5a70d557eb7a2fa42e2b50f04800584a30
  • Loading branch information
Linary committed Jun 1, 2021
1 parent 621c0b2 commit d9b72be085fb4865ee288c6ba282e6d4e20fbd0d
Showing 9 changed files with 403 additions and 8 deletions.
@@ -113,7 +113,7 @@
<manifestEntries>
<!-- Must be on one line, otherwise the automatic
upgrade script cannot replace the version number -->
<Implementation-Version>1.9.3.0</Implementation-Version>
<Implementation-Version>1.9.4.0</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
@@ -22,6 +22,7 @@
import com.baidu.hugegraph.exception.InvalidOperationException;
import com.baidu.hugegraph.structure.GraphElement;
import com.baidu.hugegraph.util.E;
import com.baidu.hugegraph.util.SplicingIdGenerator;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

@@ -40,6 +41,7 @@ public class Edge extends GraphElement {

private Vertex source;
private Vertex target;
private String name;

@JsonCreator
public Edge(@JsonProperty("label") String label) {
@@ -51,6 +53,7 @@ public Edge(@JsonProperty("label") String label) {
this.targetLabel = null;
this.source = null;
this.target = null;
this.name = null;
}

public String id() {
@@ -138,6 +141,17 @@ public void targetLabel(String targetLabel) {
this.targetLabel = targetLabel;
}

public String name() {
if (this.name == null) {
String[] idParts = SplicingIdGenerator.split(this.id);
E.checkState(idParts.length == 4,
"The edge id must be formatted by 4 parts, " +
"actual is %s", idParts.length);
this.name = idParts[2];
}
return this.name;
}

@Override
public Edge property(String key, Object value) {
E.checkNotNull(key, "The property name can not be null");
@@ -0,0 +1,74 @@
/*
* Copyright 2017 HugeGraph Authors
*
* 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 com.baidu.hugegraph.util;

import org.apache.commons.lang3.StringUtils;

/**
* Copied from HugeGraph(https://github.com/hugegraph/hugegraph)
*/
public final class IdUtil {

public static String escape(char splitor, char escape, String... values) {
int length = values.length + 4;
for (String value : values) {
length += value.length();
}
StringBuilder escaped = new StringBuilder(length);
// Do escape for every item in values
for (String value : values) {
if (escaped.length() > 0) {
escaped.append(splitor);
}

if (value.indexOf(splitor) == -1) {
escaped.append(value);
continue;
}

// Do escape for current item
for (int i = 0, n = value.length(); i < n; i++) {
char ch = value.charAt(i);
if (ch == splitor) {
escaped.append(escape);
}
escaped.append(ch);
}
}
return escaped.toString();
}

public static String[] unescape(String id, String splitor, String escape) {
/*
* Note that the `splitor`/`escape` maybe special characters in regular
* expressions, but this is a frequently called method, for faster
* execution, we forbid the use of special characters as delimiter
* or escape sign.
* The `limit` param -1 in split method can ensure empty string be
* splited to a part.
*/
String[] parts = id.split("(?<!" + escape + ")" + splitor, -1);
for (int i = 0; i < parts.length; i++) {
parts[i] = StringUtils.replace(parts[i], escape + splitor,
splitor);
}
return parts;
}
}
@@ -0,0 +1,101 @@
/*
* Copyright 2017 HugeGraph Authors
*
* 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 com.baidu.hugegraph.util;

import java.util.Arrays;
import java.util.List;

/**
* Copied from HugeGraph(https://github.com/hugegraph/hugegraph)
*/
public class SplicingIdGenerator {

private static volatile SplicingIdGenerator instance;

public static SplicingIdGenerator instance() {
if (instance == null) {
synchronized (SplicingIdGenerator.class) {
if (instance == null) {
instance = new SplicingIdGenerator();
}
}
}
return instance;
}

/*
* The following defines can't be java regex special characters:
* "\^$.|?*+()[{"
* See: http://www.regular-expressions.info/characters.html
*/
private static final char ESCAPE = '`';
private static final char IDS_SPLITOR = '>';
private static final char ID_SPLITOR = ':';
private static final char NAME_SPLITOR = '!';

public static final String ESCAPE_STR = String.valueOf(ESCAPE);
public static final String IDS_SPLITOR_STR = String.valueOf(IDS_SPLITOR);
public static final String ID_SPLITOR_STR = String.valueOf(ID_SPLITOR);

/****************************** id generate ******************************/

/**
* Concat multiple ids into one composite id with IDS_SPLITOR
* @param ids the string id values to be concatted
* @return concatted string value
*/
public static String concat(String... ids) {
// NOTE: must support string id when using this method
return IdUtil.escape(IDS_SPLITOR, ESCAPE, ids);
}

/**
* Split a composite id into multiple ids with IDS_SPLITOR
* @param ids the string id value to be splitted
* @return splitted string values
*/
public static String[] split(String ids) {
return IdUtil.unescape(ids, IDS_SPLITOR_STR, ESCAPE_STR);
}

/**
* Concat property values with NAME_SPLITOR
* @param values the property values to be concatted
* @return concatted string value
*/
public static String concatValues(List<?> values) {
// Convert the object list to string array
int valuesSize = values.size();
String[] parts = new String[valuesSize];
for (int i = 0; i < valuesSize; i++) {
parts[i] = values.get(i).toString();
}
return IdUtil.escape(NAME_SPLITOR, ESCAPE, parts);
}

/**
* Concat property values with NAME_SPLITOR
* @param values the property values to be concatted
* @return concatted string value
*/
public static String concatValues(Object... values) {
return concatValues(Arrays.asList(values));
}
}
@@ -193,7 +193,9 @@ protected static void initEdgeLabel() {
schema.edgeLabel("knows")
.sourceLabel("person")
.targetLabel("person")
.multiTimes()
.properties("date", "city")
.sortKeys("date")
.nullableKeys("city")
.ifNotExist()
.create();
@@ -30,11 +30,15 @@
import org.junit.Test;

import com.baidu.hugegraph.BaseClientTest;
import com.baidu.hugegraph.driver.SchemaManager;
import com.baidu.hugegraph.exception.InvalidOperationException;
import com.baidu.hugegraph.exception.ServerException;
import com.baidu.hugegraph.structure.constant.Direction;
import com.baidu.hugegraph.structure.constant.T;
import com.baidu.hugegraph.structure.graph.Edge;
import com.baidu.hugegraph.structure.graph.Vertex;
import com.baidu.hugegraph.structure.gremlin.Result;
import com.baidu.hugegraph.structure.gremlin.ResultSet;
import com.baidu.hugegraph.testutil.Assert;
import com.baidu.hugegraph.testutil.Utils;
import com.baidu.hugegraph.util.DateUtil;
@@ -294,6 +298,26 @@ public void testRemoveEdgePropertyNotExist() {
});
}

@Test
public void testName() {
BaseClientTest.initEdge();

Object markoId = getVertexId("person", "name", "marko");
List<Edge> edges = graph().getEdges(markoId, Direction.OUT, "knows");
Assert.assertEquals(2, edges.size());
Edge edge1 = edges.get(0);
Edge edge2 = edges.get(1);
Date date1 = DateUtil.parse((String) edge1.property("date"));
Date date2 = DateUtil.parse((String) edge2.property("date"));
String name1 = edge1.name();
String name2 = edge2.name();
if (date1.before(date2)) {
Assert.assertTrue(name1.compareTo(name2) < 0);
} else {
Assert.assertTrue(name1.compareTo(name2) >= 0);
}
}

@Test
public void testGetAllEdges() {
BaseClientTest.initEdge();
@@ -685,14 +709,76 @@ public void testIterateEdgesByVertexId() {

Map<String, Object> properties = ImmutableMap.of("date",
"P.gt(\"2012-1-1\")");
Iterator<Edge> iter = graph().iterateEdges(markoId, Direction.OUT,
"knows", properties, 1);
Assert.assertEquals(2, Iterators.size(iter));
}

@Test
public void testQueryByPagingAndFiltering() {
SchemaManager schema = schema();
schema.propertyKey("no").asText().create();
schema.propertyKey("location").asText().create();
schema.propertyKey("callType").asText().create();
schema.propertyKey("calltime").asDate().create();
schema.propertyKey("duration").asInt().create();
schema.vertexLabel("phone")
.properties("no")
.primaryKeys("no")
.enableLabelIndex(false)
.create();
schema.edgeLabel("call").multiTimes()
.properties("location", "callType", "duration", "calltime")
.sourceLabel("phone").targetLabel("phone")
.sortKeys("location", "callType", "duration", "calltime")
.create();

Vertex v1 = graph().addVertex(T.label, "phone", "no", "13812345678");
Vertex v2 = graph().addVertex(T.label, "phone", "no", "13866668888");
Vertex v10086 = graph().addVertex(T.label, "phone", "no", "10086");

v1.addEdge("call", v2, "location", "Beijing", "callType", "work",
"duration", 3, "calltime", "2017-5-1 23:00:00");
v1.addEdge("call", v2, "location", "Beijing", "callType", "work",
"duration", 3, "calltime", "2017-5-2 12:00:01");
v1.addEdge("call", v2, "location", "Beijing", "callType", "work",
"duration", 3, "calltime", "2017-5-3 12:08:02");
v1.addEdge("call", v2, "location", "Beijing", "callType", "work",
"duration", 8, "calltime", "2017-5-3 22:22:03");
v1.addEdge("call", v2, "location", "Beijing", "callType", "fun",
"duration", 10, "calltime", "2017-5-4 20:33:04");

v1.addEdge("call", v10086, "location", "Nanjing", "callType", "work",
"duration", 12, "calltime", "2017-5-2 15:30:05");
v1.addEdge("call", v10086, "location", "Nanjing", "callType", "work",
"duration", 14, "calltime", "2017-5-3 14:56:06");
v2.addEdge("call", v10086, "location", "Nanjing", "callType", "fun",
"duration", 15, "calltime", "2017-5-3 17:28:07");

ResultSet resultSet = gremlin().gremlin("g.V(vid).outE('call')" +
".has('location', 'Beijing')" +
".has('callType', 'work')" +
".has('duration', 3)" +
".has('calltime', " +
"P.between('2017-5-2', " +
"'2017-5-4'))" +
".toList()")
.binding("vid", v1.id())
.execute();
Iterator<Result> results = resultSet.iterator();
Assert.assertEquals(2, Iterators.size(results));

Assert.assertThrows(ServerException.class, () -> {
Iterator<Edge> iter = graph().iterateEdges(markoId, Direction.OUT,
"knows", properties,
1);
Iterators.size(iter);
// no location
gremlin().gremlin("g.V(vid).outE('call').has('callType', 'work')" +
".has('duration', 3).has('calltime', " +
"P.between('2017-5-2', '2017-5-4'))" +
".has('~page', '')")
.binding("vid", v1.id())
.execute();
}, e -> {
Assert.assertEquals("Can't query by paging and filtering",
e.getMessage());
Assert.assertContains("Can't query by paging and filtering",
e.getMessage());
});
}

0 comments on commit d9b72be

Please sign in to comment.