Skip to content

Commit 6510b5c

Browse files
committed
feat: Introduce Mapping To Type Support
<TODO>
1 parent 61cd40b commit 6510b5c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1556
-39
lines changed

driver/clirr-ignored-differences.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,4 +633,16 @@
633633
<method>org.neo4j.driver.net.ServerAddress of(java.lang.String)</method>
634634
</difference>
635635

636+
<difference>
637+
<className>org/neo4j/driver/Value</className>
638+
<differenceType>7012</differenceType>
639+
<method>java.lang.Object as(java.lang.Class)</method>
640+
</difference>
641+
642+
<difference>
643+
<className>org/neo4j/driver/Record</className>
644+
<differenceType>7012</differenceType>
645+
<method>java.lang.Object as(java.lang.Class)</method>
646+
</difference>
647+
636648
</differences>

driver/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
exports org.neo4j.driver.util;
3030
exports org.neo4j.driver.exceptions;
3131
exports org.neo4j.driver.exceptions.value;
32+
exports org.neo4j.driver.mapping;
3233

3334
requires org.neo4j.bolt.connection;
3435
requires org.neo4j.bolt.connection.netty;

driver/src/main/java/org/neo4j/driver/Record.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@
1919
import java.util.List;
2020
import org.neo4j.driver.exceptions.ClientException;
2121
import org.neo4j.driver.exceptions.NoSuchRecordException;
22+
import org.neo4j.driver.exceptions.value.LossyCoercion;
23+
import org.neo4j.driver.exceptions.value.Uncoercible;
24+
import org.neo4j.driver.mapping.Property;
25+
import org.neo4j.driver.types.MapAccessor;
2226
import org.neo4j.driver.types.MapAccessorWithDefaultValue;
2327
import org.neo4j.driver.util.Immutable;
2428
import org.neo4j.driver.util.Pair;
29+
import org.neo4j.driver.util.Preview;
2530

2631
/**
2732
* Container for Cypher result values.
@@ -78,4 +83,74 @@ public interface Record extends MapAccessorWithDefaultValue {
7883
* @throws NoSuchRecordException if the associated underlying record is not available
7984
*/
8085
List<Pair<String, Value>> fields();
86+
87+
/**
88+
* Maps values of this record to properties of the given type providing that is it supported.
89+
* <p>
90+
* Example (using the <a href=https://github.com/neo4j-graph-examples/movies>Neo4j Movies Database</a>):
91+
* <pre>
92+
* {@code
93+
* // assuming the following Java record
94+
* public record MovieInfo(String title, String director, List<String> actors) {}
95+
* // the record values may be mapped to MovieInfo instances
96+
* var movies = driver.executableQuery("MATCH (actor:Person)-[:ACTED_IN]->(movie:Movie)<-[:DIRECTED]-(director:Person) RETURN movie.title as title, director.name AS director, collect(actor.name) AS actors")
97+
* .execute()
98+
* .records()
99+
* .stream()
100+
* .map(record -> record.as(MovieInfo.class))
101+
* .toList();
102+
* }
103+
* </pre>
104+
* <p>
105+
* Note that Object Mapping is an alternative to accessing the user-defined values in a {@link MapAccessor}.
106+
* If Object Graph Mapping (OGM) is needed, please use a higher level solution built on top of the driver, like
107+
* <a href="https://neo4j.com/docs/getting-started/languages-guides/java/spring-data-neo4j/">Spring Data Neo4j</a>.
108+
* <p>
109+
* The mapping is done by matching user-defined property names to target type constructor parameters. Therefore, the
110+
* constructor parameters must either have {@link Property} annotation or have a matching name that is available
111+
* at runtime (note that the constructor parameter names are typically changed by the compiler unless either the
112+
* compiler {@code -parameters} option is used or they belong to the cannonical constructor of
113+
* {@link java.lang.Record java.lang.Record}). The name matching is case-sensitive.
114+
* <p>
115+
* Additionally, the {@link Property} annotation may be used when mapping a property with a different name to
116+
* {@link java.lang.Record java.lang.Record} cannonical constructor parameter.
117+
* <p>
118+
* The constructor selection criteria is the following (top priority first):
119+
* <ol>
120+
* <li>Maximum matching properties.</li>
121+
* <li>Minimum mismatching properties.</li>
122+
* </ol>
123+
* The constructor search is done in the order defined by the {@link Class#getDeclaredConstructors()} and is
124+
* finished either when a full match is found with no mismatches or once all constructors have been visited.
125+
* <p>
126+
* At least 1 property match must be present for mapping to work.
127+
* <p>
128+
* A {@code null} value is used for arguments that don't have a matching property. If the argument does not accept
129+
* {@code null} value (this includes primitive types), an alternative constructor that excludes it must be
130+
* available.
131+
* <p>
132+
* Types with generic parameters defined at the class level are not supported. However, constructor arguments with
133+
* specific types are permitted, see the {@code actors} parameter in the example above.
134+
* <p>
135+
* On the contrary, the following record would not be mapped because the type information is insufficient:
136+
* <pre>
137+
* {@code
138+
* public record MovieInfo(String title, String director, List<T> actors) {}
139+
* }
140+
* </pre>
141+
* Wildcard type value is not supported.
142+
*
143+
* @param targetClass the target class to map to
144+
* @param <T> the target type to map to
145+
* @return the mapped value
146+
* @throws Uncoercible when mapping to the target type is not possible
147+
* @throws LossyCoercion when mapping cannot be achieved without losing precision
148+
* @see Property
149+
* @since 5.28.5
150+
*/
151+
@Preview(name = "Object mapping")
152+
default <T> T as(Class<T> targetClass) {
153+
// for backwards compatibility only
154+
throw new UnsupportedOperationException("not supported");
155+
}
81156
}

0 commit comments

Comments
 (0)