Initial draft of automatic Cypher query creation for repositories.
This commit is contained in:
@@ -20,29 +20,34 @@ import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.data.mapping.context.AbstractMappingContext;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* Neo4J specific {@link MappingContext} implementation. Simply creates {@link Neo4JPersistentEntityImpl} and
|
||||
* {@link Neo4JPersistentProperty} instances.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class Neo4JMappingContext extends AbstractMappingContext<Neo4JPersistentEntityImpl<?>, Neo4JPersistentProperty> {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
@Override
|
||||
protected <T> Neo4JPersistentEntityImpl<?> createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
return new Neo4JPersistentEntityImpl<T>(typeInformation);
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
@Override
|
||||
protected <T> Neo4JPersistentEntityImpl<?> createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
return new Neo4JPersistentEntityImpl<T>(typeInformation);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)
|
||||
*/
|
||||
@Override
|
||||
protected Neo4JPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
|
||||
Neo4JPersistentEntityImpl<?> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
return new Neo4JPersistentPropertyImpl(field, descriptor, owner, simpleTypeHolder);
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)
|
||||
*/
|
||||
@Override
|
||||
protected Neo4JPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
|
||||
Neo4JPersistentEntityImpl<?> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
return new Neo4JPersistentPropertyImpl(field, descriptor, owner, simpleTypeHolder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.graph.neo4j.mapping;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
|
||||
/**
|
||||
* Interface for Neo4J specific {@link PersistentEntity}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface Neo4JPersistentEntity<T> extends PersistentEntity<T, Neo4JPersistentProperty> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.graph.neo4j.mapping;
|
||||
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* Implementation of {@link Neo4JPersistentEntity}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class Neo4JPersistentEntityImpl<T> extends BasicPersistentEntity<T, Neo4JPersistentProperty> implements Neo4JPersistentEntity<T> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Neo4JPersistentEntityImpl} instance.
|
||||
*
|
||||
* @param information must not be {@literal null}.
|
||||
*/
|
||||
public Neo4JPersistentEntityImpl(TypeInformation<T> information) {
|
||||
super(information);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.graph.neo4j.mapping;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
|
||||
/**
|
||||
* Interface for Neo4J specific {@link PersistentProperty}s. Declares additional metadata to lookup relationship
|
||||
* information.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface Neo4JPersistentProperty extends PersistentProperty<Neo4JPersistentProperty> {
|
||||
|
||||
/**
|
||||
* Returns whether the property represents a relationship. If this returns {@literal true}, clients can expect
|
||||
* {@link #getRelationshipInfo()} to return a non-{@literal null} value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isRelationship();
|
||||
|
||||
/**
|
||||
* Returns the {@link RelationshipInfo} for the given property if it is a relationship or {@literal null} otherwise.
|
||||
*
|
||||
* @see #isRelationship()
|
||||
* @return
|
||||
*/
|
||||
RelationshipInfo getRelationshipInfo();
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 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.graph.neo4j.mapping;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.data.graph.annotation.GraphId;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.AbstractPersistentProperty;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
|
||||
/**
|
||||
* Implementation of {@link Neo4JPersistentProperty}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty<Neo4JPersistentProperty> implements
|
||||
Neo4JPersistentProperty {
|
||||
|
||||
private final RelationshipInfo relationshipInfo;
|
||||
private final boolean isIdProperty;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Neo4JPersistentPropertyImpl} from the given {@link Field}, {@link PropertyDescriptor} owner
|
||||
* {@link PersistentEntity} and {@link SimpleTypeHolder}.
|
||||
*
|
||||
* @param field
|
||||
* @param propertyDescriptor
|
||||
* @param owner
|
||||
* @param simpleTypeHolder
|
||||
*/
|
||||
public Neo4JPersistentPropertyImpl(Field field, PropertyDescriptor propertyDescriptor,
|
||||
PersistentEntity<?, Neo4JPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
super(field, propertyDescriptor, owner, simpleTypeHolder);
|
||||
|
||||
this.relationshipInfo = field.isAnnotationPresent(RelatedTo.class) ? new AnnotationBasedRelationshipInfo(
|
||||
field.getAnnotation(RelatedTo.class)) : null;
|
||||
this.isIdProperty = field.isAnnotationPresent(GraphId.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.PersistentProperty#isIdProperty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isIdProperty() {
|
||||
return this.isIdProperty;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation()
|
||||
*/
|
||||
@Override
|
||||
protected Association<Neo4JPersistentProperty> createAssociation() {
|
||||
return new Association<Neo4JPersistentProperty>(this, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.graph.neo4j.mapping.Neo4JPersistentProperty#isRelationship()
|
||||
*/
|
||||
@Override
|
||||
public boolean isRelationship() {
|
||||
return this.relationshipInfo != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.graph.neo4j.mapping.Neo4JPersistentProperty#getRelationShipInfo()
|
||||
*/
|
||||
@Override
|
||||
public RelationshipInfo getRelationshipInfo() {
|
||||
return relationshipInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.graph.neo4j.mapping;
|
||||
|
||||
import org.springframework.data.graph.core.Direction;
|
||||
|
||||
/**
|
||||
* Captures information about a relationship.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface RelationshipInfo {
|
||||
|
||||
/**
|
||||
* Returns the direction of the relationship.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Direction getDirection();
|
||||
|
||||
/**
|
||||
* Returns the type of the relationship.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getType();
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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.graph.neo4j.repository.query;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.graph.neo4j.mapping.Neo4JMappingContext;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Value object to create {@link CypherQuery} instances.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class CypherQuery {
|
||||
|
||||
private final String TEMPLATE = "start %s match % where %";
|
||||
|
||||
private final Neo4JMappingContext context;
|
||||
private final TypeInformation<?> rootType;
|
||||
|
||||
private final Set<MatchClause> matchClauses;
|
||||
|
||||
public CypherQuery(TypeInformation<?> rootType, Neo4JMappingContext context) {
|
||||
|
||||
Assert.notNull(rootType);
|
||||
Assert.notNull(context);
|
||||
|
||||
this.rootType = rootType;
|
||||
this.context = context;
|
||||
this.matchClauses = new HashSet<MatchClause>();
|
||||
}
|
||||
|
||||
public CypherQuery addRestriction(Part part) {
|
||||
|
||||
matchClauses.add(new MatchClause(context, part.getProperty()));
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(TEMPLATE, "TODO", StringUtils.collectionToCommaDelimitedString(matchClauses), "TODO");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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.graph.neo4j.repository.query;
|
||||
|
||||
import org.springframework.data.graph.neo4j.mapping.Neo4JMappingContext;
|
||||
import org.springframework.data.graph.neo4j.mapping.Neo4JPersistentProperty;
|
||||
import org.springframework.data.graph.neo4j.mapping.RelationshipInfo;
|
||||
import org.springframework.data.repository.query.parser.Property;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Value object to build the {@code match} clause of a Cypher query.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class MatchClause {
|
||||
|
||||
private final Iterable<Neo4JPersistentProperty> properties;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MatchClause} using the given {@link Neo4JMappingContext} and {@link Property}.
|
||||
*
|
||||
* @param context must not be {@literal null}.
|
||||
* @param property must not be {@literal null}.
|
||||
*/
|
||||
public MatchClause(Neo4JMappingContext context, Property property) {
|
||||
|
||||
Assert.notNull(context);
|
||||
Assert.notNull(property);
|
||||
|
||||
Class<?> rootType = property.getOwningType().getType();
|
||||
this.properties = context.getPersistentPropertyPath(rootType, property.toDotPath());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String intermediate = null;
|
||||
|
||||
for (Neo4JPersistentProperty property : properties) {
|
||||
|
||||
if (!property.isRelationship()) {
|
||||
return intermediate;
|
||||
}
|
||||
|
||||
RelationshipInfo info = property.getRelationshipInfo();
|
||||
Class<?> ownerType = property.getOwner().getType();
|
||||
|
||||
intermediate = intermediate == null ? asVariableReference(StringUtils.uncapitalize(ownerType.getSimpleName()))
|
||||
: intermediate;
|
||||
intermediate = String.format(getPattern(info), intermediate, info.getType(), asVariableReference(property.getName()));
|
||||
}
|
||||
|
||||
return intermediate.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given value as variable reference.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
private static String asVariableReference(String value) {
|
||||
return String.format("(%s)", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the clause pattern for the given {@link RelationshipInfo}.
|
||||
*
|
||||
* @param info must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static String getPattern(RelationshipInfo info) {
|
||||
|
||||
switch (info.getDirection()) {
|
||||
case OUTGOING:
|
||||
return "%s-[:%s]->%s";
|
||||
case INCOMING:
|
||||
return "%s<-[:%s]-%s";
|
||||
case BOTH:
|
||||
return "%s-[:%s]-%s";
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported direction!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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.graph.neo4j.repository.query;
|
||||
|
||||
class StartClause {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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.graph.neo4j.repository.query;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.graph.annotation.NodeEntity;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
import org.springframework.data.graph.core.Direction;
|
||||
import org.springframework.data.graph.neo4j.annotation.Indexed;
|
||||
import org.springframework.data.graph.neo4j.mapping.Neo4JMappingContext;
|
||||
import org.springframework.data.repository.query.parser.Property;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MatchClauseUnitTest {
|
||||
|
||||
Neo4JMappingContext context;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = new Neo4JMappingContext();
|
||||
context.setInitialEntitySet(Collections.singleton(Person.class));
|
||||
context.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildsMatchExpressionForSimpleTraversalCorrectly() {
|
||||
|
||||
MatchClause clause = new MatchClause(context, Property.from("group", Person.class));
|
||||
assertThat(clause.toString(), is("(person)<-[:members]-(group)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsMatchClassForDeepTraversal() {
|
||||
|
||||
MatchClause clause = new MatchClause(context, Property.from("group.members.age", Person.class));
|
||||
assertThat(clause.toString(), is("(person)<-[:members]-(group)-[:members]->(members)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stopsAtNonRelationShipProperty() {
|
||||
|
||||
MatchClause clause = new MatchClause(context, Property.from("group.name", Person.class));
|
||||
assertThat(clause.toString(), is("(person)<-[:members]-(group)"));
|
||||
}
|
||||
|
||||
@NodeEntity
|
||||
class Person {
|
||||
|
||||
private int age;
|
||||
|
||||
@RelatedTo(type = "members", direction = Direction.INCOMING)
|
||||
private Group group;
|
||||
}
|
||||
|
||||
class Group {
|
||||
|
||||
@Indexed
|
||||
private String name;
|
||||
|
||||
@RelatedTo(type = "members", direction = Direction.OUTGOING)
|
||||
private Set<Person> members;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user