- Created a QueryMapResultConverter that can take a map produced by Cypher/Gremlin and produce a proxy object to use for subgraph extraction

This commit is contained in:
Andres Taylor
2011-10-19 16:54:25 +02:00
parent 3f3c329cc1
commit 31884975ee
5 changed files with 204 additions and 7 deletions

View File

@@ -0,0 +1,27 @@
/**
* Copyright 2011 the original author or authors.
*
* Licensed 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.springframework.data.neo4j.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface ResultColumn {
String value() default "";
}

View File

@@ -0,0 +1,59 @@
/**
* Copyright 2011 the original author or authors.
*
* Licensed 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.springframework.data.neo4j.conversion;
import org.springframework.data.neo4j.annotation.ResultColumn;
import org.springframework.data.neo4j.template.Neo4jOperations;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
public class QueryMapResulConverter<T> implements ResultConverter<Map<String, Object>, T> {
private final Neo4jOperations template;
public QueryMapResulConverter(Neo4jOperations template) {
this.template = template;
}
@Override
public T convert(Map<String, Object> value, Class<T> type) {
final Map<String, Object> valueCopy = value;
T resultProxy = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{type}, new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
ResultColumn column = method.getAnnotation(ResultColumn.class);
TypeInformation<Object> returnType = ClassTypeInformation.fromReturnTypeOf(method);
Object columnValue = valueCopy.get(column.value());
Object result;
if (returnType.isCollectionLike())
result = template.convert((Iterable) columnValue).to(returnType.getActualType().getType());
else
result = template.convert(columnValue, returnType.getType());
return result;
}
});
return resultProxy;
}
}

View File

@@ -0,0 +1,103 @@
/**
* Copyright 2011 the original author or authors.
*
* Licensed 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.springframework.data.neo4j.conversion;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.neo4j.annotation.ResultColumn;
import org.springframework.data.neo4j.mapping.Neo4jPersistentTestBase;
import org.springframework.data.neo4j.model.Friendship;
import org.springframework.data.neo4j.model.Person;
import java.util.List;
import java.util.Map;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;
import static org.neo4j.helpers.collection.MapUtil.map;
public class QueryMapResultConverterTest extends Neo4jPersistentTestBase {
private Map<String, Object> simpleMap;
private List<String> friends;
private Map<String, Object> advancedMap;
public interface PersonAndFriendsData {
@ResultColumn("person")
Person getPerson();
@ResultColumn("collect(r)")
Iterable<Friendship> getFriends();
}
public interface SimplestQuery {
@ResultColumn("name")
String getName();
@ResultColumn("age")
Integer getAge();
@ResultColumn("friends")
Iterable<String> getFriendNames();
}
@Before
public void init() throws Exception {
storeInGraph(michael);
storeInGraph(andres);
storeInGraph(emil);
friends = asList("Michael", "Emil", "Anders");
simpleMap = map("name", "Andres", "age", 36L, "friends", friends);
advancedMap = map("person", michaelNode(), "collect(r)", michaelNode().getRelationships(KNOWS));
makeFriends(michaelNode(), andresNode(), 19);
makeFriends(michaelNode(), emilNode(), 6);
}
@Test
public void shouldBeAbleToGetAStringFromAResultMap() throws Exception {
QueryMapResulConverter<SimplestQuery> converter = getConverter();
SimplestQuery query = converter.convert(simpleMap, SimplestQuery.class);
assertThat(query.getName(), equalTo("Andres"));
assertThat(query.getAge(), equalTo(36));
}
@Test
public void shouldBeAbleToHandleAnIterableOfString() throws Exception {
QueryMapResulConverter<SimplestQuery> converter = getConverter();
SimplestQuery query = converter.convert(simpleMap, SimplestQuery.class);
assertThat(query.getFriendNames(), hasItems("Michael", "Emil", "Anders"));
}
@Test
public void shouldHandleANodeBackedEntity() throws Exception {
QueryMapResulConverter<PersonAndFriendsData> converter = new QueryMapResulConverter<PersonAndFriendsData>(template);
PersonAndFriendsData result = converter.convert(advancedMap, PersonAndFriendsData.class);
assertThat(result.getPerson(), equalTo(michael));
assertThat(result.getFriends(), equalTo(michael.getFriendships()));
}
private QueryMapResulConverter<SimplestQuery> getConverter() {
return new QueryMapResulConverter<SimplestQuery>(template);
}
}

View File

@@ -298,8 +298,7 @@ public class Neo4jEntityConverterTest extends Neo4jPersistentTestBase {
storeInGraph(michael);
storeInGraph(andres);
Relationship friendshipRelationship = michaelNode().createRelationshipTo(andresNode(), KNOWS);
friendshipRelationship.setProperty("Friendship.years", 19);
Relationship friendshipRelationship = makeFriends(michaelNode(), andresNode(), 19);
Person m = readPerson(michaelNode());
Friendship friendship = IteratorUtil.first(m.getFriendships());

View File

@@ -30,7 +30,9 @@ import org.springframework.data.neo4j.fieldaccess.NodeDelegatingFieldAccessorFac
import org.springframework.data.neo4j.fieldaccess.RelationshipDelegatingFieldAccessorFactory;
import org.springframework.data.neo4j.model.Group;
import org.springframework.data.neo4j.model.Person;
import org.springframework.data.neo4j.support.*;
import org.springframework.data.neo4j.support.DelegatingGraphDatabase;
import org.springframework.data.neo4j.support.MappingInfrastructure;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import org.springframework.data.neo4j.support.mapping.*;
import org.springframework.data.neo4j.support.node.NodeEntityInstantiator;
import org.springframework.data.neo4j.support.node.NodeEntityStateFactory;
@@ -100,7 +102,7 @@ public class Neo4jPersistentTestBase {
nodeEntityInstantiator = new NodeEntityInstantiator(entityStateHandler);
relationshipEntityInstantiator = new RelationshipEntityInstantiator(entityStateHandler);
nodeTypeMapper = new DefaultTypeMapper<Node>(new TRSTypeAliasAccessor<Node>(infrastructure.getNodeTypeRepresentationStrategy()),asList(new ClassValueTypeInformationMapper()));
nodeTypeMapper = new DefaultTypeMapper<Node>(new TRSTypeAliasAccessor<Node>(infrastructure.getNodeTypeRepresentationStrategy()), asList(new ClassValueTypeInformationMapper()));
nodeStateTransmitter = new SourceStateTransmitter<Node>(nodeEntityStateFactory);
relationshipStateTransmitter = new SourceStateTransmitter<Relationship>(relationshipEntityStateFactory);
conversionService = template.getConversionService();
@@ -135,7 +137,7 @@ public class Neo4jPersistentTestBase {
}
private MappingInfrastructure createInfrastructure(Neo4jMappingContext mappingContext) throws Exception {
MappingInfrastructure infrastructure=new MappingInfrastructure();
MappingInfrastructure infrastructure = new MappingInfrastructure();
final ImpermanentGraphDatabase gdb = new ImpermanentGraphDatabase();
infrastructure.setGraphDatabaseService(gdb);
final DelegatingGraphDatabase graphDatabase = new DelegatingGraphDatabase(gdb);
@@ -187,7 +189,7 @@ public class Neo4jPersistentTestBase {
if (id != null) {
write(p, template.getNode(id));
} else {
write(p,null);
write(p, null);
}
return p;
}
@@ -199,7 +201,7 @@ public class Neo4jPersistentTestBase {
@SuppressWarnings("unchecked")
private <T> T storeInGraph(T obj) {
return (T) write(obj,null);
return (T) write(obj, null);
}
protected Node groupNode() {
@@ -225,6 +227,13 @@ public class Neo4jPersistentTestBase {
public Person readPerson(Node node) {
return entityPersister.read(Person.class, node);
}
protected Relationship makeFriends(Node from, Node to, int years) {
Relationship friendship = from.createRelationshipTo(to, KNOWS);
friendship.setProperty("Friendship.years", years);
return friendship;
}
public Group readGroup(Node node) {
return entityPersister.read(Group.class, node);
}