@@ -65,3 +65,81 @@ val cls: ClassConcept = client.query {
65
65
}
66
66
val className = cls.name
67
67
--
68
+
69
+ == Complex query results
70
+
71
+ While returning a list of elements is simple
72
+ the purpose of the query language is to reduce the number of request to a minimum.
73
+ This requires to combine multiple values into more complex data structures.
74
+ The `zip` operation provides a simple way of doing that:
75
+
76
+ [source,kotlin]
77
+ --
78
+ val result: List<IZip3Output<Any, Int, String, List<String>>> = query { db ->
79
+ db.products.map {
80
+ val id = it.id
81
+ val title = it.title
82
+ val images = it.images.toList()
83
+ id.zip(title, images)
84
+ }.toList()
85
+ }
86
+ result.forEach { println("ID: ${it.first}, Title: ${it.second}, Images: ${it.third}") }
87
+ --
88
+
89
+ This is suitable for combining a small number of values,
90
+ but because of the missing variable names it can be hard to read for a larger number of values
91
+ or even multiple zip operations assembled into a hierarchical data structure.
92
+
93
+ This can be solved by defining custom data classes and using the `mapLocal` operation:
94
+
95
+ [source,kotlin]
96
+ --
97
+ data class MyProduct(val id: Int, val title: String, val images: List<MyImage>)
98
+ data class MyImage(val url: String)
99
+
100
+ val result: List<MyNonSerializableClass> = remoteProductDatabaseQuery { db ->
101
+ db.products.map {
102
+ val id = it.id
103
+ val title = it.title
104
+ val images = it.images.mapLocal { MyImage(it) }.toList()
105
+ id.zip(title, images).mapLocal {
106
+ MyProduct(it.first, it.second, it.third)
107
+ }
108
+ }.toList()
109
+ }
110
+ result.forEach { println("ID: ${it.id}, Title: ${it.title}, Images: ${it.images}") }
111
+ --
112
+
113
+ The `mapLocal` operation is not just useful in combination with the `zip` operation,
114
+ but in general to create instances of classes only known to the client.
115
+
116
+ The body of `mapLocal` is executed on the client after receiving the result from the server.
117
+ That's why you only have access to the output of the `zip` operation
118
+ and still have to use `first`, `second` and `third` inside the query.
119
+
120
+ To make this even more readable there is a `mapLocal2` operation,
121
+ which provides a different syntax for the `zip`-`mapLocal` chain.
122
+
123
+ [source,kotlin]
124
+ --
125
+ data class MyProduct(val id: Int, val title: String, val images: List<MyImage>)
126
+ data class MyImage(val url: String)
127
+
128
+ val result: List<MyNonSerializableClass> = query { db ->
129
+ db.products.mapLocal2 {
130
+ val id = it.id.request()
131
+ val title = it.title.request()
132
+ val images = it.images.mapLocal { MyImage(it) }.toList().request()
133
+ onSuccess {
134
+ MyNonSerializableClass(id.get(), title.get(), images.get())
135
+ }
136
+ }.toList()
137
+ }
138
+ result.forEach { println("ID: ${it.id}, Title: ${it.title}, Images: ${it.images}") }
139
+ --
140
+
141
+ At the beginning of the `mapLocal2` body you invoke `request()` on all the values you need to assemble your object.
142
+ This basically ads the operand to the internal `zip` operation and returns an object that gives you access to the value
143
+ after receiving it from the server.
144
+ Inside the `onSuccess` block you assemble the local object using the previously requested values.
145
+
0 commit comments