Skip to content
This repository has been archived by the owner on Apr 16, 2022. It is now read-only.

[JanusGraph] Fixed - should contain id property #243

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ public class Constants {
public static final String PROPERTY_LABEL = "label";
public static final String PROPERTY_TYPE = "type";
public static final String PROPERTY_VALUE = "value";
public static final String PROPERTY_VALUE_WITH_AT = "@value";
public static final String PROPERTY_PROPERTIES = "properties";
public static final String PROPERTY_INV = "inV";
public static final String PROPERTY_OUTV = "outV";
public static final String PROPERTY_RELATION_ID = "relationId";

public static final String RESULT_TYPE_VERTEX = "vertex";
public static final String RESULT_TYPE_EDGE = "edge";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,40 @@
import java.util.LinkedHashMap;
import java.util.Map;

import static com.microsoft.spring.data.gremlin.common.Constants.PROPERTY_VALUE_WITH_AT;

@NoArgsConstructor
// TODO: seems only for Vertex.
public abstract class AbstractGremlinResultReader {

/**
* Vertex's properties returned from gremlin-driver has a complicated data structure
* This function helps to renovate it to a simple Map
* @return Map of list properties
*/
protected Map<String, Object> getProperties (@NonNull Map<String, Object> map) {
Map<String, Object> propertyMap = map;
while ((propertyMap instanceof LinkedHashMap) && propertyMap.containsKey(PROPERTY_VALUE_WITH_AT)) {
final Object value = propertyMap.get(PROPERTY_VALUE_WITH_AT);
if (value instanceof ArrayList && ((ArrayList) value).size() > 0) {
propertyMap = (Map<String, Object>) ((ArrayList) value).get(0);
} else {
propertyMap = (Map<String, Object>) value;
}
}

return propertyMap;
}

protected Object getPropertyValue (@NonNull Map<String, Object> map, @NonNull String propertyKey) {
Object value = map.get(propertyKey);

while ((value instanceof LinkedHashMap) && ((LinkedHashMap) value).containsKey(PROPERTY_VALUE_WITH_AT)) {
value = ((LinkedHashMap) value).get(PROPERTY_VALUE_WITH_AT);
}

return value;
}

/**
* properties's organization is a little complicated.
* <p>
Expand All @@ -28,14 +58,18 @@ public abstract class AbstractGremlinResultReader {
* T is LinkedHashMap<String, String>
*/
private Object readProperty(@NonNull Object value) {
Assert.isInstanceOf(ArrayList.class, value, "should be instance of ArrayList");
if (value instanceof ArrayList) {
@SuppressWarnings("unchecked") final ArrayList<LinkedHashMap<String, Object>> mapList
= (ArrayList<LinkedHashMap<String, Object>>) value;

Assert.isTrue(mapList.size() == 1, "should be only 1 element in ArrayList");

@SuppressWarnings("unchecked") final ArrayList<LinkedHashMap<String, String>> mapList
= (ArrayList<LinkedHashMap<String, String>>) value;
value = mapList.get(0);
}

Assert.isTrue(mapList.size() == 1, "should be only 1 element in ArrayList");
final Map<String, Object> renovatedMap = getProperties((Map<String, Object>) value);

return mapList.get(0).get(Constants.PROPERTY_VALUE);
return renovatedMap.get(Constants.PROPERTY_VALUE);
}

protected void readResultProperties(@NonNull Map<String, Object> properties, @NonNull GremlinSource source) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge;
import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import org.apache.tinkerpop.gremlin.driver.Result;
import org.springframework.lang.Nullable;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

Expand All @@ -23,31 +23,38 @@
@NoArgsConstructor
public class GremlinResultEdgeReader extends AbstractGremlinResultReader implements GremlinResultsReader {

private void readProperties(@NonNull GremlinSource source, @Nullable Map map) {
if (map != null) {
@SuppressWarnings("unchecked") final Map<String, Object> properties = (Map<String, Object>) map;

properties.forEach(source::setProperty);
}
}

private void validate(List<Result> results, GremlinSource source) {
Assert.notNull(results, "Results should not be null.");
Assert.notNull(source, "GremlinSource should not be null.");
Assert.isTrue(results.size() == 1, "Edge should contain only one result.");
}

final Result result = results.get(0);

private Map<String, Object> getEdgeProperties (@NonNull Result result) {
Assert.isInstanceOf(Map.class, result.getObject(), "should be one instance of Map");

@SuppressWarnings("unchecked") final Map<String, Object> map = (Map<String, Object>) result.getObject();
Map<String, Object> map = (Map<String, Object>) result.getObject();

map = getProperties(map);

Assert.isTrue(map.containsKey(PROPERTY_ID), "should contain id property");
Assert.isTrue(map.containsKey(PROPERTY_LABEL), "should contain label property");
Assert.isTrue(map.containsKey(PROPERTY_TYPE), "should contain type property");
// Assert.isTrue(map.containsKey(PROPERTY_TYPE), "should contain type property");
Assert.isTrue(map.containsKey(PROPERTY_INV), "should contain inV property");
Assert.isTrue(map.containsKey(PROPERTY_OUTV), "should contain outV property");
Assert.isTrue(map.get(PROPERTY_TYPE).equals(RESULT_TYPE_EDGE), "must be vertex type");
// Assert.isTrue(map.get(PROPERTY_TYPE).equals(RESULT_TYPE_EDGE), "must be vertex type");

return map;
}

@Override
protected Object getPropertyValue (@NonNull Map<String, Object> map, @NonNull String propertyKey) {
Object value = super.getPropertyValue(map, propertyKey);

if (value instanceof LinkedHashMap && ((LinkedHashMap) value).containsKey(PROPERTY_RELATION_ID)) {
value = ((LinkedHashMap) value).get(PROPERTY_RELATION_ID);
}

return value;
}

@Override
Expand All @@ -60,16 +67,16 @@ public void read(@NonNull List<Result> results, @NonNull GremlinSource source) {
validate(results, source);

final GremlinSourceEdge sourceEdge = (GremlinSourceEdge) source;
final Map<String, Object> map = (Map<String, Object>) results.get(0).getObject();
final Map<String, Object> map = getEdgeProperties(results.get(0));

this.readProperties(source, (Map) map.get(PROPERTY_PROPERTIES));
super.readResultProperties((Map) map.get(PROPERTY_PROPERTIES), source);

final String className = source.getProperties().get(GREMLIN_PROPERTY_CLASSNAME).toString();

sourceEdge.setIdField(GremlinUtils.getIdField(GremlinUtils.toEntityClass(className)));
sourceEdge.setId(map.get(PROPERTY_ID));
sourceEdge.setLabel(map.get(PROPERTY_LABEL).toString());
sourceEdge.setVertexIdFrom(map.get(PROPERTY_OUTV));
sourceEdge.setVertexIdTo(map.get(PROPERTY_INV));
sourceEdge.setId(getPropertyValue(map, PROPERTY_ID));
sourceEdge.setLabel(getPropertyValue(map, PROPERTY_LABEL).toString());
sourceEdge.setVertexIdFrom(getPropertyValue(map, PROPERTY_OUTV));
sourceEdge.setVertexIdTo(getPropertyValue(map, PROPERTY_INV));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,23 @@ private void validate(List<Result> results, GremlinSource source) {
Assert.notNull(results, "Results should not be null.");
Assert.notNull(source, "GremlinSource should not be null.");
Assert.isTrue(results.size() == 1, "Vertex should contain only one result.");
}

final Result result = results.get(0);

private Map<String, Object> getVertexProperties (@NonNull Result result) {
Assert.isInstanceOf(Map.class, result.getObject(), "should be one instance of Map");

@SuppressWarnings("unchecked") final Map<String, Object> map = (Map<String, Object>) result.getObject();
Map<String, Object> map = (Map<String, Object>) result.getObject();

map = getProperties(map);

Assert.isTrue(map.containsKey(PROPERTY_ID), "should contain id property");
Assert.isTrue(map.containsKey(PROPERTY_LABEL), "should contain label property");
Assert.isTrue(map.containsKey(PROPERTY_TYPE), "should contain type property");
// Assert.isTrue(map.containsKey(PROPERTY_TYPE), "should contain type property");
Assert.isTrue(map.containsKey(PROPERTY_PROPERTIES), "should contain properties property");
Assert.isTrue(map.get(PROPERTY_TYPE).equals(RESULT_TYPE_VERTEX), "must be vertex type");

// Assert.isTrue(map.get(PROPERTY_TYPE).equals(RESULT_TYPE_VERTEX), "must be vertex type");
Assert.isInstanceOf(Map.class, map.get(PROPERTY_PROPERTIES), "should be one instance of Map");

return map;
}

@Override
Expand All @@ -51,15 +54,15 @@ public void read(@NonNull List<Result> results, @NonNull GremlinSource source) {

validate(results, source);

final Map<String, Object> map = (Map<String, Object>) results.get(0).getObject();
final Map<String, Object> map = getVertexProperties(results.get(0));
final Map<String, Object> properties = (Map<String, Object>) map.get(PROPERTY_PROPERTIES);

super.readResultProperties(properties, source);

final String className = source.getProperties().get(GREMLIN_PROPERTY_CLASSNAME).toString();

source.setIdField(GremlinUtils.getIdField(GremlinUtils.toEntityClass(className)));
source.setId(map.get(PROPERTY_ID));
source.setLabel(map.get(PROPERTY_LABEL).toString());
source.setId(getPropertyValue(map, PROPERTY_ID));
source.setLabel(getPropertyValue(map, PROPERTY_LABEL).toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ public interface GremlinOperations {

<T> List<T> find(GremlinQuery query, GremlinSource<T> source);

<T> List<T> findByQuery(List<String> queries, GremlinSource<T> source);

MappingGremlinConverter getMappingConverter();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.microsoft.spring.data.gremlin.annotation.EdgeFrom;
import com.microsoft.spring.data.gremlin.annotation.EdgeTo;
import com.microsoft.spring.data.gremlin.annotation.GeneratedValue;
import com.microsoft.spring.data.gremlin.common.Constants;
import com.microsoft.spring.data.gremlin.common.GremlinEntityType;
import com.microsoft.spring.data.gremlin.common.GremlinFactory;
import com.microsoft.spring.data.gremlin.common.GremlinUtils;
Expand Down Expand Up @@ -41,9 +42,7 @@

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.ExecutionException;

import static java.util.stream.Collectors.toList;
Expand Down Expand Up @@ -94,13 +93,31 @@ private List<Result> executeQuery(@NonNull List<String> queries) {
private List<Result> executeQueryParallel(@NonNull List<String> queries) {
return queries.parallelStream()
.map(q -> getGremlinClient().submit(q).all())
.collect(toList()).parallelStream().flatMap(f -> {
.collect(toList())
.parallelStream()
.flatMap(f -> {
try {
return f.get().stream();
} catch (InterruptedException | ExecutionException e) {
throw new GremlinQueryException("unable to complete query from gremlin", e);
}
})
.flatMap(r -> {
final List<Result> results = new ArrayList<>();
Object object = r.getObject();
while (object instanceof LinkedHashMap
&& ((LinkedHashMap) object).containsKey(Constants.PROPERTY_VALUE_WITH_AT)) {
object = ((LinkedHashMap) object).get(Constants.PROPERTY_VALUE_WITH_AT);
}

if (object instanceof ArrayList) {
((ArrayList) object).forEach(o -> results.add(new Result(o)));
} else {
results.add(new Result(object));
}

return results.stream();
})
.collect(toList());
}

Expand Down Expand Up @@ -349,7 +366,10 @@ private <T> T recoverDomain(@NonNull GremlinSource<T> source, @NonNull List<Resu
}

private <T> List<T> recoverDomainList(@NonNull GremlinSource<T> source, @NonNull List<Result> results) {
return results.stream().map(r -> recoverDomain(source, Collections.singletonList(r))).collect(toList());
return results
.stream()
.map(r -> recoverDomain(source, Collections.singletonList(r)))
.collect(toList());
}

private <T> T recoverGraphDomain(@NonNull GremlinSourceGraph<T> source, @NonNull List<Result> results) {
Expand Down Expand Up @@ -382,5 +402,16 @@ public <T> List<T> find(@NonNull GremlinQuery query, @NonNull GremlinSource<T> s

return this.recoverDomainList(source, results);
}

@Override
public <T> List<T> findByQuery(@NonNull List<String> queryList, @NonNull GremlinSource<T> source) {
final List<Result> results = this.executeQuery(queryList);

if (results.isEmpty()) {
return Collections.emptyList();
}

return this.recoverDomainList(source, results);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
import org.springframework.data.repository.NoRepositoryBean;

import java.io.Serializable;
import java.util.List;

@NoRepositoryBean
public interface GremlinRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {

Iterable<T> findAll(Class<T> domainClass);

Iterable<T> findAllByQuery(List<String> queryList);

void deleteAll(GremlinEntityType type);

void deleteAll(Class<T> domainClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,9 @@ public void deleteAll(@NonNull Class<T> domainClass) {
public boolean existsById(@NonNull ID id) {
return this.operations.existsById(id, this.information.createGremlinSource());
}

public Iterable<T> findAllByQuery(@NonNull List<String> queries) {
return this.operations.findByQuery(queries, this.information.createGremlinSource());
}
}