Hi Mark, Those are indeed interesting approaches, each with their caveats, and definitely good grounds for discussion. I'll comment inline on each
2015-12-09 1:02 GMT+01:00 Mark Derricutt <[email protected]>: > > > 1. A new PartialRecordMatcher that returns an Option<T> and various > over loaded methods to accept it *only* on the value mapper side. > > I was thinking about this, too. Types can often help solve distinguishing different behaviour. A RecordMapper subtype could express this contract. In order to stay compatible, I wouldn't impose an Optional<T> return value - let that be up to the user. The library logic could deal with both null results or Optional.empty() results, regardless of the formal return type. The Javadoc contract would establish these behaviour variants. It would be elegant and reusable, not only for the value mapper side, but also as a sort of implicit filter for things like Result.map(RecordMapper), or even the group key mapper, where this would act like an implicit HAVING clause. The drawback of (ab-)using subtyping for specific behaviour is that it is hard to combine different behaviours without generating tons of types. Right now, there would be only one subtype. But there could be other kinds of behaviour (like result value duplication or generation) that might be desireable, which would require a new subtype, perhaps. > > 1. Keep the same interfaces, but a new (stack trace less) > AbsentValueException to indicate this invocation has no value - ugly, > not nice to reason with - ignore me :) > > jOOQ has the notion of the org.jooq.exception.ControlFlowSignal, an exception that isn't used as an "exception", but as an expected signal that can explicitly abort / jump out of parts of an algorithm. It is used, for instance, to deal with the size limit of bind variables in some databases. Once breached, the query has to be re-rendered with inline bind variables. I've blogged about this topic here: http://blog.jooq.org/2013/04/28/rare-uses-of-a-controlflowexception So, this wouldn't be an entirely foreign concept in jOOQ, but in this case, it would certainly be a bit unexpected. While this solution would be more reusable than #1, it doesn't seem very idiomatic. > > 1. A new group(...) method set similar to intoGroup but returning a > new intermediate SelectGroupStep with methods such as omitNullValues() > etc. > > Indeed, we could take inspiration with the Java 8 Stream.collect() method, which ships with various overloads that allow for specifying collector behaviour. This would definitely require much more design work. It's much harder to get this right and this sort of functionality would compete with calling Result.stream().collect(...) via Java 8's APIs. On the other hand, there is the idea of a jOOQ Result to behave more like a Java 8 Stream, and we could definitely think about adding additional functional API to transform results, or to make it more compatible with Stream (or even jOOλ's Seq)... > I wonder if any other users have come across similar use cases? > They definitely have. Denormalising nested collections via SQL OUTER JOIN, and then performing re-normalisation in the client is one of the most frequent topics on this list... *Group: Feel free to chime in. This will certainly evolve into a very interesting discussion.* > If using #1, I wonder if there would be any other places were accepting a > PartialRecordMapper would make sense, but outside of intoGroups or intoMap > I'm not sure if there would be? > Definitely. This must apply to *ALL* existing usages of the RecordMapper type (just like #2, by the way). Looking for references, we have: - Cursor.fetch(), fetchOne(), fetchOptional() - DAO.mapper() - Record.map() - Result.intoGroups(), intoMap(), map() - ResultQuery.fetch(), fetchAny(), fetchGroups(), fetchMap(), fetchOne(), fetchOptional() There are essentially three types of applications in the above: 1. Applications where the mapper acts on a collection: fetch(), Result.intoGroups(), Result.intoMap(), Result.map(), etc. In these cases, filtering out records based on the mapper would make sense and it would even be useful. 2. Applications where the mapper acts on individual values: Record.map(). In these cases, there is no filtering effect. The result would remain what the mapper returns (null or Optional<T>) 3. Applications where the mapper acts on "the first" value: fetchOne(), fetchOptional(), fetchAny(). In these cases, the mapper would act as a filter to find the first value that "applies", similar to Stream.findFirst() or findAny() While it is certainly possible to find consistent behaviour among these three applications, I do have a weird feeling. Is it really good to implicitly combine Stream.filter() and Stream.map() semantics simply by introspecting the result of the RecordMapper.map() operation (special return value, or exception)? Lukas -- You received this message because you are subscribed to the Google Groups "jOOQ User Group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
