Skip to content

Commit 9c7c16f

Browse files
committed
docs(modelql): some 'explanation' and 'howto' documentation for ModelQL
1 parent b0c607f commit 9c7c16f

File tree

5 files changed

+106
-16
lines changed

5 files changed

+106
-16
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
= ModelQL
2+
3+
When working with large models you will quickly run into performance issues
4+
when you try to replicate the whole model into the client.
5+
6+
While the data structure for model replication in Modelix supports partial loading of models
7+
you still need a way to describe which data you need on the client.
8+
Loading data on demand while traversing the model also results in a poor performance,
9+
because of the potentially large number of fine-grained request.
10+
11+
A first attempt to solve this problem was to disallow lazy loading
12+
and require the client to load all required data at the beginning,
13+
before working with the model.
14+
A special query language was used to filter the data and an attempt to access a node that is not included by that query
15+
results in an exception, forcing the developer to adjust the query.
16+
While this results in a more predictable performance it's hard to maintain and still not optimal for the performance.
17+
You have to download all the data at the beginning that you might eventually need.
18+
19+
The ModelQL query language provides a more dynamic way of loading parts of the model on demand,
20+
but still allows to reduce the number of request to a minimum.
21+
The downside is, that it's not just a different implementation hidden behind the model-api,
22+
but requires to use a different API.
23+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
= ModelQL
2+
3+
== Independent ModelQLClient
4+
5+
ModelQL defines its own HTTP endpoint and provides server/client implementations for it.
6+
The `model-server` and the `mps-model-server-plugin` already implement this endpoint.
7+
The client can be created like this:
8+
9+
[source,kotlin]
10+
--
11+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
12+
val result: List<String?> = client.query { root ->
13+
root.children("modules").property("name").toList()
14+
}
15+
--
16+
17+
== Integration with LightModelClient
18+
19+
When creating a `LightModelClient` you can optionally provide a `ModelQLClient` instance,
20+
which allows to invoke `.query { ... }` (see below) on a node returned by the `LightModelClient`.
21+
22+
[source,kotlin]
23+
--
24+
val modelqlClient = ModelQLClient.builder().build()
25+
val client = LightModelClient.builder().modelQLClient(modelqlClient).build()
26+
val result: List<String?> = client.getRootNode()!!.query {
27+
it.children("modules").property("name").toList()
28+
}
29+
--
30+
31+
== Type safe ModelQL API
32+
33+
You can use the `model-api-gen-gradle` plugin to generate type safe extensions from your meta-model.
34+
35+
[source,kotlin]
36+
--
37+
val result: List<StaticMethodDeclaration> = client.query { root ->
38+
root.children("classes").ofConcept(C_ClassConcept)
39+
.member
40+
.ofConcept(C_StaticMethodDeclaration)
41+
.filter { it.visibility.instanceOf(C_PublicVisibility) }
42+
.toList()
43+
}
44+
--
45+
46+
== Run query on an INode
47+
48+
If a query returns a node you can execute a new query starting from that node.
49+
50+
[source,kotlin]
51+
--
52+
val cls: ClassConcept = client.query {
53+
it.children("classes").ofConcept(C_ClassConcept).first()
54+
}
55+
val names = cls.query { it.member.ofConcept(C_StaticMethodDeclaration).name.toList() }
56+
--
57+
58+
Or you can just use the INode API to access further data of that node.
59+
This is not recommended though, because each access sends a new query to the server.
60+
61+
[source,kotlin]
62+
--
63+
val cls: ClassConcept = client.query {
64+
it.children("classes").ofConcept(C_ClassConcept).first()
65+
}
66+
val className = cls.name
67+
--

model-api-gen-gradle-test/kotlin-generation/src/test/kotlin/org/modelix/modelql/typed/TypedModelQLTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ class TypedModelQLTest {
106106

107107
@Test
108108
fun simpleTest() = runTest { httpClient ->
109-
val client = ModelQLClient("http://localhost/query", httpClient)
109+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
110110
val result: Int = client.query { root ->
111111
root.children("classes").ofConcept(C_ClassConcept)
112112
.member
@@ -118,7 +118,7 @@ class TypedModelQLTest {
118118

119119
@Test
120120
fun test() = runTest { httpClient ->
121-
val client = ModelQLClient("http://localhost/query", httpClient)
121+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
122122
val result: List<Pair<String, String>> = client.query { root ->
123123
root.children("classes").ofConcept(C_ClassConcept)
124124
.member
@@ -132,7 +132,7 @@ class TypedModelQLTest {
132132

133133
@Test
134134
fun testReferences() = runTest { httpClient ->
135-
val client = ModelQLClient("http://localhost/query", httpClient)
135+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
136136
val usedVariables: Set<String> = client.query { root ->
137137
root.children("classes").ofConcept(C_ClassConcept)
138138
.member
@@ -148,7 +148,7 @@ class TypedModelQLTest {
148148

149149
@Test
150150
fun testReferencesFqName() = runTest { httpClient ->
151-
val client = ModelQLClient("http://localhost/query", httpClient)
151+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
152152
val usedVariables: Set<String> = client.query { root ->
153153
root.children("classes").ofConcept(C_ClassConcept)
154154
.member

modelql-client/src/jvmTest/kotlin/org/modelix/modelql/client/HtmlBuilderTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class HtmlBuilderTest {
7575

7676
@Test
7777
fun modular() = runTest { httpClient ->
78-
val client = ModelQLClient("http://localhost/query", httpClient)
78+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
7979

8080
val modelTemplate = buildModelQLFragment<INode, FlowContent> {
8181
val name = input.property("name").getLater()
@@ -121,7 +121,7 @@ class HtmlBuilderTest {
121121

122122
@Test
123123
fun recursive() = runTest { httpClient ->
124-
val client = ModelQLClient("http://localhost/query", httpClient)
124+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
125125

126126
val modelTemplate = buildModelQLFragment<INode, FlowContent> {
127127
val name = input.property("name").getLater()

modelql-client/src/jvmTest/kotlin/org/modelix/modelql/client/ModelQLClientTest.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class ModelQLClientTest {
7979

8080
@Test
8181
fun test_count() = runTest { httpClient ->
82-
val client = ModelQLClient("http://localhost/query", httpClient)
82+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
8383
val result: Int = client.query { root ->
8484
root.allChildren().count()
8585
}
@@ -88,7 +88,7 @@ class ModelQLClientTest {
8888

8989
@Test
9090
fun test_properties() = runTest { httpClient ->
91-
val client = ModelQLClient("http://localhost/query", httpClient)
91+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
9292
val result: List<String?> = client.query { root ->
9393
root.children("modules").property("name").toList()
9494
}
@@ -97,7 +97,7 @@ class ModelQLClientTest {
9797

9898
@Test
9999
fun test_zip() = runTest { httpClient ->
100-
val client = ModelQLClient("http://localhost/query", httpClient)
100+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
101101
val result = client.query { root ->
102102
root.children("modules").map {
103103
it.property("name").zip(it.allChildren().nodeReference().toList())
@@ -107,7 +107,7 @@ class ModelQLClientTest {
107107

108108
@Test
109109
fun test_zipN() = runTest { httpClient ->
110-
val client = ModelQLClient("http://localhost/query", httpClient)
110+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
111111
val result = client.query { root ->
112112
root.children("modules").map {
113113
it.property("name").zip(
@@ -123,7 +123,7 @@ class ModelQLClientTest {
123123

124124
@Test
125125
fun writeProperty() = runTest { httpClient ->
126-
val client = ModelQLClient("http://localhost/query", httpClient)
126+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
127127
val updatesNodes = client.query { root ->
128128
root.children("modules")
129129
.children("models").filter { it.property("name").contains("model1a") }
@@ -148,7 +148,7 @@ class ModelQLClientTest {
148148

149149
@Test
150150
fun writeReference() = runTest { httpClient ->
151-
val client = ModelQLClient("http://localhost/query", httpClient)
151+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
152152
val updatedNodes = client.query { root ->
153153
root.children("modules")
154154
.children("models").filter { it.property("name").contains("model1a") }
@@ -167,7 +167,7 @@ class ModelQLClientTest {
167167

168168
@Test
169169
fun addNewChild() = runTest { httpClient ->
170-
val client = ModelQLClient("http://localhost/query", httpClient)
170+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
171171
val createdNodes = client.query { root ->
172172
root.children("modules")
173173
.children("models")
@@ -188,7 +188,7 @@ class ModelQLClientTest {
188188

189189
@Test
190190
fun removeNode() = runTest { httpClient ->
191-
val client = ModelQLClient("http://localhost/query", httpClient)
191+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
192192

193193
suspend fun countModels(): Int {
194194
return client.query { root ->
@@ -214,7 +214,7 @@ class ModelQLClientTest {
214214

215215
@Test
216216
fun recursiveQuery() = runTest { httpClient ->
217-
val client = ModelQLClient("http://localhost/query", httpClient)
217+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
218218

219219
val descendantsNames: IFluxUnboundQuery<INode, String?> = buildFluxQuery<INode, String?> {
220220
it.property("name") + it.allChildren().mapRecursive()
@@ -229,7 +229,7 @@ class ModelQLClientTest {
229229

230230
@Test
231231
fun testCaching() = runTest { httpClient ->
232-
val client = ModelQLClient("http://localhost/query", httpClient)
232+
val client = ModelQLClient.builder().url("http://localhost/query").httpClient(httpClient).build()
233233

234234
val result: List<Int> = client.query { root ->
235235
val numberOfNodes = root.descendants()

0 commit comments

Comments
 (0)