Neo4j SDN4 OGM AmbiguousBaseClassException - neo4j

I am running into an issue wherein some of my Neo4J queries like the one below ends up in an OGM AmbiguousBaseClassException while others don't. For example findByTitle for the movie "The Score" throws an exception but "The Matrix" does not. My graph is populated by the Movie Database found at https://neo4j.com/developer/example-data/
I am unable to find an explanation to the above observation and I hope someone can help.
curl http://localhost:8080/movies/search/findByTitle?title=The%20Score
Neo4j Server: 3.1.0
Spring-data-neo4j:4.1.1
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.neo4j.ogm.exception.MappingException: Error mapping GraphModel to instance of com.knowledgeGraph.kgClient.domain.Movie
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982) ~[spring-webmvc-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
Caused by: org.neo4j.ogm.exception.AmbiguousBaseClassException: Multiple classes found in type hierarchy that map to: [Person, Actor, Director]
at org.neo4j.ogm.MetaData.resolve(MetaData.java:174) ~[neo4j-ogm-core-2.0.1.jar:na]
at org.neo4j.ogm.annotations.EntityFactory.resolve(EntityFactory.java:121) ~[neo4j-ogm-core-2.0.1.jar:na]
at org.neo4j.ogm.annotations.EntityFactory.instantiateObjectFromTaxa(EntityFactory.java:105) ~[neo4j-ogm-core-2.0.1.jar:na]
at org.neo4j.ogm.annotations.EntityFactory.newObject(EntityFactory.java:61) ~[neo4j-ogm-core-2.0.1.jar:na]
Domain Objects:
Movie Class:
import static org.neo4j.graphdb.Direction.INCOMING;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.voodoodyne.jackson.jsog.JSOGGenerator;
#JsonIdentityInfo(generator=JSOGGenerator.class)
#NodeEntity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Movie {
#GraphId Long nodeId;
String id;
String title;
String description;
#Relationship(type="DIRECTED", direction = Relationship.INCOMING)
List<Person> directors;
#Relationship(type="ACTS_IN", direction = Relationship.INCOMING)
List<Person> actors;
private String language;
private String imdbId;
private String tagline;
private String releaseDate;
private Integer runtime;
private String homepage;
private String trailer;
private String genre;
private String studio;
private Integer version;
private String lastModified;
private String imageUrl;
public Movie() { }
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/*Remaining Set's and Get's*/
}
Person Class:
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
import com.fasterxml.jackson.annotation.JsonSubTypes;
#NodeEntity
#JsonSubTypes({
#JsonSubTypes.Type(value = Actor.class, name = "actor"),
#JsonSubTypes.Type(value = Director.class, name = "director")
})
public class Person {
#GraphId Long nodeId;
String id;
String name;
private String birthday;
private String birthplace;
private String biography;
private Integer version;
private String lastModified;
private String profileImageUrl;
public Person () {}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/*Remaining Set's and Get's*/
}
Director Class:
#NodeEntity
public class Director extends Person{
#GraphId
Long id;
public Director() {
}
#Relationship(type="DIRECTED", direction = Relationship.OUTGOING)
private List<Movie> directedMovies = new ArrayList<Movie>();
public List<Movie> getDirectedMovies() {
return directedMovies;
}
public void setDirectedMovies(List<Movie> directedMovies) {
this.directedMovies = directedMovies;
}
}
Actor Class:
#NodeEntity
public class Actor extends Person {
#GraphId
Long id;
public Actor() {
}
#Relationship(type="ACTS_IN", direction = Relationship.OUTGOING)
private List<Movie> actedMovies = new ArrayList<Movie>();
public List<Movie> getMovies() {
return actedMovies;
}
public void setMovies(List<Movie> movies) {
this.actedMovies = movies;
}
}
Repositories:
public interface ActorRepository extends GraphRepository<Actor>{
#Query("MATCH (a:Actor) -[:ACTS_IN]-> (m:Movie {`title`:{title}}) return a")
Collection<Actor> findActorsOfMovie(#Param("title") String title);
}
public interface DirectorRepository extends GraphRepository<Director>{
#Query("MATCH (d:Director) -[:DIRECTED]-> (m:Movie {`title`:{title}}) return d")
Collection<Director> findDirectorOfMovie(#Param("title") String title);
}
public interface MovieRepository extends GraphRepository<Movie>{
Movie findByTitle(#Param("title") String title);
#Query("MATCH (m:Movie) WHERE m.title =~ ('(?i).*'+{title}+'.*') RETURN m")
Collection<Movie> findByTitleContaining(#Param("title") String title);
}
public interface PersonRepository extends GraphRepository<Person>{
#Query("MATCH (a:Person) -[:ACTS_IN]-> (m:Movie {`title`:{title}}) return a")
Set<Person> findActorsOfMovie(#Param("title") String title);
#Query("MATCH (d:Person) -[:DIRECTED]-> (m:Movie {`title`:{title}}) return d")
Set<Person> findDirectorOfMovie(#Param("title") String title);
}

Resolved this problem by removing Actor and Director domain classes and used Person class with Actor list and director list.

Related

Sesame alibaba mapping subclasses

I'm programming an application Web (Semantic Web) and for storing the triples, using sesame, and for mapping of data alibaba. My question is, that way I have to implement the classes,to that at the base of triplets is store an object as a subclass of another , try interfaces but I got no results , below I present the "entities":
the parent class:
import java.math.BigInteger;
import org.openrdf.annotations.Iri;
#Iri(Parent.NS + "Parent")
public interface Parent {
public static final String NS = "http://www.spelta.ec/ontology/example#";
#Iri(Parent.NS +"code")
BigInteger getCode();
#Iri(Parent.NS +"code")
void setCode(BigInteger code);
#Iri(Parent.NS+"description")
String getDescription();
#Iri(Parent.NS+"description")
void setDescription(String description);
}
The child class:
import java.math.BigInteger;
public class Child implements Parent {
private BigInteger code;
private String description;
#Override
public BigInteger getCode() {
return code;
}
#Override
public void setCode(BigInteger code) {
this.code = code;
}
#Override
public String getDescription() {
return description;
}
#Override
public void setDescription(String description) {
this.description = description;
}
}
to consult the store of triples, should define an instance of the Children class is a subclass of Parent
many thanks

How to specify many relationship types in a #Relationship

I have two nodes user and account and the relationship between them is any one of the 25 relationship.
My Query is
MATCH (u:User)-[r:rel1|rel2|rel3|rel4]->(a:Account) WHERE u.login_id=~('(?i).*'+{fulltextsearch}+'.*') RETURN u as User,r as acronym
My user pojo is
public class User{
#GraphId
private Long id;
String fulltextsearch;
String user_id;
String status;
//#Relationship(type = "rel1", direction= Relationship.OUTGOING)
Acronym acronym;
public Acronym getAcronym() {
return acronym;
}
private Set<Account> accounts;
public User() {
}
public String getUser_id() {
return user_id;
}
public String getStatus() {
return status;
}
public String getFulltextsearch() {
return fulltextsearch;
}
public Set<Account> getAccounts() {
return accounts;
}
public void setAccounts(Set<Account> accounts) {
this.accounts = accounts;
}
}
I am confused in writing my user pojo with multiple relatioships.
can I give #Relatioship for multiple relatioship with OR.
like this #Relationship(type = "rel1 | rel2", direction= Relationship.OUTGOING)
No, you cannot supply multiple relationship types to the #Relationship. You will have to declare them independently in your entity:
#Relationship(type = "rel1", direction= Relationship.OUTGOING)
Acronym acronymRel1;
#Relationship(type = "rel2", direction= Relationship.OUTGOING)
Acronym acronymRel2;
You can write a custom query to fetch all Acronyms based on a set of relationship types.

SDN 4 doesn't create relationship with properties

I am new to Neo4J. I have built a project that uses spring-data-neo4j (4.0.0.BUILD-SNAPSHOT - version), spring-boot (1.2.3.RELEASE - version) and succeeded to create node entities, add properties to node entities and add relationships. It works fine. Now I want to create properties for the relationships. I have used sdn4 university as a reference, here is the link https://github.com/neo4j-examples/sdn4-university .
I want to create a property called "challengedBy" for relationship PLAY_MATCH (Start node is Match and end node is Player). You can have a look on below class.
#RelationshipEntity(type = "PLAY_MATCH")
public class PlayMatch extends Entity {
//Entity is a class with the id property for the node / relationship
#Property
private String challengedBy;
#StartNode
private Match match;
#EndNode
private Player player1;
}
I have created a controller in the project /api/playmatch to create only the relationship between match and a player. So when I pass the values for an existing match node and a player node, the relationship is not created at all.
Any help will be appreciated..
PlayMatch code is
#RelationshipEntity(type = "PLAY_MATCH")
public class PlayMatch extends Entity{
#Property
private String challengedBy;
#StartNode
private Match match;
#EndNode
private Player player1;
public PlayMatch() {
}
public PlayMatch(String challengedBy, Match match,
Player player1) {
super();
this.challengedBy = challengedBy;
this.match = match;
this.player1 = player1;
}
// after this i have getters & setters and toString method for above fields.
}
Match code is
#NodeEntity(label = "Match")
public class Match extends Entity {
private String createdBy;
private Long createdTime;
private String status;
private int noOfGames;
private int noOfPoints;
private String type;
private Long date;
#Relationship(type="PLAY_MATCH",direction= Relationship.UNDIRECTED)
private PlayMatch playMatch;
public Match() {
}
public Match(String createdBy, Long createdTime, String status,
int noOfGames, int noOfPoints, String type, Long date) {
super();
this.createdBy = createdBy;
this.createdTime = createdTime;
this.status = status;
this.noOfGames = noOfGames;
this.noOfPoints = noOfPoints;
this.type = type;
this.date = date;
}
public PlayMatch getPlayMatch() {
return playMatch;
}
public void setPlayMatch(PlayMatch playMatch) {
this.playMatch = playMatch;
}
// after this i have getters & setters and toString method for above fields.
}
Player code is
#NodeEntity(label = "Player")
public class Player extends Entity {
private String address;
private String preferredSport;
private float height;
private float weight;
private String phone;
private String photo;
#Relationship(type="PLAY_MATCH")
private PlayMatch playMatch;
public PlayMatch getPlayMatch() {
return playMatch;
}
public void setPlayMatch(PlayMatch playMatch) {
this.playMatch = playMatch;
}
public Player() {
}
public Player(String address, String preferredSport, float height,
float weight, String phone, String photo) {
super();
this.address = address;
this.preferredSport = preferredSport;
this.height = height;
this.weight = weight;
this.phone = phone;
this.photo = photo;
}
// after this i have getters & setters and toString method for above fields.
}
I think you have playmatch relationship within the player end node as well. If you comment the following code in the player node. It should work. I have also attached a json sample to pass from the UI in the match URL (/api/match) instead of (/api/playmatch)
#Relationship(type="PLAY_MATCH")
private PlayMatch playMatch;
public PlayMatch getPlayMatch() {
return playMatch;
}
public void setPlayMatch(PlayMatch playMatch) {
this.playMatch = playMatch;
}
Sample JSON
{
"type": "typename",
"status": "statusname",
"createdTime": 1435928223021,
"noOfGames": 5,
"noOfPoints": 19,
"playMatch": {"challengedBy" : "John", "player1" : {"id":732}, "match":{"type": "typename",
"status": "statusname",
"createdTime": 1435928223021,
"noOfGames": 5,
"noOfPoints": 19}}
}
this should create a new match and a new relationship with property challengedBy to an existing player node with id 732.
check it out and let me know if this works.

MappingException: Error mapping GraphModel to instance

I'm trying to follow the new Cineasts app with SDN 4.0.0.M1 and SpringBoot to learn Spring and Neo4j but I have an error when I try to access the movie url with
curl http://localhost:8080/movies
MappingException: Error mapping GraphModel to instance
I implemented the minimum to get something working so the code is simple but I probably forgot something
the movie class
#NodeEntity
public class Movie {
#GraphId
private Long nodeId;
private String id;
private String title;
public Movie() {
}
public Movie(String id, String title) {
this.id = id;
this.title = title;
}
}
the associated MovieRepository is empty at the moment
public interface MovieRepository extends GraphRepository<Movie> {
}
the MovieController
#Autowired
private MovieRepository movieRepository;
#Autowired
private Session session;
#RequestMapping(value = "/movies/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
public
#ResponseBody
Movie getMovie(#PathVariable String id) {
return IteratorUtil.firstOrNull(findMovieByProperty("id", id));
}
public Iterable<Movie> findMovieByProperty(String propertyName, Object propertyValue) {
return session.loadByProperty(Movie.class, new Property(propertyName, propertyValue));
}
and the main class with database connection
#SpringBootApplication
#EnableNeo4jRepositories("cineasts.repository")
#EnableTransactionManagement
public class CineastsApplication extends Neo4jConfiguration {
public static final int NEO4J_PORT = 7474;
#Bean
public Neo4jServer neo4jServer() {
return new RemoteServer("http://localhost:" + NEO4J_PORT);
}
#Override
public SessionFactory getSessionFactory() {
return new SessionFactory("org.neo4j.cineasts.domain");
}
#Override
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
public static void main(String[] args) {
SpringApplication.run(CineastsApplication.class, args);
}
}
I started Neo4j and added one record with Neo4j browser
CREATE (m:Movie {id:1, name:'The Matrix'}) return m
when I go to localhost:8080 I can see the json response
{
"_links" : {
"movies" : {
"href" : "http://localhost:8080/movies"
},
"profile" : {
"href" : "http://localhost:8080/alps"
}
}
but it fails to display the movies or http://localhost:8080/movies/1 record I just created. Any idea to fix this or get a more relevant message?
Thanks!
The problem could be the fact that your entity definition does not match that of the node you've created.
The Movie class defines a property id of data type String, and a property title of type String.
The Cypher you used however
CREATE (m:Movie {id:1, name:'The Matrix'}) return m
creates a node with a number id instead of a String id and a name property instead of a title property.
Changing the above to
CREATE (m:Movie {id:'1', title:'The Matrix'}) return m
should fix it.

Target state is not the requested interface org.neo4j.graphdb.Node but null

I encountered some difficulties during playing with neo4j. Firstly, when I try to delete defined #EntityModel, I get an exception (Please, forgive me the quality of pics, the exception messages are also in question title):
My Controller (this is just for testing purpouse):
#Controller
public class HomeController {
#Autowired
PersonRepository personRepository;
#RequestMapping(value="/", method = RequestMethod.GET)
public String loadPage(final Model model, final HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
Person person = new Person("My person");
personRepository.save(person);
personRepository.findOne(person.getId());
return "home";
}
}
And model:
#NodeEntity
public class Person {
#GraphId
private Long id;
private String name;
public Person() {}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public Long getId() {
return id;
}
}
Configuration file:
#Configuration
#EnableTransactionManagement
#EnableNeo4jRepositories(basePackages = "com.springapp.mvc.repository")
#ComponentScan({"com.springapp.mvc"})
public class PersistenceConfig extends Neo4jConfiguration {
#Bean
public GraphDatabaseService graphDatabaseService() {
return new SpringRestGraphDatabase("http://localhost:7474/db/data");
}
}
My Repository:
public interface PersonRepository extends GraphRepository<Person> {
#Query("MATCH (n:Person{name: \"{namveValue}\"}) RETURN n;")
Person findByName(#Param("nameValue") final String name);
}
What am I doing wrong? I figured out that perhaps Person should implement org.neo4j.graphdb.Node and this is the source of these exceptions. However, having searched github repos I see that people do not implement this interface in their models. I have not found solution on stackoverflow so far.
Node exists in database but I cannot either delete it or save it. Please, help.
You are trying to see if a node with ID '0' exists as a person. Since the root node hasn't got a '__type__' property, the call will fail. SDN uses this property to determine the entity type of a node.
That being said, the exception seems to be caused by the following line:
if(! personRepository.exists(0L)) {

Resources