Comprehensive developer toolkit providing reusable skills for Java/Spring Boot, TypeScript/NestJS/React/Next.js, Python, PHP, AWS CloudFormation, AI/RAG, DevOps, and more.
82
82%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Risky
Do not use without reviewing
This document provides detailed reference information for Spring Data Neo4j, including annotations, query language syntax, configuration options, and API documentation.
Marks a class as a Neo4j node entity.
@Node // Label defaults to class name
@Node("CustomLabel") // Explicit label
@Node({"Label1", "Label2"}) // Multiple labels
public class MyEntity {
// ...
}Properties:
value or labels: String or String array for node labelsprimaryLabel: Specify which label is primary (when using multiple labels)Marks a field as the entity identifier.
@Id
private String businessKey; // Custom business key
@Id @GeneratedValue
private Long id; // Auto-generated internal IDImportant:
Configures ID generation strategy.
@Id @GeneratedValue
private Long id; // Uses Neo4j internal ID
@Id @GeneratedValue(generatorClass = UUIDStringGenerator.class)
private String uuid; // Custom UUID generator
@Id @GeneratedValue(generatorClass = MyCustomGenerator.class)
private String customId;Built-in Generators:
InternalIdGenerator (default for Long): Uses Neo4j's internal IDUUIDStringGenerator: Generates UUID stringsMaps a field to a different property name in Neo4j.
@Property("graph_property_name")
private String javaFieldName;When to use:
Defines relationships between nodes.
@Relationship(type = "RELATIONSHIP_TYPE", direction = Direction.OUTGOING)
private RelatedEntity related;
@Relationship(type = "RELATED_TO", direction = Direction.INCOMING)
private List<RelatedEntity> incoming;
@Relationship(type = "CONNECTED", direction = Direction.UNDIRECTED)
private Set<RelatedEntity> connections;Properties:
type (required): Relationship type in Neo4jdirection: OUTGOING, INCOMING, or UNDIRECTEDDirection Guidelines:
OUTGOING: This node → target nodeINCOMING: Target node → this nodeUNDIRECTED: Ignores direction when queryingMarks a class as relationship properties container.
@RelationshipProperties
public class ActedIn {
@Id @GeneratedValue
private Long id;
@TargetNode
private Movie movie;
private List<String> roles;
private Integer screenTime;
}Required Fields:
@Id field (can be generated)@TargetNode field pointing to target entityDefines custom Cypher query for a repository method.
@Query("MATCH (n:Node) WHERE n.property = $param RETURN n")
List<Node> customQuery(@Param("param") String param);
@Query("MATCH (n:Node) WHERE n.id = $0 RETURN n")
Node findById(String id); // Positional parameterParameter Binding:
$paramName for named parameters with @Param$0, $1, etc. for positional parameters#{#entityName}Binds method parameter to query parameter.
@Query("MATCH (n) WHERE n.name = $customName RETURN n")
List<Node> find(@Param("customName") String name);When required:
Enables Neo4j repository support.
@Configuration
@EnableNeo4jRepositories(basePackages = "com.example.repositories")
public class Neo4jConfiguration {
// ...
}Properties:
basePackages: Packages to scan for repositoriesbasePackageClasses: Type-safe package specificationrepositoryImplementationPostfix: Custom implementation suffix (default: "Impl")Note: Auto-enabled by Spring Boot starter, manual configuration rarely needed.
Test slice annotation for Neo4j tests.
@DataNeo4jTest
class MyRepositoryTest {
@Autowired
private MyRepository repository;
}What it does:
// Find all nodes with label
MATCH (n:Label) RETURN n
// Find node with property
MATCH (n:Label {property: 'value'}) RETURN n
// Find nodes with WHERE clause
MATCH (n:Label) WHERE n.property > 100 RETURN n
// Multiple labels
MATCH (n:Label1:Label2) RETURN n// Outgoing relationship
MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a, b
// Incoming relationship
MATCH (a:Person)<-[:KNOWS]-(b:Person) RETURN a, b
// Undirected relationship
MATCH (a:Person)-[:KNOWS]-(b:Person) RETURN a, b
// Relationship with properties
MATCH (a)-[r:KNOWS {since: 2020}]->(b) RETURN a, r, b
// Variable length relationships
MATCH (a)-[:KNOWS*1..3]->(b) RETURN a, b// Create single node
CREATE (n:Person {name: 'John', age: 30})
// Create node and relationship
CREATE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})
// Create relationship between existing nodes
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[:KNOWS {since: 2020}]->(b)// Find or create node
MERGE (n:Person {email: 'john@example.com'})
ON CREATE SET n.created = timestamp()
ON MATCH SET n.accessed = timestamp()
// Find or create relationship
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
MERGE (a)-[r:KNOWS]->(b)
ON CREATE SET r.since = 2020// Set single property
MATCH (n:Person {name: 'John'})
SET n.age = 31
// Set multiple properties
MATCH (n:Person {name: 'John'})
SET n.age = 31, n.city = 'London'
// Set from map
MATCH (n:Person {name: 'John'})
SET n += {age: 31, city: 'London'}
// Add label
MATCH (n:Person {name: 'John'})
SET n:Premium// Delete node (must have no relationships)
MATCH (n:Person {name: 'John'})
DELETE n
// Delete node and relationships
MATCH (n:Person {name: 'John'})
DETACH DELETE n
// Delete relationship
MATCH (a)-[r:KNOWS]->(b)
WHERE a.name = 'Alice' AND b.name = 'Bob'
DELETE r
// Remove property
MATCH (n:Person {name: 'John'})
REMOVE n.age
// Remove label
MATCH (n:Person {name: 'John'})
REMOVE n:Premium// Collect results
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name, collect(m.title) AS movies
// Unwind collection
UNWIND [1, 2, 3] AS number
RETURN number
// List comprehension
MATCH (p:Person)
RETURN [x IN p.skills WHERE x STARTS WITH 'Java'] AS javaSkills
// Size of collection
MATCH (p:Person)
RETURN p.name, size(p.skills) AS skillCount// Count
MATCH (p:Person) RETURN count(p)
// Sum
MATCH (p:Product) RETURN sum(p.price)
// Average
MATCH (p:Product) RETURN avg(p.price)
// Min/Max
MATCH (p:Product) RETURN min(p.price), max(p.price)
// Group by with aggregation
MATCH (p:Person)-[:LIVES_IN]->(c:City)
RETURN c.name, count(p) AS population
ORDER BY population DESC// CASE expression
MATCH (p:Person)
RETURN p.name,
CASE
WHEN p.age < 18 THEN 'Minor'
WHEN p.age < 65 THEN 'Adult'
ELSE 'Senior'
END AS category
// COALESCE - first non-null value
MATCH (p:Person)
RETURN coalesce(p.nickname, p.name) AS displayName// Pattern comprehension
MATCH (p:Person)
RETURN p.name,
[(p)-[:KNOWS]->(friend) | friend.name] AS friends
// With filtering
MATCH (p:Person)
RETURN p.name,
[(p)-[:KNOWS]->(friend) WHERE friend.age > 30 | friend.name] AS olderFriends// Create index (admin query, not in @Query)
CREATE INDEX person_name FOR (n:Person) ON (n.name)
// Composite index
CREATE INDEX person_name_age FOR (n:Person) ON (n.name, n.age)
// Use index hint
MATCH (p:Person)
USING INDEX p:Person(name)
WHERE p.name = 'John'
RETURN p// Analyze query performance
PROFILE
MATCH (p:Person)-[:KNOWS*1..3]->(friend)
WHERE p.name = 'Alice'
RETURN friend.name
// Dry run without execution
EXPLAIN
MATCH (p:Person)-[:KNOWS]->(friend)
RETURN p, friend// Limit results
MATCH (n:Person) RETURN n LIMIT 10
// Skip and limit (pagination)
MATCH (n:Person)
RETURN n
ORDER BY n.name
SKIP 20 LIMIT 10# Neo4j URI
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.uri=neo4j://localhost:7687
spring.neo4j.uri=neo4j+s://production.server:7687
# Authentication
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret
spring.neo4j.authentication.realm=native
spring.neo4j.authentication.kerberos-ticket=...
# Connection pool
spring.neo4j.pool.max-connection-pool-size=50
spring.neo4j.pool.idle-time-before-connection-test=PT30S
spring.neo4j.pool.max-connection-lifetime=PT1H
spring.neo4j.pool.connection-acquisition-timeout=PT60S
spring.neo4j.pool.metrics-enabled=true# Logging
spring.neo4j.logging.level=WARN
spring.neo4j.logging.log-leaked-sessions=true
# Connection timeout
spring.neo4j.connection-timeout=PT30S
# Max transaction retry time
spring.neo4j.max-transaction-retry-time=PT30S
# Encrypted connection
spring.neo4j.security.encrypted=true
spring.neo4j.security.trust-strategy=TRUST_ALL_CERTIFICATES
spring.neo4j.security.hostname-verification-enabled=true@Configuration
public class Neo4jConfiguration {
@Bean
org.neo4j.driver.Config neo4jDriverConfig() {
return org.neo4j.driver.Config.builder()
.withMaxConnectionPoolSize(50)
.withConnectionAcquisitionTimeout(60, TimeUnit.SECONDS)
.withConnectionLivenessCheckTimeout(30, TimeUnit.SECONDS)
.withMaxConnectionLifetime(1, TimeUnit.HOURS)
.withLogging(Logging.slf4j())
.withEncryption()
.build();
}
@Bean
Configuration cypherDslConfiguration() {
return Configuration.newConfig()
.withDialect(Dialect.NEO4J_5)
.build();
}
}| Keyword | Cypher Equivalent |
|---|---|
findBy | MATCH ... RETURN |
existsBy | MATCH ... RETURN count(*) > 0 |
countBy | MATCH ... RETURN count(*) |
deleteBy | MATCH ... DETACH DELETE |
And | AND |
Or | OR |
Between | >= $lower AND <= $upper |
LessThan | < |
LessThanEqual | <= |
GreaterThan | > |
GreaterThanEqual | >= |
Before | < (for dates) |
After | > (for dates) |
IsNull | IS NULL |
IsNotNull | IS NOT NULL |
Like | =~ '.*pattern.*' |
NotLike | NOT =~ '.*pattern.*' |
StartingWith | STARTS WITH |
EndingWith | ENDS WITH |
Containing | CONTAINS |
In | IN |
NotIn | NOT IN |
True | = true |
False | = false |
OrderBy...Asc | ORDER BY ... ASC |
OrderBy...Desc | ORDER BY ... DESC |
| Return Type | Description |
|---|---|
Entity | Single result or null |
Optional<Entity> | Single result wrapped in Optional |
List<Entity> | Multiple results |
Stream<Entity> | Results as Java Stream |
Page<Entity> | Paginated results |
Slice<Entity> | Slice of results |
Mono<Entity> | Reactive single result |
Flux<Entity> | Reactive stream of results |
boolean | Existence check |
long | Count query |
public interface UserRepository extends Neo4jRepository<User, String> {
// Simple query derivation
Optional<User> findByEmail(String email);
List<User> findByAgeGreaterThan(Integer age);
List<User> findByAgeBetween(Integer minAge, Integer maxAge);
List<User> findByNameStartingWith(String prefix);
// Boolean queries
boolean existsByEmail(String email);
// Count queries
long countByAgeGreaterThan(Integer age);
// Delete queries
long deleteByAgeLessThan(Integer age);
// Sorting
List<User> findByAgeGreaterThanOrderByNameAsc(Integer age);
// Pagination
Page<User> findByAgeGreaterThan(Integer age, Pageable pageable);
// Stream
Stream<User> findByAgeBetween(Integer min, Integer max);
// Multiple conditions
List<User> findByNameAndAge(String name, Integer age);
List<User> findByNameOrEmail(String name, String email);
// Null checks
List<User> findByNicknameIsNull();
List<User> findByNicknameIsNotNull();
// Collection queries
List<User> findByRolesContaining(String role);
List<User> findByIdIn(Collection<String> ids);
}// Closed projection - only declared properties
public interface UserSummary {
String getUsername();
String getEmail();
}
// Open projection - with SpEL
public interface UserWithFullName {
@Value("#{target.firstName + ' ' + target.lastName}")
String getFullName();
}
// Nested projection
public interface UserWithPosts {
String getUsername();
List<PostSummary> getPosts();
interface PostSummary {
String getTitle();
LocalDateTime getCreatedAt();
}
}
// Usage
public interface UserRepository extends Neo4jRepository<User, String> {
List<UserSummary> findAllBy();
Optional<UserWithFullName> findByUsername(String username);
}public record UserDTO(
String username,
String email,
LocalDateTime joinedAt
) {}
// Repository usage
public interface UserRepository extends Neo4jRepository<User, String> {
List<UserDTO> findAllBy();
}public interface UserRepository extends Neo4jRepository<User, String> {
<T> T findByUsername(String username, Class<T> type);
}
// Usage
UserSummary summary = repository.findByUsername("john", UserSummary.class);
UserDTO dto = repository.findByUsername("john", UserDTO.class);
User full = repository.findByUsername("john", User.class);@Service
public class UserService {
private final UserRepository userRepository;
@Transactional
public User createUser(CreateUserRequest request) {
User user = new User(request.username(), request.email());
return userRepository.save(user);
}
@Transactional(readOnly = true)
public Optional<User> getUser(String username) {
return userRepository.findByUsername(username);
}
@Transactional(
propagation = Propagation.REQUIRES_NEW,
isolation = Isolation.READ_COMMITTED,
timeout = 30
)
public void complexOperation() {
// Multiple repository calls in single transaction
// ...
}
}@Service
public class TransactionalService {
private final Neo4jTransactionManager transactionManager;
public void executeInTransaction() {
TransactionTemplate template = new TransactionTemplate(transactionManager);
template.execute(status -> {
try {
// Your transactional code here
return someResult;
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
}
}@Service
public class ReactiveUserService {
private final ReactiveUserRepository repository;
private final ReactiveNeo4jTransactionManager transactionManager;
public Mono<User> createUser(CreateUserRequest request) {
return transactionManager.getReactiveTransaction()
.flatMap(status -> {
User user = new User(request.username(), request.email());
return repository.save(user)
.doOnError(e -> status.setRollbackOnly());
});
}
}Create indexes on frequently queried properties:
// Single property index
CREATE INDEX user_email FOR (u:User) ON (u.email);
// Composite index
CREATE INDEX user_name_age FOR (u:User) ON (u.name, u.age);
// Full-text index
CREATE FULLTEXT INDEX user_search FOR (u:User) ON EACH [u.name, u.bio];
// Show indexes
SHOW INDEXES;
// Drop index
DROP INDEX user_email;Use specific labels:
// Good
MATCH (u:User {email: $email}) RETURN u
// Bad
MATCH (n {email: $email}) RETURN nFilter early:
// Good
MATCH (u:User)
WHERE u.age > 18
MATCH (u)-[:POSTED]->(p:Post)
RETURN p
// Bad
MATCH (u:User)-[:POSTED]->(p:Post)
WHERE u.age > 18
RETURN pUse projections to fetch only needed data:
// Good
List<UserSummary> findAllBy();
// Bad (when you only need summary)
List<User> findAll();Limit result sets:
// Use pagination
Page<User> findAll(Pageable pageable);
// Or explicit limits
@Query("MATCH (u:User) RETURN u LIMIT $limit")
List<User> findTopUsers(@Param("limit") int limit);@Bean
org.neo4j.driver.Config driverConfig() {
return org.neo4j.driver.Config.builder()
.withMaxConnectionPoolSize(50)
.withConnectionAcquisitionTimeout(60, TimeUnit.SECONDS)
.withIdleTimeBeforeConnectionTest(30, TimeUnit.SECONDS)
.build();
}// Save in batches
@Service
public class BatchService {
private final UserRepository repository;
public void saveUsersInBatches(List<User> users) {
int batchSize = 1000;
for (int i = 0; i < users.size(); i += batchSize) {
int end = Math.min(i + batchSize, users.size());
List<User> batch = users.subList(i, end);
repository.saveAll(batch);
}
}
}# Enable driver metrics
spring.neo4j.pool.metrics-enabled=true
# Log slow queries (if using Neo4j Enterprise)
# Set in neo4j.conf:
# dbms.logs.query.enabled=true
# dbms.logs.query.threshold=1splugins
developer-kit-ai
skills
chunking-strategy
prompt-engineering
developer-kit-aws
skills
aws
aws-cli-beast
aws-cost-optimization
aws-drawio-architecture-diagrams
aws-sam-bootstrap
aws-cloudformation
aws-cloudformation-auto-scaling
references
aws-cloudformation-bedrock
references
aws-cloudformation-cloudfront
references
aws-cloudformation-cloudwatch
references
aws-cloudformation-dynamodb
references
aws-cloudformation-ec2
aws-cloudformation-ecs
references
aws-cloudformation-elasticache
aws-cloudformation-iam
references
aws-cloudformation-lambda
references
aws-cloudformation-rds
aws-cloudformation-s3
references
aws-cloudformation-security
references
aws-cloudformation-task-ecs-deploy-gh
aws-cloudformation-vpc
developer-kit-core
skills
developer-kit-java
skills
aws-lambda-java-integration
aws-rds-spring-boot-integration
aws-sdk-java-v2-bedrock
aws-sdk-java-v2-core
aws-sdk-java-v2-dynamodb
aws-sdk-java-v2-kms
aws-sdk-java-v2-lambda
aws-sdk-java-v2-messaging
aws-sdk-java-v2-rds
aws-sdk-java-v2-s3
aws-sdk-java-v2-secrets-manager
graalvm-native-image
langchain4j
langchain4j-mcp-server-patterns
langchain4j-ai-services-patterns
references
langchain4j-mcp-server-patterns
references
langchain4j-rag-implementation-patterns
references
langchain4j-spring-boot-integration
langchain4j-testing-strategies
langchain4j-tool-function-calling-patterns
langchain4j-vector-stores-configuration
references
qdrant
references
spring-ai-mcp-server-patterns
references
spring-boot-actuator
spring-boot-cache
spring-boot-crud-patterns
spring-boot-dependency-injection
spring-boot-event-driven-patterns
spring-boot-openapi-documentation
spring-boot-project-creator
spring-boot-resilience4j
spring-boot-rest-api-standards
spring-boot-saga-pattern
spring-boot-security-jwt
assets
references
scripts
spring-boot-test-patterns
spring-data-jpa
references
spring-data-neo4j
references
unit-test-application-events
unit-test-bean-validation
unit-test-boundary-conditions
unit-test-caching
unit-test-config-properties
unit-test-controller-layer
unit-test-exception-handler
unit-test-json-serialization
unit-test-mapper-converter
unit-test-parameterized
unit-test-scheduled-async
unit-test-service-layer
unit-test-utility-methods
unit-test-wiremock-rest-api
developer-kit-php
skills
aws-lambda-php-integration
developer-kit-python
skills
aws-lambda-python-integration
developer-kit-tools
developer-kit-typescript
skills
aws-lambda-typescript-integration
better-auth
drizzle-orm-patterns
dynamodb-toolbox-patterns
references
nestjs
nestjs-best-practices
nestjs-code-review
nestjs-drizzle-crud-generator
scripts
nextjs-app-router
nextjs-authentication
nextjs-code-review
nextjs-data-fetching
references
nextjs-deployment
nextjs-performance
nx-monorepo
react-code-review
react-patterns
references
shadcn-ui
tailwind-css-patterns
references
tailwind-design-system
references
turborepo-monorepo
typescript-docs
typescript-security-review
zod-validation-utilities