I read that using a Neo4j internal Ids for external purpose is a not a good practice.
I think I have made a mistake in my SDN4/Neo4j application because I'm using internal ids everywhere.
Every SDN 4 node entity in my application have a following property:
@GraphId
private Long id;
I use this long value as a part of my web application urls.. for example
/products/3245234
where 3245234
is an internal Neo4j identifier for this product node entity.
Is it safe from Neo4j point of view - to use internal IDs in such way ? If no, could you please provide an example how a new surrogate keys can solve this issue.
What Eric has said is correct when it comes to how Neo4j internal ID's are able to be recycled.
We have started to add support to SDN to help developers get around this. We can do this by way of a few examples.
Example 1: When you have a natural ID
Let's say we have a User
domain object and it is uniquely identifiable by a field called email
. We might set up our model like so:
@NodeEntity
public class User {
@GraphId
private Long id;
@Index(unique=true, primary=true)
private String email;
...
}
We can then have a repository set up as such:
public interface UserRepository extends CrudRepository<User, String> {
}
Notice how the last value in the parameterized type is a String. This represents the primary index used for this class.
You can now do something like:
User user = userRepository.findOne("john.doe@email.com");
See how you can just pass in the primary ID for the class?
Example 2: When you need a synthetic ID
Let's say the user we defined above has Tweets. Since there is no natural ID for a tweet we give it one. The best way to avoid ID collisions it to use a type 4 UUID. Luckily Java comes pre packed with UUID and SDN supports persistence of it.
import org.neo4j.ogm.annotation.typeconversion.Convert;
import org.neo4j.ogm.typeconversion.UuidStringConverter;
import java.util.UUID;
@NodeEntity
public class Tweet {
@GraphId
private Long id;
@Convert(UuidStringConverter.class)
@Index(unique = true, primary = true)
private UUID uuid;
...
public Tweet(String message) {
this.uuid = UUID.randomUUID();
// other initialisation.
}
}
So what we have here is a UUID
assigned to any created Tweet. This can then be saved to the database via the converter. The handy thing about this is that there are no extra libraries to install. It is also guaranteed (well, for the most part!) to never have the problems that the internal Neo4j ID's have. The pro (or con) is that ID's are manufactured to be universally unique by your application code.
If you are keen to have the database always generate the UUID then I would also recommend the GraphAware https://github.com/graphaware/neo4j-uuid plugin.
Again the Tweet Repository can take advantage of this:
public interface TweetRepository extends CrudRepository<Tweet, UUID> {
}
You can now do something like:
Tweet tweet = tweetRepository.findOne(UUID.fromString("0f6e7004-cefc-4397-b4d2-078c1370856a"));
A final note; at the time of writing @Indexed(unique=true,primary=true)
may be changed to simply be called @Id
in SDN 5.0.