|
19 | 19 | import java.util.List;
|
20 | 20 | import org.neo4j.driver.exceptions.ClientException;
|
21 | 21 | 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; |
22 | 26 | import org.neo4j.driver.types.MapAccessorWithDefaultValue;
|
23 | 27 | import org.neo4j.driver.util.Immutable;
|
24 | 28 | import org.neo4j.driver.util.Pair;
|
| 29 | +import org.neo4j.driver.util.Preview; |
25 | 30 |
|
26 | 31 | /**
|
27 | 32 | * Container for Cypher result values.
|
@@ -78,4 +83,75 @@ public interface Record extends MapAccessorWithDefaultValue {
|
78 | 83 | * @throws NoSuchRecordException if the associated underlying record is not available
|
79 | 84 | */
|
80 | 85 | 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 | + * @see Value#as(Class) |
| 150 | + * @since 5.28.5 |
| 151 | + */ |
| 152 | + @Preview(name = "Object mapping") |
| 153 | + default <T> T as(Class<T> targetClass) { |
| 154 | + // for backwards compatibility only |
| 155 | + throw new UnsupportedOperationException("not supported"); |
| 156 | + } |
81 | 157 | }
|
0 commit comments