From 8ca5b11aecd4e6713afa4fe5906247f84f940aed Mon Sep 17 00:00:00 2001
From: Leonardo <leomilho@gmail.com>
Date: Wed, 15 Jan 2025 18:06:32 +0100
Subject: [PATCH] feat: frontend inclusion

also had to refactor so that neo4j, redis and rabbitMQ are not globally exported. Also used the opportunity to run prettier
---
 bun.lockb                                     | Bin 64603 -> 64603 bytes
 eslint.config.js                              |  16 +--
 index.ts                                      |   2 +-
 {src/neo4jConnection => neo4j}/index.ts       |   4 +-
 package.json                                  |   8 +-
 {src/brokerReader => rabbitMq}/index.ts       |   0
 .../rabbitmqBroker.ts                         |   6 +-
 .../rabbitmqConnection.ts                     |   2 +-
 {src/redisConnection => redis}/index.ts       |   2 +-
 src/index.ts                                  |   3 -
 src/logger/logger.ts                          |   3 +-
 src/model/graphology.ts                       |   2 +
 src/model/index.ts                            |   3 +
 src/model/insight.ts                          |  20 ++-
 src/model/query/index.ts                      |   4 +-
 src/model/query/logic/boolFilters.ts          |  37 ++++++
 src/model/query/logic/general.ts              |   5 +
 src/model/query/logic/index.ts                |   2 +
 ...Aggregations.tsx => numberAggregations.ts} |   0
 .../{numberFilters.tsx => numberFilters.ts}   |   0
 ...numberFunctions.tsx => numberFunctions.ts} |   0
 .../{stringFilters.tsx => stringFilters.ts}   |   0
 ...stringFunctions.tsx => stringFunctions.ts} |   0
 src/model/query/machineLearningModel.ts       |  29 +++--
 src/model/query/queryResultModel.ts           |   2 +-
 src/model/query/statistics/graphStatistics.ts |  71 ++++++++++
 src/model/query/statistics/index.ts           |   2 +
 .../{ => statistics}/statisticsResultModel.ts |  61 ++++-----
 .../statistics/tests/attributeStats.spec.ts   | 107 ++++++++++++++++
 .../statistics/tests/getAttributeType.spec.ts |  65 ++++++++++
 .../statistics/tests/graphStatistics.spec.ts  | 121 ++++++++++++++++++
 .../statistics/utils/attributeStats/array.ts  |   6 +
 .../utils/attributeStats/boolean.ts           |   9 ++
 .../utils/attributeStats/categorical.ts       |  15 +++
 .../statistics/utils/attributeStats/index.ts  |   7 +
 .../utils/attributeStats/initialize.ts        |  45 +++++++
 .../utils/attributeStats/numerical.ts         |   9 ++
 .../statistics/utils/attributeStats/object.ts |   5 +
 .../utils/attributeStats/temporal.ts          |  10 ++
 .../statistics/utils/getAttributeType.ts      |  76 +++++++++++
 src/model/query/statistics/utils/index.ts     |   3 +
 .../statistics/utils/updateStatistics.ts      |  45 +++++++
 src/model/reactflow.ts                        |  17 +++
 src/model/saveStateModel.ts                   |   3 +-
 src/model/schemaModel.ts                      |   6 +-
 src/model/utils.ts                            |   5 +
 src/model/webSocket/graphResult.ts            |  10 +-
 src/model/webSocket/index.ts                  |   2 +
 src/model/webSocket/message2Backend.ts        |   2 +-
 src/model/webSocket/message2Frontend.ts       |  10 +-
 src/model/webSocket/model.ts                  |   1 -
 src/model/webSocket/policy.ts                 |  20 ++-
 52 files changed, 795 insertions(+), 88 deletions(-)
 rename {src/neo4jConnection => neo4j}/index.ts (92%)
 rename {src/brokerReader => rabbitMq}/index.ts (100%)
 rename {src/brokerReader => rabbitMq}/rabbitmqBroker.ts (97%)
 rename {src/brokerReader => rabbitMq}/rabbitmqConnection.ts (97%)
 rename {src/redisConnection => redis}/index.ts (98%)
 create mode 100644 src/model/query/logic/boolFilters.ts
 rename src/model/query/logic/{numberAggregations.tsx => numberAggregations.ts} (100%)
 rename src/model/query/logic/{numberFilters.tsx => numberFilters.ts} (100%)
 rename src/model/query/logic/{numberFunctions.tsx => numberFunctions.ts} (100%)
 rename src/model/query/logic/{stringFilters.tsx => stringFilters.ts} (100%)
 rename src/model/query/logic/{stringFunctions.tsx => stringFunctions.ts} (100%)
 create mode 100644 src/model/query/statistics/graphStatistics.ts
 create mode 100644 src/model/query/statistics/index.ts
 rename src/model/query/{ => statistics}/statisticsResultModel.ts (56%)
 create mode 100644 src/model/query/statistics/tests/attributeStats.spec.ts
 create mode 100644 src/model/query/statistics/tests/getAttributeType.spec.ts
 create mode 100644 src/model/query/statistics/tests/graphStatistics.spec.ts
 create mode 100644 src/model/query/statistics/utils/attributeStats/array.ts
 create mode 100644 src/model/query/statistics/utils/attributeStats/boolean.ts
 create mode 100644 src/model/query/statistics/utils/attributeStats/categorical.ts
 create mode 100644 src/model/query/statistics/utils/attributeStats/index.ts
 create mode 100644 src/model/query/statistics/utils/attributeStats/initialize.ts
 create mode 100644 src/model/query/statistics/utils/attributeStats/numerical.ts
 create mode 100644 src/model/query/statistics/utils/attributeStats/object.ts
 create mode 100644 src/model/query/statistics/utils/attributeStats/temporal.ts
 create mode 100644 src/model/query/statistics/utils/getAttributeType.ts
 create mode 100644 src/model/query/statistics/utils/index.ts
 create mode 100644 src/model/query/statistics/utils/updateStatistics.ts
 create mode 100644 src/model/utils.ts

diff --git a/bun.lockb b/bun.lockb
index ff60b54747a65e9deaa18ce00b1089ed3bf2c76a..b1ce2cd440ec2c66722e45c4062ef2c1c528fcb9 100755
GIT binary patch
delta 11422
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
P`?PO8_k~2KzFGg@5lrF6

delta 11554
zcmeHNdt4M(maZyjq=ZIL(MB5q-=eh8A`K!PU-&8nQ4t?#Y@oqz0eLDI4Q<p>#~2?+
zC($HkH4h^(M#RMT6OCeGl9}veG{#Ianao6@*|=+FHKWOVryhyfHJklw|JdKukB@V|
zd(XXf&bjB_s_wp5Zu?!i?YDkaeUTst?FGSC=`QqCp*%>`cm$*w^}mG-g**!x3|Z$Y
zDe+WQ3%&JrvtBRi@^2U;2zoOhS--N@%fkxQRVAK$$WQzPAq@3rAgzK>@B7|g5JFIq
zZPGgC*Ax}GD+R&hcDo&e6NPtBFQa`tI<q0J(u#5(>dOGFKCO*r=s-vw=L+gs?;K=D
zNMqbWcYaMV#wb<I5rh<!!-RUlhrxxmP;|q>vT8xN0`r+WVLlI3;$Bb&6(JFv>tl4;
zACe6#bi2#r5WBb0uN`<ZB-h(u4fBnV+;2G~{(J|}j;_9qkiF_r@KnVWm(0%>{Bbu{
zT!{w!`8>sC^DEtguvq6sp0dKYnsOl(@#FS*U6#j{%r9_N!-^75si#_q$9!^o3I4GI
zr@(pKq7qkiwYyN5=dN484wZI5{8@1p{;{R*s+wv~NtNKPs;F^S)(LYFKkn$RDz9|I
zknzw%AKz!FXAfNk^DE0RkQ4RnQQJ;heJnVKJ_-_lzALEb@#8ydQ&sM&o*M@%7S8q1
zD^hrUrA3R(Tvt^bj4Un&=YB&XH2u|(Y=_IM+AXy2A_(~NeTRC!PfevKj<c-1rmU`D
zexcip&YaUc2<Py1bY{JGAiF`Hg5>(mNI=N?9~53g19yB85`VtU_{S0aQ>5m&w<@mA
zRazprDvQfql~wK!P|uDBM``kVjLrjZ1?K?qw7L+`)fk8MY9QH=&C!Ao4w(lY!8zze
zfh{v4T!Y@w@#R6nN#C08f`Bo7Q9T3!!SOjDdqOs$o(HaSmoDH%^IvmsGL+bm1HCkd
z?m;rouXGi-5wYstsOKC!i~=WTa34*f)LmKZw*Q!WJJFFNA@tQEF%6P$`~oCT?UQ<Y
zk*lh@0x41HuBq}A)d`s}kR54&<QtE|KX#xD9KrMThi*OJH~}Rda0w*eC=!xGRfkx^
zP~o`&+P<+Kl5e;ilJ#qK`9Yi@AiaEVK?Xsdf<#b#$84H(R~5KQT!rp}5>Jf><CPB7
z45~{=PUjpJf^b8g`xunDV}87*@ERl!cngwm_>x|ag`h@kY2@%eD_wg&p!02~HC-}&
z?N8A`8Kw+qN_4Z6Lpmc+>>#I{j{Pwml_FP~ehrpNaS^HVrznk8>nqwhq<#{`COS<X
zNf44Ku2ZV~97?HLw-AT?Ay|sWIy&SI*g|y{;*c`@$U8WR<^%;w%TNq*N;mzeA;>Am
z_zMDN66Q1pIZTtm6e<l$HEl!5#U;sXqK06nlxLz=h^;1yH9O@$m|%l;uV9B{4xk3J
zQyLRMtq|)2C^p0?-3lOYh*KVqop*$)nE-zuZ$qu9d=HhHUtDPkByT&XJSz~J1vE{>
z&ORMTt?iuB#XyP;bxL=j0&!BN1_-$=wL*LzM6qE`DKnV75Wfkg28cI<sWr?gOIT?`
ze=yK#CU1MERBNV&_D*>Z+D2<_VGj8g*bp#3^|omt<n7>;o(Z7_h)W^V3Sn(Wu^pXq
z9#RX-2(#4K!6Cl`#v$;h1hYd538mJKPSf~M_&~3=PnA}Mk~iEbALR;Y1<+0O`4)`(
zn27xz9Y(R8obqz))S3z^<<nrvKd5{gMy*guMeebR!1*E9fpPQ#c@+6AF!t16wW<v=
zkM96)!W~jt2a2^g<yus7cRy9-4KQ}!j}k&1@?E`yq`G16NU;%4xe|+u$23u6utVMh
zHW;j}%C3WDg89?U5Qk|9cB&y%8lEaW8BX3VPWb{VhM_r-o8=CjwBW+HV25b}*hDpR
zpGAo+YC{#6f>toL2sXf>G^|w47}y6^561EEqnpU;--6jyO%A6YHVcih=3t%q^F+!%
zuu;%#qnddJELpwtU6lCFszve`?4Nuuq(8<ZFfC~s+dHIpBgos$DG$KIBu~2~*5)d(
ziS%k%s(cBhL~WojhZNkEyirbRR99+<a>~E%YAj5+XF3ZuT<sHJ6@(o0Q8P%IX{Cl}
zr@RA|oPYvVU_L$q8?Lb^hZGq}-WaFkisXcse}hetV~bcIw%>s9L}-!8?q;OpO~hg)
zSTfo)zfOQ@{dg8EcvcKkV=)aS_Fgr|^gP%Qx`BP=DoPI2hp1`VF`8m~I^~RLK^TWh
z)l8`&ni_gykw;T2+R|bKVG`O9RK%hohP=I;rrW5TN~NKxrayE?!m6d2cr;B>ORu3c
zS}lEp(rhltc|A2dwLI7b#sO|ajiC<dYESa^amt<XDA7^{6DG~-MXi0D(y3k)+t+Cd
z=&j~rRH{4-CGLt8GCSl)!G@{>aSELW8wG~k#*~NSk;cJqqb6Gx823X!ke189I90R_
z=~N$zjdhwj^u<<9ul7up3sK^zV$;SB+tim@W1aFHR6c}Ci7NUyq~w0&?eCQ4^`nOV
zPSf^&dO+m=M2YW;uE>vsSR)h*QJScR;v7nBrJ%04G(3LzR)}wqLtYEU>t9rD{swF!
zm`sh>htda7Yn;=x7Z1MibfaUc{4GkO(X1LI4~x^XD_mVoOTn~ZZ^k<08(>qRsja(_
zcr>!}+9tCUj6)cp2JM{Av=I8?Vad6sWo0TDugO4Fa{<_o;oS=sk2Y+7eI4>AVEiOh
z?<Wn2CvO5m6;BNihvTUgLP(%kyHgsTKwgNN1ZuD+QGAd;znJoKSHSa;pSwZ;&|c@*
z&;&gE1Z*nm^&rXpu$8F&u_34ui(i#pAh|!5m&&nt)ayQ3uPPwFRRv_Xda;xM<gI!=
zNb<l)D^(9kpk5D>tcRIXFP3aD=10Ba1WY*|z#M1wV%Y=0)}lsYrdlfqEG3%d=!_Qu
ztTDaze#yf=46xZQz5OSWY8+AZ+>o!gvgBR`0QYhOe6i&EB3%~iaxNraEV<nSaKCu~
zUo5%4gbA((NczkX7Q+E4S2Z-refV9HFP0KTCU>r9t%U%$)&hLpFS))>t<@x3@(957
zi}iYz%$Ml$QN5m}Kk%5&@0YB<3@@u{6AHg5`Nk`Bz568(^eerdr3|doIZJL|uXC0>
z&PJVY<omM%=@s`&uHOW3gr3yf@0UE#7QLQjFz_PK2G|X7`yPNVmTcGofX6)u@O6;=
z=ZZtRJPc_9P5?Z>DS$7Qtan<M&AL1T$=Cgo_09s^|C}ycAi4emz?UKGxxfu?aV;*E
z-0>n4Tr7F7cwd*7Avq!+0o?B@z!yvI_eX#u@E3ru2TAUKO>M;z(+jHP0b8|(pGfX-
z17P{J-hRL2em4O&;6DK7w*Y~z4vDH~#*@Qt{2y+4#*SC7n$sOO=Zhtq`Jk<i+qs_I
z>;te~KY*_XN!A;nwrY|c{c+1PcDx>%#y___1oWR<-aogz|L<E~+%O;ATcFVGVP?u%
zsEB^FePJ%WJlssCT17Nbc5N<A8)2r`!P<~mmrH>o&E&3AM7$7#9Rv$sq=-S}T9id|
zMupR>!xYg>cZTJXCEZN3hAUz_+BH0v&V!jpC}J2*ACXJ7qs?>-tOLm-bE)qbGnI~1
z#Be$Ub_pzUlp=N}&!}8lHP%cQ!6GOkJ(rTlnQ2M7B6g(~urI*kMk`_@)s4=jr^cJ<
zDp(Zt8<R_86*H|Jqlhtd8SFM#+E_*GL955YzYH_o0P965<KW)}Gd(*_5&O_Ju)s_+
zO&G6;{b<X0_y=|mtUrxYa%pasnO;>CF^=wlSti223`HDByE5P(n0bOCCeZW=@Gl$w
zfej)#6aGztf0>Fnm=1wm0*lO2L<f1Y;NN8U2bMw+6XD+!_%~4zQ>g{)3$VCsMNFf*
zZ1|T0|G*xiev{zeRQNYZ5r@-du-jm1lNE6!t)2}3a^W9XI;Bj3f79UK6h$0E*T4d&
z!@nFw97kJn;2+pMFoniVg?}^P-&94MKzG0_GvQyZB4*L9T=)lOo~DS|G<_QU%Y%Pl
zlS!Tq|7O9z>57;`hrlj@Mb1#fT=L9-f3x8q*mR1R3I863e=`+vCbfWl0T!31h_k3J
z5B|-8e_#(&zgh6l1^;F#qKhtr-3CjWt%wD*dN%yahksyhN_iOm6~MoT6|tDEfdv-A
zzd4HNp)GUZAJ{#xc{I)i|J?A;rHG|;2h36g|MC@aKJChfe_-YUMXaFd1@NyJ{()7I
zTnPW>!oNaAtf50-m%t+3inx$GZusYce_(YKQ3U_I@UKV_AE6eoFTmo86>$mG6~n)I
z@DHq>`pt!ZCGc;qA}*!NV7I~2Jc_uCR(s%IDf|OlK`CDNR|fyQinx-lfd$TofAbV^
zHEo#(|G@5nt)X!x@UI;Hl_=s`x&vmZfPbZmxQ=#}!ap!`nIdkW>1FV*68?b^$@Ae~
z75tm8h@0sU*d?&Yaz%WSJmv7O8vcQ8p@<6jR|Eel6!B?l0s8_hu2K=7rMgP^w*dZu
zJxBej;NL>{SEY#C=`z@Du(WDLe4bWU!#{jgcmwQ3N~wW=b?~o75qHuxu)sy|Z-FB2
zqAd%unrOu)iRLbjmhQ?;k1qRGq?3!j#`moLX5;sdC-8-sOiP!oQ)>@&*1kYK1sU43
zblJP2TJ6<;I~dsw-y5TPo=D}(f~9AAV$APEv+;fR@B0fvG@6^mroO8VMvHd(^yLm|
zDf&y(3y}QM#4l1i^*Vlq=Q@8N8sO_Cfb00pypa-ibr72<eOGbVH{kpp$}hzq0#_)k
zA*((erP06`0N*<BcR<2;Kmjs<2|y;01xy68fl0t*0H5#$d{>~p#__9QFQ7NTAF=VJ
z82`q{KZ)>n_WX6Z`d%9f83yp1FW!Rdg^y9-_jCUAvKm+eJPteotN@k+O93CipV8`o
zML-R(0H_A|y|MzR1WJK2fIpEI0EK`Hm<{0j4`BwtpQNV&x%`o8D+*5o&jQ<k?EuG+
zV~hz=UvB$Cb_1e;NC1=0zfkZmV!|iDr@)_q&w)Py{K4WgU_Gz_;LmoC0sP^UKaeg2
z_=Bq#_g6nNyCEM2<^YYrexN(h1Nai)4;p_3z5q4?OMrPm5pWnd0vrR51J{6J;7#Bx
zZ~{09oCo+5)G43^I1NNJ<KL6OGN1{F0geI(DQ5S;dS<(T7XZF3n-mQoz11&Zf*~b9
z)MXH4TR;Z<fk41dFSmgV08BdPb_1LhoF$wsoGBbv@Om{>+JhJY=ak~qvH)Fx2%Yof
z@Wey`9CXeM&JZ@3ou~phJIaCi0LP7EUI@5=IRM9hHZTj|?3jf7P}cwtt^nhJ(Lg#d
z5*Pst2ZjPkz+fN|7z88$@xVZ!KM)J_0KUi7hlan}xxNorZ-6ByunmX<>;U&p1{?sd
z{~<sskOn*iIDuinC}1ow24Ks$JRabB#zY_sm;ht~*+333nPWQz1zr*GQvEK8U6}z)
z2l9XdARp)ja6C8+>^iS5cGC;Y1=zh}zyp*3^8j9j9LEZP`>{?n!1KZL@C2{C4Jhzr
zEd<s9JSfL$5x^73$~<{b0JQ)web(gxjW(`l&siF6kD|;GTnzAP<MDVL#u9)dy$1Ie
zRsz2QRshVG0zTj|U>UF+;DH_oRsm~()xfWTwSaL`&aV57G>l_|*+Dj#jWFt;N4b78
z{t>VVV9S03>;V1+cn;VKYyqAEo&=r+o&lZ)wgKA#u46r(Q<ltM1YQPS0(R<>%hUGa
zAFe4asvyVS7`#NU-5b%h11dt``N2tJKFZvjk{&3|j<+Sm+Xf3UbZTz|UOunv9fTLe
zc8!C&a(0D6cXG<hmK%9Xzmizjo@h%JX3(t02)l8Xq;-36|C@6rRHHr7=CH+UOAbrf
z=lb$OeDPg-C0gvZ1Y114ea@QnHw+tu#+8q!jSn5{{!|jtVsoHY6x9cZZ<zc|!Jg;)
z`*Dk%#}mvVbvtiKGES=;U3XyUk@uVTvqF-pV4PFAb17xUmKSHMgKAv|iBxjlB1MSw
z)p-kTJKrVAIOCGFBO%M<S+PM92ia28QH)bCM<%U#uPSkriN}WtFhJ|BkMdJt)z7&2
zwtltz-RyOLl_YVn&927b6F=J6Sd{dMpZ246@BMv-&djU0@}q)rD(1-V{*wQ4)&7+T
zRDx{~+mdY}+dhl*j)}(ZvrFd!XffE80QwH%%K+ND&yr*ug}M24uM;~4C0*2`j4U<|
z%j{_UzU|)^eW0kr#`Cc12zCjh+nemtD4BL2w@7(1`I;<JjZ9mcVyA7EHESW%U&x>9
z<r~m{g*!h~d<j*YJjOAGs-rot?2+2t#PEsQL}@1-1mCBtu2eT>UI<iM5-|5dO<S_=
zw^)rM5)lEpJ-hZ@un7wBTJSfwrHT73NyeFsPNwORnUTv{Br(O7pa#=8wNaHZFK5ok
z$5yCa?Y2R9%C)6!=xUt%7?Pk&>9X{XyP?3Dg?JbzLvDT8BYgX=&mK~{CUT1qMA!FQ
zBy$i2yq+ON1<{n(?McSLmM6CF%elAY-5jVVVjzUiI0EzZmY&Ba(9T<EN!C0cA4ErA
zFR~hEY$DEYkgu-T`wA2WA%$RMNf0^TuuF@Bs1jmj5WVz<JxM(rgosY<GWNGoH=E$C
zVX|?2sO(P4?FrsVT~!;BI2W6PDD=3+YMes~4{bcMXkFP;YFC)aC!1)*0lO3vObZU!
zt;YGKlOccIHKwVMCyQ4V9$<;Vbmo9P$v6O2d*~bd3OtTY#>%lJVXMM3zb&k5YN_8i
zqIh>Ny(_|ot^*EQtUs?{3!ym&Ez<H3+IGrfHO@x0tX&+^S^9mgBqEw>`29m^&q2G@
zIO8-m`>jk@=>BuK6V^35lNv^M4`x`6!%hQtI#2#(Mg9^fU;tJ))t(9uWmx%?6Ynae
z{kr8hY|78#MXlXpZby3ikX`z$<Ho}lNu4GO{rOO6aHv=vt{pIH+lev{Tdc-8sCj9d
zR#m+J5gUPp%=f?$j~&jCu5_aJFh+c5YCUY1iY?Ubh()TjQ1TJGG$evbz*a}lua4N0
zekKhNIpZ+X<yF1EbIx2PtB$Ct*uD#Wb;NFcV6LiL!e%QCJ-T1|Ad>$5s9n<1K>AZ8
z4LfGH8b^{|iJ7+J+ZEsU#IT(Eust}6o;YSnGR`V}85|uE^jI%D6gWUoFpg23zm~YW
zX79(>(Sr46OBgI{j;4=}{ctCQF7|kA+jV$=;k68hnrJrmj?C_Kve{xa4*G03(A@0l
zp0G+1lWhrDxp+6x_Lw!@>G5WZ)X<$iId1<SlDDZltv_R#Xq<>Dy8O_-oVeuAL&Xz#
zqFQiY<NV&CDZWQu3w`-^sQAkpmS&_B)r0n)uvm>#Rl6dup8x&6^uPURt7atKJu#r?
zFAu7Xre~Bsw)DdYU8Ko9>7A2m^rTaEN%!mLH`V{%;!MM{)|Mz3Ck&^b_{#Lm$2a<7
zf_Q-<w%YPaGEQZ!Dcsa}^uv}v>I&E^jPqP420Z;?YK~<*T6pneJHkG69cCTqMbW42
zR^z}}&YbJtf4km=kl+^BxcKN8m7TU&je}$U)9h0&1~i<7!eGsY;6AkDbcQ7KrLRw0
z9$*Taip@qaj*_ix({I~^=lAVYO-Zn&;1fq*ayHw48bRv=pE2t8_LIq!96jh(bFA~1
zr|aiu9T}Q;MxUYB|HZ@y^{0=|Sdxs7^7hrg`#iVx{V+I`VjIjcGY+|hhm4z1F!v8b
z(84o`mL>gZz*!64#nR6%O)}23-769ImUkcZp{k}%j&bTOt@ypd#VftMn(h21k|3-f
zKzGkttj009KVQv0=eZF%n-$b{;Zz*OziCM_&c|IZoRM>N=*dw~NXA59xf>_wcF#}P
zU-DJKHniv)!7&>>`liKd9GS~`|9a0w^FyaYf%gM+)y9@;2hiKlGmdX&4m`ah;zDyr
z=;=?%+5sESStM0EJPQ4cGj@+$FTS{{{g>Uf*Q!By<$EcfGS69(jKg;u2F_U9`}MRG
zNqp3f1h?CgjpKOo{Go5QW^X-$7VODMSQW3u(=)h>CTL2_VrjojziSyo{V&W4t@4zX
pm$>89H`KV&nra&Makr*_z5Q<zZNAu*j=vS$^x6g4M$LbJ>K{wG85{rr

diff --git a/eslint.config.js b/eslint.config.js
index 3c5557f..7a97a5d 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -1,20 +1,20 @@
-import js from "@eslint/js";
-import globals from "globals";
-import tseslint from "typescript-eslint";
+import js from '@eslint/js';
+import globals from 'globals';
+import tseslint from 'typescript-eslint';
 
 export default tseslint.config(
-  { ignores: ["dist"] },
+  { ignores: ['dist'] },
   {
     extends: [js.configs.recommended, ...tseslint.configs.recommended],
-    files: ["**/*.{ts,tsx}"],
+    files: ['**/*.{ts,tsx}'],
     languageOptions: {
       ecmaVersion: 2020,
       globals: globals.browser,
     },
     plugins: {},
     rules: {
-      "@typescript-eslint/no-explicit-any": "off",
-      "@typescript-eslint/no-unused-vars": "off",
+      '@typescript-eslint/no-explicit-any': 'off',
+      '@typescript-eslint/no-unused-vars': 'off',
     },
-  }
+  },
 );
diff --git a/index.ts b/index.ts
index e6918d5..8420b10 100644
--- a/index.ts
+++ b/index.ts
@@ -1 +1 @@
-export * from './src'
\ No newline at end of file
+export * from './src';
diff --git a/src/neo4jConnection/index.ts b/neo4j/index.ts
similarity index 92%
rename from src/neo4jConnection/index.ts
rename to neo4j/index.ts
index 008c4c9..feca82f 100644
--- a/src/neo4jConnection/index.ts
+++ b/neo4j/index.ts
@@ -1,6 +1,6 @@
 import neo4j, { Driver, type QueryResult, type RecordShape } from 'neo4j-driver';
-import type { DbConnection } from '../model/saveStateModel';
-import { formatTimeDifference, log } from '../logger/logger';
+import type { DbConnection } from '../src/model/saveStateModel';
+import { formatTimeDifference, log } from '../src/logger/logger';
 
 export class Neo4jConnection {
   private driver: Driver;
diff --git a/package.json b/package.json
index 13083f9..81d8d74 100644
--- a/package.json
+++ b/package.json
@@ -5,9 +5,13 @@
   "main": "./index.ts",
   "description": "",
   "type": "module",
-  "files": [],
+  "files": [
+    "index.ts",
+    "src"
+  ],
   "scripts": {
-    "lint": "eslint src/**/* --no-error-on-unmatched-pattern"
+    "lint": "eslint src/**/* --no-error-on-unmatched-pattern",
+    "type": "tsc --noEmit --skipLibCheck"
   },
   "devDependencies": {
     "@types/amqplib": "^0.10.5",
diff --git a/src/brokerReader/index.ts b/rabbitMq/index.ts
similarity index 100%
rename from src/brokerReader/index.ts
rename to rabbitMq/index.ts
diff --git a/src/brokerReader/rabbitmqBroker.ts b/rabbitMq/rabbitmqBroker.ts
similarity index 97%
rename from src/brokerReader/rabbitmqBroker.ts
rename to rabbitMq/rabbitmqBroker.ts
index 4b57d9c..dd9dcc2 100644
--- a/src/brokerReader/rabbitmqBroker.ts
+++ b/rabbitMq/rabbitmqBroker.ts
@@ -1,7 +1,7 @@
 import { type Channel, type Connection, type ConsumeMessage, type Options } from 'amqplib';
-import type { BackendMessageHeader, SessionData } from '../model/headerModel';
-import { formatTimeDifference, log } from '../logger/logger';
-import type { WsMessageBackend2Frontend, WsMessageBody } from '../model/webSocket';
+import type { BackendMessageHeader, SessionData } from '../src/model/headerModel';
+import { formatTimeDifference, log } from '../src/logger/logger';
+import type { WsMessageBackend2Frontend, WsMessageBody } from '../src/model/webSocket';
 import type { RabbitMqConnection } from './rabbitmqConnection';
 
 export class RabbitMqBroker {
diff --git a/src/brokerReader/rabbitmqConnection.ts b/rabbitMq/rabbitmqConnection.ts
similarity index 97%
rename from src/brokerReader/rabbitmqConnection.ts
rename to rabbitMq/rabbitmqConnection.ts
index f6b616f..cbcb839 100644
--- a/src/brokerReader/rabbitmqConnection.ts
+++ b/rabbitMq/rabbitmqConnection.ts
@@ -1,5 +1,5 @@
 import { connect, type Connection, type Options } from 'amqplib';
-import { log } from '../logger/logger';
+import { log } from '../src/logger/logger';
 
 export class RabbitMqConnection {
   private connection?: Connection;
diff --git a/src/redisConnection/index.ts b/redis/index.ts
similarity index 98%
rename from src/redisConnection/index.ts
rename to redis/index.ts
index ac3dede..83ccbe4 100644
--- a/src/redisConnection/index.ts
+++ b/redis/index.ts
@@ -1,6 +1,6 @@
 import { createClient, type RedisClientType } from 'redis';
 import { lock, tryLock, type ReleaseFunc, type TryLockOptions } from 'simple-redis-mutex';
-import { log } from '../logger/logger';
+import { log } from '../src/logger/logger';
 
 export type Routing = {
   queueID: string;
diff --git a/src/index.ts b/src/index.ts
index 61ea6d8..767a4e9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,6 +1,3 @@
-export * from './neo4jConnection';
-export * from './redisConnection';
-export * from './brokerReader';
 export * from './model';
 export * from './userManagementServiceAPI';
 export * from './logger';
diff --git a/src/logger/logger.ts b/src/logger/logger.ts
index c9d9a0f..f64b1e8 100644
--- a/src/logger/logger.ts
+++ b/src/logger/logger.ts
@@ -1,6 +1,7 @@
 import { Logger } from '.';
 
-export const log = new Logger(parseInt(process.env.LOG_LEVEL || '1') as any, 'ts-common');
+const logLevel = typeof process !== 'undefined' && process.env.LOG_LEVEL ? process.env.LOG_LEVEL : '1';
+export const log = new Logger(parseInt(logLevel) as any, 'ts-common');
 
 export function formatTimeDifference(milliseconds: number): string {
   const seconds = Math.floor((milliseconds / 1000) % 60);
diff --git a/src/model/graphology.ts b/src/model/graphology.ts
index 62f419f..afeee7f 100644
--- a/src/model/graphology.ts
+++ b/src/model/graphology.ts
@@ -78,3 +78,5 @@ export type QueryGraphEdges = {
 };
 
 export type QueryMultiGraph = SerializedGraph<QueryGraphNodes, QueryGraphEdges, GAttributes>;
+
+export type QueryGraphEdgesOpt = Partial<QueryGraphEdges>;
diff --git a/src/model/index.ts b/src/model/index.ts
index da0824c..2e1338a 100644
--- a/src/model/index.ts
+++ b/src/model/index.ts
@@ -4,3 +4,6 @@ export * from './query';
 export * from './saveStateModel';
 export * from './insight';
 export * from './webSocket';
+export * from './graphology';
+export * from './reactflow';
+export * from './utils';
diff --git a/src/model/insight.ts b/src/model/insight.ts
index 0d70a38..70bf652 100644
--- a/src/model/insight.ts
+++ b/src/model/insight.ts
@@ -9,7 +9,7 @@ export type InsightRequest = {
   saveStateId: string;
   userId?: number;
   status?: boolean;
-  type: 'report' | 'alert';
+  type: InsightType;
   alarmMode: 'disabled' | 'diff' | 'conditional' | 'always';
 };
 
@@ -27,3 +27,21 @@ export type InsightModel = {
     value: number;
   }[];
 } & InsightRequest;
+
+export const InsightConditionMap: Record<string, InsightCondition['operator']> = {
+  'Greater than': '>',
+  'Equal than': '==',
+  'Smaller than': '<',
+  'Not equal': '!=',
+  'Greater or equal': '>=',
+  'Smaller or equal': '<=',
+};
+export const InsightConditionMapReverse = Object.fromEntries(Object.entries(InsightConditionMap).map(([key, value]) => [value, key]));
+
+export type InsightCondition = {
+  nodeLabel: string;
+  property: string;
+  statistic: string;
+  operator: '>' | '==' | '<' | '!=' | '>=' | '<=';
+  value: number;
+};
diff --git a/src/model/query/index.ts b/src/model/query/index.ts
index 7188f79..e89aad1 100644
--- a/src/model/query/index.ts
+++ b/src/model/query/index.ts
@@ -1,5 +1,7 @@
 export * from './machineLearningModel';
 export * from './queryRequestModel';
+export * from './queryBuilderModel';
 export * from './queryResultModel';
 export * from './mlModel';
-export * from './statisticsResultModel';
+export * from './statistics';
+export * from './logic';
diff --git a/src/model/query/logic/boolFilters.ts b/src/model/query/logic/boolFilters.ts
new file mode 100644
index 0000000..9ccb849
--- /dev/null
+++ b/src/model/query/logic/boolFilters.ts
@@ -0,0 +1,37 @@
+/**
+ * This program has been developed by students from the bachelor Computer Science at
+ * Utrecht University within the Software Project course.
+ * © Copyright Utrecht University (Department of Information and Computing Sciences)
+ */
+
+import { BoolFilterTypes, type GeneralDescription } from './general';
+
+export const BoolFilters: Record<BoolFilterTypes, GeneralDescription<BoolFilterTypes>> = {
+  [BoolFilterTypes.EQUAL]: {
+    key: 'boolFilterEqual',
+    name: 'Equal',
+    type: BoolFilterTypes.EQUAL,
+    description: 'Equal to another value',
+    input: { name: 'Value', type: 'bool', default: 0 },
+    numExtraInputs: 1,
+    inputs: [{ name: '1', type: 'bool', default: 0 }],
+    output: { name: '==', type: 'bool' },
+    logic: ['==', '@i', '@1'],
+    icon: 'icon-[ic--baseline-equals]',
+  },
+  [BoolFilterTypes.NOT_EQUAL]: {
+    key: 'boolFilterNotEqual',
+    name: 'Not Equal',
+    type: BoolFilterTypes.NOT_EQUAL,
+    description: 'Not equal to another value',
+    input: { name: 'Value', type: 'bool', default: 0 },
+    numExtraInputs: 1,
+    inputs: [{ name: '1', type: 'bool', default: 0 }],
+    output: { name: '!=', type: 'bool' },
+    logic: ['!=', '@i', '@1'],
+    icon: 'icon-[ic--baseline-not-equal]',
+  },
+};
+
+/** All available functions in the function bar. */
+export const BoolFilterArray: Array<GeneralDescription<BoolFilterTypes>> = Object.values(BoolFilters);
diff --git a/src/model/query/logic/general.ts b/src/model/query/logic/general.ts
index 063bb32..e6b25a3 100644
--- a/src/model/query/logic/general.ts
+++ b/src/model/query/logic/general.ts
@@ -2,6 +2,11 @@ export type InputNodeTypeTypes = string | number | boolean;
 export type InputNodeType = 'string' | 'float' | 'int' | 'bool' | 'date' | 'time' | 'datetime' | 'duration';
 export type InputNodeDimension = 'categorical' | 'numerical' | 'temporal' | 'spatial';
 
+export enum BoolFilterTypes {
+  EQUAL = '==',
+  NOT_EQUAL = '!=',
+}
+
 export enum NumberFilterTypes {
   EQUAL = '==',
   NOT_EQUAL = '!=',
diff --git a/src/model/query/logic/index.ts b/src/model/query/logic/index.ts
index 18429b6..c4b0ea8 100644
--- a/src/model/query/logic/index.ts
+++ b/src/model/query/logic/index.ts
@@ -27,3 +27,5 @@ export * from './numberFunctions';
 export * from './numberFilters';
 export * from './stringFunctions';
 export * from './stringFilters';
+export * from './boolFilters';
+export * from './general';
diff --git a/src/model/query/logic/numberAggregations.tsx b/src/model/query/logic/numberAggregations.ts
similarity index 100%
rename from src/model/query/logic/numberAggregations.tsx
rename to src/model/query/logic/numberAggregations.ts
diff --git a/src/model/query/logic/numberFilters.tsx b/src/model/query/logic/numberFilters.ts
similarity index 100%
rename from src/model/query/logic/numberFilters.tsx
rename to src/model/query/logic/numberFilters.ts
diff --git a/src/model/query/logic/numberFunctions.tsx b/src/model/query/logic/numberFunctions.ts
similarity index 100%
rename from src/model/query/logic/numberFunctions.tsx
rename to src/model/query/logic/numberFunctions.ts
diff --git a/src/model/query/logic/stringFilters.tsx b/src/model/query/logic/stringFilters.ts
similarity index 100%
rename from src/model/query/logic/stringFilters.tsx
rename to src/model/query/logic/stringFilters.ts
diff --git a/src/model/query/logic/stringFunctions.tsx b/src/model/query/logic/stringFunctions.ts
similarity index 100%
rename from src/model/query/logic/stringFunctions.tsx
rename to src/model/query/logic/stringFunctions.ts
diff --git a/src/model/query/machineLearningModel.ts b/src/model/query/machineLearningModel.ts
index ee64ba3..fbf9bc7 100644
--- a/src/model/query/machineLearningModel.ts
+++ b/src/model/query/machineLearningModel.ts
@@ -1,12 +1,19 @@
-export type MLTypes = 'centrality' | 'linkPrediction' | 'communityDetection' | 'shortestPath';
-export const allMLTypes: MLTypes[] = ['centrality', 'linkPrediction', 'communityDetection', 'shortestPath'];
 export enum MLTypesEnum {
-  CENTRALITY = 'centrality',
-  LINK_PREDICTION = 'linkPrediction',
-  COMMUNITY_DETECTION = 'communityDetection',
-  SHORTEST_PATH = 'shortestPath',
+  centrality = 'centrality',
+  linkPrediction = 'linkPrediction',
+  communityDetection = 'communityDetection',
+  shortestPath = 'shortestPath',
 }
 
+export const allMLTypes = [
+  MLTypesEnum.centrality,
+  MLTypesEnum.linkPrediction,
+  MLTypesEnum.communityDetection,
+  MLTypesEnum.shortestPath,
+] as const;
+
+export type MLTypes = keyof typeof MLTypesEnum;
+
 export type LinkPredictionInstance = {
   attributes: { jaccard_coefficient: number };
   from: string;
@@ -16,7 +23,7 @@ export type LinkPredictionInstance = {
 
 export type CommunityDetectionInstance = string[]; // set of ids
 
-export type MLInstance<T> = {
+export type MLInstance<T = object> = {
   enabled: boolean;
   result: T;
 };
@@ -33,10 +40,10 @@ export type ShortestPath = {
 };
 
 export type ML = {
-  [MLTypesEnum.LINK_PREDICTION]: MLInstance<LinkPredictionInstance[]>;
-  [MLTypesEnum.CENTRALITY]: MLInstance<Record<string, number>>;
-  [MLTypesEnum.COMMUNITY_DETECTION]: CommunityDetection;
-  [MLTypesEnum.SHORTEST_PATH]: ShortestPath;
+  [MLTypesEnum.linkPrediction]: MLInstance<LinkPredictionInstance[]>;
+  [MLTypesEnum.centrality]: MLInstance<Record<string, number>>;
+  [MLTypesEnum.communityDetection]: CommunityDetection;
+  [MLTypesEnum.shortestPath]: ShortestPath;
 };
 
 export type MLInstanceTypes = ML[keyof ML];
diff --git a/src/model/query/queryResultModel.ts b/src/model/query/queryResultModel.ts
index c1d9930..cff277f 100644
--- a/src/model/query/queryResultModel.ts
+++ b/src/model/query/queryResultModel.ts
@@ -1,4 +1,4 @@
-import type { GraphStatistics } from './statisticsResultModel';
+import type { GraphStatistics } from './statistics/statisticsResultModel';
 
 export type NodeAttributes = { [key: string]: unknown };
 
diff --git a/src/model/query/statistics/graphStatistics.ts b/src/model/query/statistics/graphStatistics.ts
new file mode 100644
index 0000000..05f9852
--- /dev/null
+++ b/src/model/query/statistics/graphStatistics.ts
@@ -0,0 +1,71 @@
+import type { GraphQueryResultFromBackend } from '../../webSocket';
+import type { GraphStatistics } from './statisticsResultModel';
+import { getAttributeType, initializeStatistics, updateStatistics } from './utils';
+
+const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphStatistics => {
+  const { nodes, edges } = graph;
+
+  const n_nodes = nodes.length;
+  const n_edges = edges.length;
+
+  const density = n_nodes < 2 ? 0 : (n_edges * 2) / (n_nodes * (n_nodes - 1));
+
+  // general nodes and edges statistics
+  const metaData: GraphStatistics = {
+    topological: { density, self_loops: 0 },
+    nodes: { labels: [], count: n_nodes, types: {} },
+    edges: { labels: [], count: n_edges, types: {} },
+  };
+
+  // attributes based statistics
+  nodes.forEach(node => {
+    const nodeType = node.label;
+    if (!metaData.nodes.labels.includes(nodeType)) {
+      metaData.nodes.labels.push(nodeType);
+    }
+
+    if (!metaData.nodes.types[nodeType]) {
+      metaData.nodes.types[nodeType] = { count: 0, attributes: {} };
+    }
+    metaData.nodes.types[nodeType].count++;
+
+    Object.entries(node.attributes).forEach(([attributeId, attributeValue]) => {
+      const attributeType = getAttributeType(attributeValue);
+
+      if (!metaData.nodes.types[nodeType].attributes[attributeId]) {
+        metaData.nodes.types[nodeType].attributes[attributeId] = { attributeType, statistics: initializeStatistics(attributeType) };
+      }
+      updateStatistics(metaData.nodes.types[nodeType].attributes[attributeId], attributeValue);
+    });
+  });
+
+  // Process edges
+  edges.forEach(edge => {
+    const edgeType = edge.label;
+    if (!metaData.edges.labels.includes(edgeType)) {
+      metaData.edges.labels.push(edgeType);
+    }
+
+    if (!metaData.edges.types[edgeType]) {
+      metaData.edges.types[edgeType] = { count: 0, attributes: {} };
+    }
+
+    metaData.edges.types[edgeType].count++;
+
+    if (edge.from === edge.to) metaData.topological.self_loops++;
+
+    Object.entries(edge.attributes).forEach(([attributeId, attributeValue]) => {
+      const attributeType = getAttributeType(attributeValue);
+
+      if (!metaData.edges.types[edgeType].attributes[attributeId]) {
+        metaData.edges.types[edgeType].attributes[attributeId] = { attributeType, statistics: initializeStatistics(attributeType) };
+      }
+
+      updateStatistics(metaData.edges.types[edgeType].attributes[attributeId], attributeValue);
+    });
+  });
+
+  return metaData;
+};
+
+export { getGraphStatistics };
diff --git a/src/model/query/statistics/index.ts b/src/model/query/statistics/index.ts
new file mode 100644
index 0000000..aa1cac6
--- /dev/null
+++ b/src/model/query/statistics/index.ts
@@ -0,0 +1,2 @@
+export * from './graphStatistics';
+export * from './statisticsResultModel';
diff --git a/src/model/query/statisticsResultModel.ts b/src/model/query/statistics/statisticsResultModel.ts
similarity index 56%
rename from src/model/query/statisticsResultModel.ts
rename to src/model/query/statistics/statisticsResultModel.ts
index cdc6e18..f429445 100644
--- a/src/model/query/statisticsResultModel.ts
+++ b/src/model/query/statistics/statisticsResultModel.ts
@@ -1,10 +1,10 @@
-type GraphStatistics = {
+export type GraphStatistics = {
   topological: TopologicalStats;
-  nodes: NodeOrEdgeStats;
-  edges: NodeOrEdgeStats;
+  nodes: QueryNodeOrEdgeStats;
+  edges: QueryNodeOrEdgeStats;
 };
 
-type NodeOrEdgeStats = {
+export type QueryNodeOrEdgeStats = {
   count: number;
   labels: string[];
   types: {
@@ -19,28 +19,28 @@ type NodeOrEdgeStats = {
   };
 };
 
-type AttributeStats<T extends AttributeType> = {
+export type AttributeStats<T extends AttributeType> = {
   attributeType: T;
   statistics: AttributeTypeStats<T>;
 };
 
-type AttributeTypeStats<T extends AttributeType> = T extends 'string'
+export type AttributeTypeStats<T extends AttributeType> = T extends 'string'
   ? CategoricalStats
   : T extends 'boolean'
-  ? BooleanStats
-  : T extends 'number'
-  ? NumericalStats
-  : T extends 'date' | 'time' | 'datetime' | 'timestamp'
-  ? TemporalStats
-  : T extends 'array'
-  ? ArrayStats
-  : T extends 'object'
-  ? ObjectStats
-  : never;
+    ? BooleanStats
+    : T extends 'number'
+      ? NumericalStats
+      : T extends 'date' | 'time' | 'datetime' | 'timestamp'
+        ? TemporalStats
+        : T extends 'array'
+          ? ArrayStats
+          : T extends 'object'
+            ? ObjectStats
+            : never;
 
-type AttributeType = 'string' | 'boolean' | 'number' | 'array' | 'object' | TemporalType;
+export type AttributeType = 'string' | 'boolean' | 'number' | 'array' | 'object' | TemporalType;
 
-type TemporalType = 'date' | 'time' | 'datetime' | 'timestamp';
+export type TemporalType = 'date' | 'time' | 'datetime' | 'timestamp';
 // Date: Date in the YYYY-MM-DD format (ISO 8601 syntax) (e.g., 2021-09-28)
 // Time: Time in the hh:mm:ss format for the time of day, time since an event, or time interval between events (e.g., 12:00:59)
 // Datetime: Date and time together in the YYYY-MM-DD hh:mm:ss format (e.g., 2021-09-28 12:00:59)
@@ -51,49 +51,36 @@ type TopologicalStats = {
   self_loops: number;
 };
 
-type NumericalStats = {
+export type NumericalStats = {
   min: number;
   max: number;
   average: number;
   count: number;
 };
 
-type BooleanStats = {
+export type BooleanStats = {
   true: number;
   false: number;
 };
 
-type CategoricalStats = {
+export type CategoricalStats = {
   uniqueItems: number;
   values: string[];
   mode: string;
   count: number;
 };
 
-type TemporalStats = {
+export type TemporalStats = {
   min: number;
   max: number;
   range: number;
 };
 
-type ArrayStats = {
+export type ArrayStats = {
   length: number;
   count: number;
 };
 
-type ObjectStats = {
+export type ObjectStats = {
   length: number;
 };
-
-export type {
-  GraphStatistics,
-  AttributeStats,
-  NumericalStats,
-  CategoricalStats,
-  BooleanStats,
-  TemporalStats,
-  AttributeType,
-  AttributeTypeStats,
-  ArrayStats,
-  ObjectStats,
-};
diff --git a/src/model/query/statistics/tests/attributeStats.spec.ts b/src/model/query/statistics/tests/attributeStats.spec.ts
new file mode 100644
index 0000000..bd7175b
--- /dev/null
+++ b/src/model/query/statistics/tests/attributeStats.spec.ts
@@ -0,0 +1,107 @@
+import { describe, it, expect } from 'bun:test';
+import {
+  updateArrayStats,
+  updateBooleanStats,
+  updateCategoricalStats,
+  updateNumericalStats,
+  updateTemporalStats,
+  updateObjectStats,
+  initializeStatistics,
+} from '../utils/attributeStats';
+import type { ArrayStats, BooleanStats, CategoricalStats, NumericalStats, TemporalStats, ObjectStats } from '../statisticsResultModel';
+
+describe('updateArrayStats', () => {
+  it('should update the length of the array', () => {
+    const stats: ArrayStats = { length: 0, count: 0 };
+    const value = [1, 2, 3];
+    updateArrayStats(stats, value);
+    expect(stats.length).toBe(3);
+    expect(stats.count).toBe(1);
+  });
+});
+
+describe('updateBooleanStats', () => {
+  it('should update true count when value is true', () => {
+    const stats: BooleanStats = { true: 0, false: 0 };
+    updateBooleanStats(stats, true);
+    expect(stats.true).toBe(1);
+    expect(stats.false).toBe(0);
+  });
+
+  it('should update false count when value is false', () => {
+    const stats: BooleanStats = { true: 0, false: 0 };
+    updateBooleanStats(stats, false);
+    expect(stats.false).toBe(1);
+    expect(stats.true).toBe(0);
+  });
+});
+
+describe('updateCategoricalStats', () => {
+  it('should update mode and unique items count', () => {
+    const stats: CategoricalStats = { uniqueItems: 0, values: [], mode: '', count: 0 };
+    updateCategoricalStats(stats, 'apple');
+    updateCategoricalStats(stats, 'banana');
+    updateCategoricalStats(stats, 'apple');
+
+    expect(stats.values).toEqual(['apple', 'banana', 'apple']);
+    expect(stats.uniqueItems).toBe(2);
+    expect(stats.mode).toBe('apple');
+    expect(stats.count).toBe(3);
+  });
+});
+
+describe('updateNumericalStats', () => {
+  it('should update min, max, average, and count', () => {
+    const stats: NumericalStats = { min: Infinity, max: -Infinity, average: 0, count: 0 };
+    updateNumericalStats(stats, 10);
+    updateNumericalStats(stats, 20);
+    updateNumericalStats(stats, 5);
+
+    expect(stats.min).toBe(5);
+    expect(stats.max).toBe(20);
+    expect(stats.average).toBeCloseTo(11.67, 2);
+    expect(stats.count).toBe(3);
+  });
+});
+
+describe('updateTemporalStats', () => {
+  it('should update min, max, and range for temporal values', () => {
+    const stats: TemporalStats = { min: Infinity, max: -Infinity, range: 0 };
+    updateTemporalStats(stats, '2022-01-01');
+    updateTemporalStats(stats, '2022-01-05');
+
+    expect(stats.min).toBe(new Date('2022-01-01').getTime());
+    expect(stats.max).toBe(new Date('2022-01-05').getTime());
+    expect(stats.range).toBe(new Date('2022-01-05').getTime() - new Date('2022-01-01').getTime());
+  });
+});
+
+describe('updateObjectStats', () => {
+  it('should update the length of the object keys', () => {
+    const stats: ObjectStats = { length: 0 };
+    const value = { key1: 'value1', key2: 'value2' };
+    updateObjectStats(stats, value);
+    expect(stats.length).toBe(2);
+  });
+});
+
+describe('initializeStatistics', () => {
+  it('should initialize statistics for string type', () => {
+    const stats = initializeStatistics('string');
+    expect(stats).toEqual({ uniqueItems: 0, values: [], mode: '', count: 0 });
+  });
+
+  it('should initialize statistics for boolean type', () => {
+    const stats = initializeStatistics('boolean');
+    expect(stats).toEqual({ true: 0, false: 0 });
+  });
+
+  it('should initialize statistics for number type', () => {
+    const stats = initializeStatistics('number');
+    expect(stats).toEqual({ min: Infinity, max: -Infinity, average: 0, count: 0 });
+  });
+
+  it('should throw an error for an unknown type', () => {
+    expect(() => initializeStatistics('unknown' as any)).toThrow('Unknown attribute type: unknown');
+  });
+});
diff --git a/src/model/query/statistics/tests/getAttributeType.spec.ts b/src/model/query/statistics/tests/getAttributeType.spec.ts
new file mode 100644
index 0000000..6b8923c
--- /dev/null
+++ b/src/model/query/statistics/tests/getAttributeType.spec.ts
@@ -0,0 +1,65 @@
+import { describe, it, expect } from 'bun:test';
+import { getAttributeType } from '../utils/getAttributeType';
+
+// Sample values for testing
+const invalidDate = '2023-13-03';
+const validTime = '12:30:45';
+const invalidTime = '25:61:61';
+const invalidDatetime = '2023-10-03 25:61:61';
+const validNumber = '123.45';
+const invalidNumber = 'abc123';
+const booleanTrue = true;
+const booleanFalse = false;
+const numberValue = 123;
+const arrayValue = [1, 2, 3];
+const objectValue = { key: 'value' };
+const dateInstance = new Date('2023-10-03T12:30:45');
+
+// Unit tests for getAttributeType function
+describe('getAttributeType', () => {
+  it('should correctly identify numbers as type "number"', () => {
+    expect(getAttributeType(validNumber)).toBe('number');
+    expect(getAttributeType(numberValue)).toBe('number');
+  });
+
+  it('should correctly identify strings as valid "time"', () => {
+    expect(getAttributeType(validTime)).toBe('time');
+  });
+
+  it('should identify invalid datetime strings as "string"', () => {
+    expect(getAttributeType(invalidDatetime)).toBe('string');
+  });
+
+  it('should identify invalid date strings as "string"', () => {
+    expect(getAttributeType(invalidDate)).toBe('string');
+  });
+
+  it('should identify invalid time strings as "string"', () => {
+    expect(getAttributeType(invalidTime)).toBe('string');
+  });
+
+  it('should correctly identify boolean values as type "boolean"', () => {
+    expect(getAttributeType(booleanTrue)).toBe('boolean');
+    expect(getAttributeType(booleanFalse)).toBe('boolean');
+  });
+
+  it('should correctly identify arrays as type "array"', () => {
+    expect(getAttributeType(arrayValue)).toBe('array');
+  });
+
+  it('should correctly identify objects as type "object"', () => {
+    expect(getAttributeType(objectValue)).toBe('object');
+  });
+
+  it('should correctly identify Date instances as type "datetime"', () => {
+    expect(getAttributeType(dateInstance)).toBe('datetime');
+  });
+
+  it('should identify string representations of invalid numbers as "string"', () => {
+    expect(getAttributeType(invalidNumber)).toBe('string');
+  });
+
+  it('should identify a regular string as type "string"', () => {
+    expect(getAttributeType('random string')).toBe('string');
+  });
+});
diff --git a/src/model/query/statistics/tests/graphStatistics.spec.ts b/src/model/query/statistics/tests/graphStatistics.spec.ts
new file mode 100644
index 0000000..70ea8da
--- /dev/null
+++ b/src/model/query/statistics/tests/graphStatistics.spec.ts
@@ -0,0 +1,121 @@
+import { describe, it, expect } from 'bun:test';
+import { getGraphStatistics } from '../graphStatistics';
+import type { GraphQueryResultFromBackend } from '../../../webSocket';
+
+describe('getGraphStatistics', () => {
+  it('should return correct statistics for a graph with no nodes and edges', () => {
+    const graph: GraphQueryResultFromBackend = {
+      nodes: [],
+      edges: [],
+    };
+
+    const stats = getGraphStatistics(graph);
+
+    expect(stats).toEqual({
+      topological: { density: 0, self_loops: 0 },
+      nodes: { labels: [], count: 0, types: {} },
+      edges: { labels: [], count: 0, types: {} },
+    });
+  });
+
+  it('should return correct statistics for a graph with nodes and no edges', () => {
+    const graph: GraphQueryResultFromBackend = {
+      nodes: [
+        { _id: '1', label: 'Person', attributes: { age: 25 } },
+        { _id: '2', label: 'Person', attributes: { age: 30, city: 'New York' } },
+      ],
+      edges: [],
+    };
+
+    const stats = getGraphStatistics(graph);
+
+    expect(stats).toEqual({
+      topological: { density: 0, self_loops: 0 },
+      nodes: {
+        labels: ['Person'], // Assuming default label
+        count: 2,
+        types: {
+          Person: {
+            count: 2,
+            attributes: {
+              age: { attributeType: 'number', statistics: expect.any(Object) },
+              city: { attributeType: 'string', statistics: expect.any(Object) },
+            },
+          },
+        },
+      },
+      edges: { labels: [], count: 0, types: {} },
+    });
+  });
+
+  it('should return correct statistics for a graph with edges and nodes', () => {
+    const graph: GraphQueryResultFromBackend = {
+      nodes: [
+        { _id: '1', label: 'Person', attributes: { age: 25 } },
+        { _id: '2', label: 'Person', attributes: { age: 30 } },
+      ],
+      edges: [
+        { _id: 'e1', label: 'Relationship', attributes: { weight: 5 }, from: '1', to: '2' },
+        { _id: 'e2', label: 'Relationship', attributes: { weight: 10 }, from: '2', to: '2' }, // self-loop
+      ],
+    };
+
+    const stats = getGraphStatistics(graph);
+
+    expect(stats).toEqual({
+      topological: { density: 2, self_loops: 1 },
+      nodes: {
+        labels: ['Person'], // Assuming default label
+        count: 2,
+        types: {
+          Person: {
+            count: 2,
+            attributes: {
+              age: { attributeType: 'number', statistics: expect.any(Object) },
+            },
+          },
+        },
+      },
+      edges: {
+        labels: ['Relationship'], // Assuming default edge type
+        count: 2,
+        types: {
+          Relationship: {
+            count: 2,
+            attributes: {
+              weight: { attributeType: 'number', statistics: expect.any(Object) },
+            },
+          },
+        },
+      },
+    });
+  });
+
+  it('should correctly count self-loops', () => {
+    const graph: GraphQueryResultFromBackend = {
+      nodes: [{ _id: '1', attributes: {}, label: 'Person' }],
+      edges: [
+        { _id: 'e1', attributes: {}, from: '1', to: '1', label: 'Person' }, // self-loop
+      ],
+    };
+
+    const stats = getGraphStatistics(graph);
+
+    expect(stats.topological.self_loops).toBe(1);
+  });
+
+  it('should correctly compute density for a graph with nodes and edges', () => {
+    const graph: GraphQueryResultFromBackend = {
+      nodes: [
+        { _id: '1', attributes: {}, label: 'Person' },
+        { _id: '2', attributes: {}, label: 'Person' },
+      ],
+      edges: [{ _id: 'e1', attributes: {}, from: '1', to: '2', label: 'Person' }],
+    };
+
+    const stats = getGraphStatistics(graph);
+
+    // Density = (n_edges * 2) / (n_nodes * (n_nodes - 1)) = (1 * 2) / (2 * 1) = 1
+    expect(stats.topological.density).toBe(1);
+  });
+});
diff --git a/src/model/query/statistics/utils/attributeStats/array.ts b/src/model/query/statistics/utils/attributeStats/array.ts
new file mode 100644
index 0000000..915e56d
--- /dev/null
+++ b/src/model/query/statistics/utils/attributeStats/array.ts
@@ -0,0 +1,6 @@
+import type { ArrayStats } from '../../statisticsResultModel';
+
+export const updateArrayStats = (stats: ArrayStats, value: any[]) => {
+  stats.length = value.length;
+  stats.count++;
+};
diff --git a/src/model/query/statistics/utils/attributeStats/boolean.ts b/src/model/query/statistics/utils/attributeStats/boolean.ts
new file mode 100644
index 0000000..952aea8
--- /dev/null
+++ b/src/model/query/statistics/utils/attributeStats/boolean.ts
@@ -0,0 +1,9 @@
+import type { BooleanStats } from '../../statisticsResultModel';
+
+export const updateBooleanStats = (stats: BooleanStats, value: boolean) => {
+  if (value) {
+    stats.true += 1;
+  } else {
+    stats.false += 1;
+  }
+};
diff --git a/src/model/query/statistics/utils/attributeStats/categorical.ts b/src/model/query/statistics/utils/attributeStats/categorical.ts
new file mode 100644
index 0000000..1932817
--- /dev/null
+++ b/src/model/query/statistics/utils/attributeStats/categorical.ts
@@ -0,0 +1,15 @@
+import { type CategoricalStats } from '../../statisticsResultModel';
+
+export const updateCategoricalStats = (stats: CategoricalStats, value: string | boolean) => {
+  if (!stats.values) stats.values = [];
+  stats.values.push(value.toString());
+
+  stats.uniqueItems = new Set(stats.values).size;
+
+  const frequencyMap: { [key: string]: number } = {};
+  stats.values.forEach(val => {
+    frequencyMap[val] = (frequencyMap[val] || 0) + 1;
+  });
+  stats.mode = Object.keys(frequencyMap).reduce((a, b) => (frequencyMap[a] > frequencyMap[b] ? a : b));
+  stats.count++;
+};
diff --git a/src/model/query/statistics/utils/attributeStats/index.ts b/src/model/query/statistics/utils/attributeStats/index.ts
new file mode 100644
index 0000000..42c4501
--- /dev/null
+++ b/src/model/query/statistics/utils/attributeStats/index.ts
@@ -0,0 +1,7 @@
+export * from './array';
+export * from './categorical';
+export * from './numerical';
+export * from './object';
+export * from './temporal';
+export * from './boolean';
+export * from './initialize';
diff --git a/src/model/query/statistics/utils/attributeStats/initialize.ts b/src/model/query/statistics/utils/attributeStats/initialize.ts
new file mode 100644
index 0000000..fcc6238
--- /dev/null
+++ b/src/model/query/statistics/utils/attributeStats/initialize.ts
@@ -0,0 +1,45 @@
+import type { AttributeType, AttributeTypeStats } from '../../statisticsResultModel';
+
+export const initializeStatistics = <T extends AttributeType>(type: T): AttributeTypeStats<T> => {
+  switch (type) {
+    case 'string':
+      return {
+        uniqueItems: 0,
+        values: [],
+        mode: '',
+        count: 0,
+      } as unknown as AttributeTypeStats<T>;
+    case 'boolean':
+      return {
+        true: 0,
+        false: 0,
+      } as unknown as AttributeTypeStats<T>;
+    case 'number':
+      return {
+        min: Infinity,
+        max: -Infinity,
+        average: 0,
+        count: 0,
+      } as unknown as AttributeTypeStats<T>;
+    case 'date':
+    case 'time':
+    case 'datetime':
+    case 'timestamp':
+      return {
+        min: Infinity,
+        max: -Infinity,
+        range: 0,
+      } as unknown as AttributeTypeStats<T>;
+    case 'array':
+      return {
+        length: 0,
+        count: 0,
+      } as unknown as AttributeTypeStats<T>;
+    case 'object':
+      return {
+        length: 0,
+      } as unknown as AttributeTypeStats<T>;
+    default:
+      throw new Error(`Unknown attribute type: ${type}`);
+  }
+};
diff --git a/src/model/query/statistics/utils/attributeStats/numerical.ts b/src/model/query/statistics/utils/attributeStats/numerical.ts
new file mode 100644
index 0000000..d845d66
--- /dev/null
+++ b/src/model/query/statistics/utils/attributeStats/numerical.ts
@@ -0,0 +1,9 @@
+import type { NumericalStats } from '../../statisticsResultModel';
+
+export const updateNumericalStats = (stats: NumericalStats, value: number) => {
+  if (stats.min === undefined || value < stats.min) stats.min = value;
+  if (stats.max === undefined || value > stats.max) stats.max = value;
+
+  stats.count++;
+  stats.average = (stats.average * (stats.count - 1) + value) / stats.count;
+};
diff --git a/src/model/query/statistics/utils/attributeStats/object.ts b/src/model/query/statistics/utils/attributeStats/object.ts
new file mode 100644
index 0000000..26ed4ec
--- /dev/null
+++ b/src/model/query/statistics/utils/attributeStats/object.ts
@@ -0,0 +1,5 @@
+import type { ObjectStats } from '../../statisticsResultModel';
+
+export const updateObjectStats = (stats: ObjectStats, value: object) => {
+  stats.length = Object.keys(value).length;
+};
diff --git a/src/model/query/statistics/utils/attributeStats/temporal.ts b/src/model/query/statistics/utils/attributeStats/temporal.ts
new file mode 100644
index 0000000..f2a2a11
--- /dev/null
+++ b/src/model/query/statistics/utils/attributeStats/temporal.ts
@@ -0,0 +1,10 @@
+import type { TemporalStats } from '../../statisticsResultModel';
+
+export const updateTemporalStats = (stats: TemporalStats, value: string | Date) => {
+  const timestamp = value instanceof Date ? value.getTime() : new Date(value).getTime();
+
+  if (stats.min === undefined || timestamp < stats.min) stats.min = timestamp;
+  if (stats.max === undefined || timestamp > stats.max) stats.max = timestamp;
+
+  stats.range = stats.max - stats.min;
+};
diff --git a/src/model/query/statistics/utils/getAttributeType.ts b/src/model/query/statistics/utils/getAttributeType.ts
new file mode 100644
index 0000000..f017b2e
--- /dev/null
+++ b/src/model/query/statistics/utils/getAttributeType.ts
@@ -0,0 +1,76 @@
+import type { AttributeType } from '../statisticsResultModel';
+
+// Check if a string is a valid date in the YYYY-MM-DD format
+const isValidDate = (value: string): boolean => {
+  const dateRegex = /^\d{4}-\d{2}-\d{2}$/; // Matches YYYY-MM-DD format
+  const [year, month, day] = value.split('-').map(Number);
+  const date = new Date(value);
+
+  // Check if the regex matches, and if the date is valid (correct month/day conversion)
+  return (
+    dateRegex.test(value) &&
+    date.getFullYear() === year &&
+    date.getMonth() + 1 === month && // Months are 0-based in JS Date
+    date.getDate() === day
+  );
+};
+
+// Check if a string is a valid time in the hh:mm:ss format
+const isValidTime = (value: string): boolean => {
+  const timeRegex = /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/;
+  return timeRegex.test(value);
+};
+
+// Check if a string is a valid datetime in the YYYY-MM-DD hh:mm:ss format
+const isValidDatetime = (value: string): boolean => {
+  const datetimeRegex = /^\d{4}-\d{2}-\d{2} ([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/;
+  const [date, time] = value.split(' ');
+
+  return datetimeRegex.test(value) && isValidDate(date) && isValidTime(time);
+};
+
+// Check if a string is a valid number
+const isValidNumber = (value: string): boolean => {
+  return !isNaN(Number(value)) && !isNaN(parseFloat(value));
+};
+
+// Determines the type of an attribute
+export const getAttributeType = (value: any): AttributeType => {
+  if (typeof value === 'string') {
+    if (isValidNumber(value)) {
+      return 'number';
+    }
+    if (isValidDatetime(value)) {
+      return 'datetime';
+    }
+    if (isValidDate(value)) {
+      return 'date';
+    }
+    if (isValidTime(value)) {
+      return 'time';
+    }
+    return 'string';
+  }
+
+  if (typeof value === 'boolean') {
+    return 'boolean';
+  }
+
+  if (typeof value === 'number') {
+    return 'number';
+  }
+
+  if (Array.isArray(value)) {
+    return 'array';
+  }
+
+  if (value instanceof Date) {
+    return 'datetime';
+  }
+
+  if (typeof value === 'object' && value !== null) {
+    return 'object';
+  }
+
+  return 'string';
+};
diff --git a/src/model/query/statistics/utils/index.ts b/src/model/query/statistics/utils/index.ts
new file mode 100644
index 0000000..4fb895d
--- /dev/null
+++ b/src/model/query/statistics/utils/index.ts
@@ -0,0 +1,3 @@
+export * from './getAttributeType';
+export * from './attributeStats';
+export * from './updateStatistics';
diff --git a/src/model/query/statistics/utils/updateStatistics.ts b/src/model/query/statistics/utils/updateStatistics.ts
new file mode 100644
index 0000000..82cab15
--- /dev/null
+++ b/src/model/query/statistics/utils/updateStatistics.ts
@@ -0,0 +1,45 @@
+import type {
+  AttributeStats,
+  AttributeType,
+  NumericalStats,
+  CategoricalStats,
+  BooleanStats,
+  TemporalStats,
+  ArrayStats,
+  ObjectStats,
+} from '../statisticsResultModel';
+import {
+  updateArrayStats,
+  updateCategoricalStats,
+  updateNumericalStats,
+  updateObjectStats,
+  updateTemporalStats,
+  updateBooleanStats,
+} from './attributeStats';
+
+// Update statistics based on attribute type and value
+export const updateStatistics = (attribute: AttributeStats<AttributeType>, value: any) => {
+  switch (attribute.attributeType) {
+    case 'number':
+      updateNumericalStats(attribute.statistics as NumericalStats, value);
+      break;
+    case 'string':
+      updateCategoricalStats(attribute.statistics as CategoricalStats, value);
+      break;
+    case 'boolean':
+      updateBooleanStats(attribute.statistics as BooleanStats, value);
+      break;
+    case 'datetime':
+    case 'timestamp':
+    case 'date':
+    case 'time':
+      updateTemporalStats(attribute.statistics as TemporalStats, value);
+      break;
+    case 'array':
+      updateArrayStats(attribute.statistics as ArrayStats, value);
+      break;
+    case 'object':
+      updateObjectStats(attribute.statistics as ObjectStats, value);
+      break;
+  }
+};
diff --git a/src/model/reactflow.ts b/src/model/reactflow.ts
index 1f72892..5f90c9d 100644
--- a/src/model/reactflow.ts
+++ b/src/model/reactflow.ts
@@ -21,3 +21,20 @@ export enum QueryElementTypes {
   // Function = 'function',
   Logic = 'logic',
 }
+
+export function isRelationHandle(handle: Handles): boolean {
+  return handle.startsWith(Handles.RelationLeft) || handle.startsWith(Handles.RelationRight);
+}
+
+export function isEntityHandle(handle: Handles): boolean {
+  return handle.startsWith(Handles.EntityLeft) || handle.startsWith(Handles.EntityRight);
+}
+
+export function isLogicHandle(handle: Handles): boolean {
+  return (
+    handle.startsWith(Handles.LogicLeft) ||
+    handle.startsWith(Handles.LogicRight) ||
+    handle.startsWith(Handles.RelationAttribute) ||
+    handle.startsWith(Handles.EntityAttribute)
+  );
+}
diff --git a/src/model/saveStateModel.ts b/src/model/saveStateModel.ts
index 25b3829..bf2890d 100644
--- a/src/model/saveStateModel.ts
+++ b/src/model/saveStateModel.ts
@@ -1,3 +1,4 @@
+import type { QueryGraphEdgeHandle } from './graphology';
 import type { QueryBuilderSettings } from './query/queryBuilderModel';
 
 export type DbConnection = {
@@ -22,7 +23,7 @@ export type Query = {
   id?: number;
   graph: Graph;
   settings: QueryBuilderSettings;
-  attributesBeingShown: any[];
+  attributesBeingShown: QueryGraphEdgeHandle[];
 };
 
 export type Visualization = {
diff --git a/src/model/schemaModel.ts b/src/model/schemaModel.ts
index f107e4d..e115c8c 100644
--- a/src/model/schemaModel.ts
+++ b/src/model/schemaModel.ts
@@ -54,15 +54,15 @@ export type SchemaGraphInference = {
 export type SchemaGraphStats = {
   nodes: {
     count: number;
-    stats: Record<string, NodeOrEdgeStats>; // node key -> Stats
+    stats: Record<string, SchemaNodeOrEdgeStats>; // node key -> Stats
   };
   edges: {
     count: number;
-    stats: Record<string, NodeOrEdgeStats>; // edge key -> Stats
+    stats: Record<string, SchemaNodeOrEdgeStats>; // edge key -> Stats
   };
 };
 
-export type NodeOrEdgeStats = {
+export type SchemaNodeOrEdgeStats = {
   key: string;
   type?: string;
   count: number;
diff --git a/src/model/utils.ts b/src/model/utils.ts
new file mode 100644
index 0000000..9fa144f
--- /dev/null
+++ b/src/model/utils.ts
@@ -0,0 +1,5 @@
+export type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
+
+// Example usage:
+// type Union = { a: string } | { b: number };
+// type Intersection = UnionToIntersection<Union>; // { a: string } & { b: number }
diff --git a/src/model/webSocket/graphResult.ts b/src/model/webSocket/graphResult.ts
index deebcf0..5da8126 100644
--- a/src/model/webSocket/graphResult.ts
+++ b/src/model/webSocket/graphResult.ts
@@ -1,21 +1,19 @@
 import type { NodeQueryResult, EdgeQueryResult, GraphStatistics } from '../query';
 
-export interface GraphQueryResultFromBackend {
+export type GraphQueryResultFromBackend = {
   nodes: NodeQueryResult[];
   edges: EdgeQueryResult[];
 
   // TODO: Also include type in node and edge
   // TODO: The backend should send all the different entitytypes and relationtypes in the result
-}
+};
 
-export interface GraphQueryResultMetaFromBackend {
-  nodes: NodeQueryResult[];
-  edges: EdgeQueryResult[];
+export type GraphQueryResultMetaFromBackend = GraphQueryResultFromBackend & {
   metaData: GraphStatistics;
 
   // TODO: Also include type in node and edge
   // TODO: The backend should send all the different entitytypes and relationtypes in the result
-}
+};
 
 export type GraphQueryTranslationResultMessage = {
   queryID: string;
diff --git a/src/model/webSocket/index.ts b/src/model/webSocket/index.ts
index 0d903cf..af4a8c1 100644
--- a/src/model/webSocket/index.ts
+++ b/src/model/webSocket/index.ts
@@ -2,3 +2,5 @@ export * from './message2Backend';
 export * from './message2Frontend';
 export * from './schema';
 export * from './graphResult';
+export * from './policy';
+export * from './model';
diff --git a/src/model/webSocket/message2Backend.ts b/src/model/webSocket/message2Backend.ts
index 77b559e..d6e4732 100644
--- a/src/model/webSocket/message2Backend.ts
+++ b/src/model/webSocket/message2Backend.ts
@@ -63,7 +63,7 @@ export type WsMessageBody =
   | WsMessageBodyI<wsKeys.schema, wsSubKeys.get, { cached: boolean; saveStateID: string }>
   | WsMessageBodyI<wsKeys.schema, wsSubKeys.getSchemaStats, { cached: boolean; saveStateID: string }>
   // Query
-  | WsMessageBodyI<wsKeys.query, wsSubKeys.get, { saveStateID: string; ml: MLInstanceTypes[]; cached: boolean }>
+  | WsMessageBodyI<wsKeys.query, wsSubKeys.get, { saveStateID: string; ml: MLInstanceTypes[]; cached: boolean; queryID: string }>
   | WsMessageBodyI<wsKeys.query, wsSubKeys.manual, string>
   // Insights
   | WsMessageBodyI<wsKeys.insight, wsSubKeys.create, InsightRequest>
diff --git a/src/model/webSocket/message2Frontend.ts b/src/model/webSocket/message2Frontend.ts
index af0e116..8d8fde3 100644
--- a/src/model/webSocket/message2Frontend.ts
+++ b/src/model/webSocket/message2Frontend.ts
@@ -4,6 +4,7 @@ import type { DBConnectionResult } from './dbConnection';
 import type { GraphQueryTranslationResultMessage, QueryStatusResult } from './graphResult';
 import type { InsightModel } from '../insight';
 import type { SaveStateAuthorizationHeaders, UserAuthorizationHeaders } from './policy';
+import type { MLTypesEnum } from '../query';
 
 export enum wsReturnKey {
   testedConnection = 'tested_connection',
@@ -25,8 +26,10 @@ export enum wsReturnKey {
   userPolicy = 'user_policy',
   error = 'error',
   reconnect = 'reconnect',
+  schemaInference = 'schema_inference',
 }
 export type WsReturnKey = keyof typeof wsReturnKey;
+export type wsReturnKeyWithML = wsReturnKey | MLTypesEnum;
 
 type WsMessageBody2FrontendI<T extends wsReturnKey, V> = {
   callID: string;
@@ -65,4 +68,9 @@ export type WsMessageBackend2Frontend =
   // Reconnect
   | WsMessageBody2FrontendI<wsReturnKey.reconnect, { sessionID: string }>;
 
-export type ResponseCallback = (data: WsMessageBackend2Frontend['value'], status: string) => void;
+export type ResponseCallback<T extends wsReturnKey> = (
+  data: Extract<WsMessageBackend2Frontend, { type: T }>['value'] | null,
+  status: string,
+) => void;
+
+export type WsFrontendCall<T, R extends wsReturnKey = never> = (params: T, callback?: ResponseCallback<R>) => void;
diff --git a/src/model/webSocket/model.ts b/src/model/webSocket/model.ts
index 8b13789..e69de29 100644
--- a/src/model/webSocket/model.ts
+++ b/src/model/webSocket/model.ts
@@ -1 +0,0 @@
-
diff --git a/src/model/webSocket/policy.ts b/src/model/webSocket/policy.ts
index 6c40dbd..22c074e 100644
--- a/src/model/webSocket/policy.ts
+++ b/src/model/webSocket/policy.ts
@@ -1,5 +1,3 @@
-export type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
-
 export enum UserAuthorizationObjectsEnum {
   savestate = 'savestate',
   demoUser = 'demoUser',
@@ -48,3 +46,21 @@ export type PolicyItem = {
   obj: string;
   act: string;
 };
+
+export interface UserPolicy {
+  name: string;
+  email: string;
+  type: string;
+}
+
+export type UserAuthenticationHeader = {
+  username: string;
+  userID: number;
+  roomID: string;
+  jwt: string;
+};
+
+export interface PolicyResourcesState {
+  read: string[];
+  write: string[];
+}
-- 
GitLab