telos-oql 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +201 -0
- package/README.md +264 -0
- package/omniQuery.js +500 -0
- package/oql.js +182 -0
- package/oql.json +10 -0
- package/package.json +16 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# OmniQuery
|
|
2
|
+
|
|
3
|
+
## 1 - Abstract
|
|
4
|
+
|
|
5
|
+
***The State of Union.***
|
|
6
|
+
|
|
7
|
+
OmniQuery (OQ, or OmniQuery Language - OQL / "Ockle") is a LISP dialect for database-agnostic
|
|
8
|
+
querying.
|
|
9
|
+
|
|
10
|
+
## 2 - Contents
|
|
11
|
+
|
|
12
|
+
### 2.1 - Language
|
|
13
|
+
|
|
14
|
+
#### 2.1.1 - Conventions
|
|
15
|
+
|
|
16
|
+
##### 2.1.1.1 - Standard Fusion LISP
|
|
17
|
+
|
|
18
|
+
OmniQuery borrows the logic and arithmetic operators of
|
|
19
|
+
[Standard Fusion LISP](https://github.com/Telos-Project/Fusion-LISP?tab=readme-ov-file#222---standard-fusion-lisp).
|
|
20
|
+
|
|
21
|
+
Additionally, it also borrows the return operator, uses modified versions of certain list-related
|
|
22
|
+
operators, and uses the [dynamic LISP](https://github.com/Telos-Project/Fusion-LISP?tab=readme-ov-file#2222---dynamic-lisp)
|
|
23
|
+
conventions.
|
|
24
|
+
|
|
25
|
+
##### 2.1.1.2 - Data
|
|
26
|
+
|
|
27
|
+
###### 2.1.1.2.1 - State
|
|
28
|
+
|
|
29
|
+
OmniQuery is, in and of itself, stateless.
|
|
30
|
+
|
|
31
|
+
###### 2.1.1.2.2 - Contexts
|
|
32
|
+
|
|
33
|
+
OmniQuery contexts are objects which act as cursors which point to specific databases and sets of
|
|
34
|
+
data therein.
|
|
35
|
+
|
|
36
|
+
###### 2.1.1.2.3 - Dynamic Mapping
|
|
37
|
+
|
|
38
|
+
OmniQuery represents any database it interacts with, and the contexts it uses to interact with
|
|
39
|
+
them, as dynamic lists. The resulting mapping is referred to as a dynamic mapped database (DMDB).
|
|
40
|
+
|
|
41
|
+
As such, when a context is returned to an external environment, its contents are returned as a
|
|
42
|
+
dynamic list. Usually, the returned contents are in the form JSON.
|
|
43
|
+
|
|
44
|
+
A relational database may be mapped to a dynamic list, where each table in the database is
|
|
45
|
+
represented as a list dynamic value in said dynamic list, the key of the value being the name of
|
|
46
|
+
the table and the value itself being the contents of the table.
|
|
47
|
+
|
|
48
|
+
Each element of said list shall be a dynamic list representing a row in said table, with each value
|
|
49
|
+
of said list being a dynamic value and representing a column in said table, the key of the value
|
|
50
|
+
being the name of the column, and the value itself being the value of the column, converted to a
|
|
51
|
+
JSON compatible types.
|
|
52
|
+
|
|
53
|
+
###### 2.1.1.2.4 - Type LISP
|
|
54
|
+
|
|
55
|
+
Type LISP is a LISP convention where a value may be assigned metadata using a list where the
|
|
56
|
+
operator is "type", where said value is the first argument, and where the second argument is a
|
|
57
|
+
dynamic list containing said metadata.
|
|
58
|
+
|
|
59
|
+
Codified conventions for the content or application of such metadata are referred to as type LISP
|
|
60
|
+
conventions.
|
|
61
|
+
|
|
62
|
+
##### 2.1.1.3 - Usage
|
|
63
|
+
|
|
64
|
+
###### 2.1.1.3.1 - Selectors
|
|
65
|
+
|
|
66
|
+
An OmniQuery script which returns data but does not edit data may be treated as a selector for the
|
|
67
|
+
returned data.
|
|
68
|
+
|
|
69
|
+
###### 2.1.1.3.2 - Subscriptions
|
|
70
|
+
|
|
71
|
+
OmniQuery subscriptions event handlers tied to specific values within a DMDB which trigger events
|
|
72
|
+
when said values are altered, ideally with the previous and new values being passed to said event.
|
|
73
|
+
|
|
74
|
+
OmniQuery subscriptions may be specified with OmniQuery selectors.
|
|
75
|
+
|
|
76
|
+
###### 2.1.1.3.3 - Entanglement
|
|
77
|
+
|
|
78
|
+
OmniQuery entanglement is when rules for two values in separate DMDBs are enforced to keep them
|
|
79
|
+
aligned, if not identical, when one of them is altered.
|
|
80
|
+
|
|
81
|
+
OmniQuery entanglements may be specified with OmniQuery selectors, handled by OmniQuery
|
|
82
|
+
subscriptions, and declared and unidirectional or bidirectional.
|
|
83
|
+
|
|
84
|
+
###### 2.1.1.3.4 - Resolvers
|
|
85
|
+
|
|
86
|
+
An OmniQuery resolver is an API endpoint which transforms an incoming query to the API into a query
|
|
87
|
+
to a database according to its content.
|
|
88
|
+
|
|
89
|
+
###### 2.1.1.3.5 - Streams
|
|
90
|
+
|
|
91
|
+
An OmniQuery stream is a sustained connection between data in a DMDB and an external system.
|
|
92
|
+
|
|
93
|
+
###### 2.1.1.3.6 - Agnostic Scripts
|
|
94
|
+
|
|
95
|
+
Agnostic scripts are scripts for a system which may be written in any language. As such, rather
|
|
96
|
+
than interacting with said system through environmental variables and functions, the scripts,
|
|
97
|
+
written as function bodies, return an OmniQuery script as a LISP string, which executes upon the
|
|
98
|
+
state of said system (said state itself represented as a DMDB), the results of which are then
|
|
99
|
+
passed to the same script as a dynamic list encoded in a JSON string upon its next execution.
|
|
100
|
+
|
|
101
|
+
##### 2.1.1.4 - Meta Models
|
|
102
|
+
|
|
103
|
+
Meta models are models which contextualize a disparate set of records and databases by serializing
|
|
104
|
+
them within, or referencing them from, a hierarchical structure.
|
|
105
|
+
|
|
106
|
+
###### 2.1.1.4.1 - Dynamic Meta Models
|
|
107
|
+
|
|
108
|
+
A dynamic list, referred to as a model list, may be used to encode the structure of a meta model,
|
|
109
|
+
and metadata may be assigned to a model list by embedding it in a meta dynamic list referred to as
|
|
110
|
+
a meta model list.
|
|
111
|
+
|
|
112
|
+
The model list should only serialize the hierarchical structure and data content of the meta model
|
|
113
|
+
to which it maps. A model list is not required to serialize the entirety of the meta model to which
|
|
114
|
+
it, though one which does is referred to as a complete model list, and one which does not is
|
|
115
|
+
referred to as incomplete.
|
|
116
|
+
|
|
117
|
+
The property lists assigned to values within model lists may have a value with the key "context",
|
|
118
|
+
which contains an OQL query which returns a context that the value corresponding to the property
|
|
119
|
+
list in question acts as an alias to, with any descendant of said corresponding value being nested
|
|
120
|
+
within said context; and may have a value with the key "properties", said value being a dynamic
|
|
121
|
+
list by default, which specifies the system and type properties assigned to the value corresponding
|
|
122
|
+
to the property list in question.
|
|
123
|
+
|
|
124
|
+
###### 2.1.1.5.2 - Dynamic OQL Queries
|
|
125
|
+
|
|
126
|
+
A dynamic OQL query is submitted as a meta model list where the property lists thereof may have an
|
|
127
|
+
additional value with the key "query", containing an OQL query to execute upon the resource to
|
|
128
|
+
which the value, corresponding to the property list in question, maps.
|
|
129
|
+
|
|
130
|
+
When executed, it shall not only execute said OQL queries, but shall also create and update the
|
|
131
|
+
resources to which it maps according to the structure and content declaratively specified in its
|
|
132
|
+
model list and property list properties. It shall then resolve to and return itself with the data
|
|
133
|
+
said OQL queries resolved to embedded in its model list and property list properties.
|
|
134
|
+
|
|
135
|
+
#### 2.1.2 - Operators
|
|
136
|
+
|
|
137
|
+
##### 2.1.2.1 - arguments
|
|
138
|
+
|
|
139
|
+
The arguments atom, if used in the main scope of the script, resolves to a context passed in by the
|
|
140
|
+
process executing the script, and if used in the scope of an isolated expression, resolves to the
|
|
141
|
+
value passed to said expression when said expression is invoked.
|
|
142
|
+
|
|
143
|
+
##### 2.1.2.2 - at
|
|
144
|
+
|
|
145
|
+
###### 2.1.2.2.1 - As Context
|
|
146
|
+
|
|
147
|
+
The at operator takes a context object as its first argument, with each subsequent argument being a
|
|
148
|
+
number or string.
|
|
149
|
+
|
|
150
|
+
For each subsequent argument, it shall be treated as an identifier for a value in the dynamic list
|
|
151
|
+
selected corresponding to the previous argument, with strings being keys and numbers being indices.
|
|
152
|
+
|
|
153
|
+
It shall return a context object corresponding to the dynamic list selected by the last argument.
|
|
154
|
+
|
|
155
|
+
###### 2.1.2.2.2 - As Field
|
|
156
|
+
|
|
157
|
+
If used in an expression applied to other values as part of a selection or transformation process,
|
|
158
|
+
the at operator shall operate largely as described above, but with the arguments atom as the first
|
|
159
|
+
argument, representing the value to which the expression is applied, and with the operator
|
|
160
|
+
returning the raw value selected by its last argument rather than a context.
|
|
161
|
+
|
|
162
|
+
##### 2.1.2.3 - access
|
|
163
|
+
|
|
164
|
+
The access operator takes a string as its first argument, and may optionally take a dynamic list as
|
|
165
|
+
its second argument.
|
|
166
|
+
|
|
167
|
+
The string shall specify the address of a database to be accessed, and the dynamic list, if
|
|
168
|
+
present, shall specify credentials for accessing said database if necessary.
|
|
169
|
+
|
|
170
|
+
It shall return a context object representing the accessed database.
|
|
171
|
+
|
|
172
|
+
##### 2.1.2.4 - append
|
|
173
|
+
|
|
174
|
+
The append operator takes a context object as its first argument, with each subsequent argument
|
|
175
|
+
being a dynamic list.
|
|
176
|
+
|
|
177
|
+
It shall create a value in the database, and at the location therein, corresponding to the context,
|
|
178
|
+
from each dynamic list.
|
|
179
|
+
|
|
180
|
+
##### 2.1.2.5 - crop
|
|
181
|
+
|
|
182
|
+
The crop operator takes a context argument as its first argument, and a number as its second. It
|
|
183
|
+
may also optionally have a third numerical argument.
|
|
184
|
+
|
|
185
|
+
It shall return the context modified such that its contents are trimmed to no more than the length
|
|
186
|
+
specified by the first numerical argument. However, if it has a third argument, said contents shall
|
|
187
|
+
be cropped to a subsection starting at the index specified by the second numerical argument, before
|
|
188
|
+
the operation is otherwise applied as specified.
|
|
189
|
+
|
|
190
|
+
##### 2.1.2.6 - focus
|
|
191
|
+
|
|
192
|
+
The focus operator takes a context argument as its first argument, with each subsequent argument
|
|
193
|
+
being a string.
|
|
194
|
+
|
|
195
|
+
It shall return the context modified such that all values nested within it only contain values
|
|
196
|
+
keyed by the specified strings.
|
|
197
|
+
|
|
198
|
+
##### 2.1.2.7 - filter
|
|
199
|
+
|
|
200
|
+
The filter operator takes a context argument as its first argument, with an expression that
|
|
201
|
+
evaluates to a boolean as its second argument.
|
|
202
|
+
|
|
203
|
+
It shall return the context modified such that any value nested within it is removed if the
|
|
204
|
+
expression, when applied to it, resolves to false.
|
|
205
|
+
|
|
206
|
+
##### 2.1.2.8 - merge
|
|
207
|
+
|
|
208
|
+
The merge operator takes two context objects, which should correspond to tables, as its first two
|
|
209
|
+
arguments, the first one being referred to as the left context and the second being referred to as
|
|
210
|
+
the right context, and an expression which resolves to a boolean as its third argument.
|
|
211
|
+
|
|
212
|
+
It returns a new context generated by performing a full outer join on the two contexts with the
|
|
213
|
+
expression as the join condition.
|
|
214
|
+
|
|
215
|
+
###### 2.1.2.8.1 - merge-inner
|
|
216
|
+
|
|
217
|
+
The merge-inner operator behaves similarly to the merge operator, but performs an inner join
|
|
218
|
+
instead of a full outer join.
|
|
219
|
+
|
|
220
|
+
###### 2.1.2.8.2 - merge-lateral
|
|
221
|
+
|
|
222
|
+
The merge-lateral operator behaves similarly to the merge operator, but performs a left outer join
|
|
223
|
+
instead of a full outer join.
|
|
224
|
+
|
|
225
|
+
##### 2.1.2.9 - properties
|
|
226
|
+
|
|
227
|
+
The properties operator takes a context as its only argument, and returns a context containing the
|
|
228
|
+
system metadata corresponding to the original context. Such metadata shall, if applicable, include
|
|
229
|
+
data regarding the size of the latter context's content.
|
|
230
|
+
|
|
231
|
+
##### 2.1.2.10 - query
|
|
232
|
+
|
|
233
|
+
The query operator takes a list containing an OQL query as its only argument, executes the query,
|
|
234
|
+
and returns the contents of any context object the query returns as a dynamic list.
|
|
235
|
+
|
|
236
|
+
It may be used to embed OQL in other LISP dialects.
|
|
237
|
+
|
|
238
|
+
###### 2.1.2.10.1 - query-meta
|
|
239
|
+
|
|
240
|
+
The query-meta operator behaves similarly to the query operator, but instead takes a dynamic list
|
|
241
|
+
containing a dynamic OQL query as its only argument, executes the query, and returns the resulting
|
|
242
|
+
meta model the query resolves to as a dynamic list.
|
|
243
|
+
|
|
244
|
+
##### 2.1.2.11 - remove
|
|
245
|
+
|
|
246
|
+
The remove operator takes a context object as its only argument, and removes all values which
|
|
247
|
+
correspond to the context from the database which contains them.
|
|
248
|
+
|
|
249
|
+
##### 2.1.2.12 - set
|
|
250
|
+
|
|
251
|
+
The set operator takes a context object as its first argument, and an arbitrary expression as its
|
|
252
|
+
second argument.
|
|
253
|
+
|
|
254
|
+
It shall transform all values which correspond to the context to the value generated by passing
|
|
255
|
+
them to the expression.
|
|
256
|
+
|
|
257
|
+
##### 2.1.2.13 - sort
|
|
258
|
+
|
|
259
|
+
The sort operator takes a context object as its first argument, and a dynamic list consisting of
|
|
260
|
+
dynamic values where every such value is a boolean as its second argument.
|
|
261
|
+
|
|
262
|
+
It shall sort contents of the context and return it, where every key in the dynamic list specifies
|
|
263
|
+
the key of a field to sort the contents by, with the order of the values determining sorting
|
|
264
|
+
priority. A value of true means ascending order and a value of false means descending order.
|
package/omniQuery.js
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
var autoCORS = require("telos-autocors");
|
|
2
|
+
var Database = require('better-sqlite3');
|
|
3
|
+
var { MongoClient } = require("mongodb");
|
|
4
|
+
var pg = require("pg");
|
|
5
|
+
var sqlite3 = require('sqlite3').verbose();
|
|
6
|
+
|
|
7
|
+
// STUB: Selector Format Conversions?
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
|
|
11
|
+
FORMAT:
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
access: {
|
|
15
|
+
url: "...",
|
|
16
|
+
options: { ... },
|
|
17
|
+
type?: "..." / ["...", ...]
|
|
18
|
+
},
|
|
19
|
+
operation: {
|
|
20
|
+
type: "...",
|
|
21
|
+
data: { ... },
|
|
22
|
+
options: { ... }
|
|
23
|
+
},
|
|
24
|
+
filters: [
|
|
25
|
+
{
|
|
26
|
+
type: "...",
|
|
27
|
+
options: { ... }
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
var omniQuery = {
|
|
35
|
+
entangle: (source, target, mutual) => {
|
|
36
|
+
// STUB
|
|
37
|
+
},
|
|
38
|
+
intervals: [],
|
|
39
|
+
middleware: [
|
|
40
|
+
// STUB: Postgres, Mongo, JSON | File / Storage / Memory / Server
|
|
41
|
+
{ // MONGO
|
|
42
|
+
match: (data) => {
|
|
43
|
+
return data.access.url.startsWith("mongodb://") ||
|
|
44
|
+
data.access.url.startsWith("mongodb+srv://");
|
|
45
|
+
},
|
|
46
|
+
query: (data, options) => {
|
|
47
|
+
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
|
|
50
|
+
(async () => {
|
|
51
|
+
|
|
52
|
+
let client = new MongoClient(data.access.url);
|
|
53
|
+
let contents = null;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
|
|
57
|
+
let operation =
|
|
58
|
+
data.operation.type.toLowerCase().trim();
|
|
59
|
+
|
|
60
|
+
let types = omniQuery.utils.general.getFilterTypes(
|
|
61
|
+
data.filters
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
await client.connect();
|
|
65
|
+
|
|
66
|
+
let db = client.db(types["at"][0].value);
|
|
67
|
+
|
|
68
|
+
let collection =
|
|
69
|
+
db.collection(types["at"][1].value);
|
|
70
|
+
|
|
71
|
+
if(operation == "read") {
|
|
72
|
+
|
|
73
|
+
contents =
|
|
74
|
+
await collection.find({ }).toArray();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if(operation == "create") {
|
|
78
|
+
|
|
79
|
+
contents =
|
|
80
|
+
await collection.insertMany(
|
|
81
|
+
data.operation.data
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
catch(error) {
|
|
87
|
+
console.error(error);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await client.close();
|
|
91
|
+
|
|
92
|
+
resolve(contents);
|
|
93
|
+
})();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{ // POSTGRES
|
|
98
|
+
match: (data) => {
|
|
99
|
+
return data.access.url.startsWith("postgres://") ||
|
|
100
|
+
data.access.url.startsWith("postgresql://");
|
|
101
|
+
},
|
|
102
|
+
query: (data, options) => {
|
|
103
|
+
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
|
|
106
|
+
(async () => {
|
|
107
|
+
|
|
108
|
+
let client = new pg.Client({
|
|
109
|
+
connectionString: data.access.url
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
|
|
114
|
+
await client.connect();
|
|
115
|
+
|
|
116
|
+
let result = await client.query(
|
|
117
|
+
omniQuery.utils.sql.constructSQL(
|
|
118
|
+
data, { addColumns: true }
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
await client.end();
|
|
123
|
+
|
|
124
|
+
resolve(result.rows);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
catch(error) {
|
|
128
|
+
|
|
129
|
+
console.error(error);
|
|
130
|
+
client.end();
|
|
131
|
+
|
|
132
|
+
resolve(null);
|
|
133
|
+
}
|
|
134
|
+
})();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{ // SQLITE
|
|
139
|
+
match: (data) => {
|
|
140
|
+
return !data.access.url.includes("://");
|
|
141
|
+
},
|
|
142
|
+
query: (data, options) => {
|
|
143
|
+
|
|
144
|
+
let operation = data.operation.type.toLowerCase().trim();
|
|
145
|
+
let query = omniQuery.utils.sql.constructSQL(data);
|
|
146
|
+
|
|
147
|
+
let db = options.sync ?
|
|
148
|
+
new Database(data.access.url) :
|
|
149
|
+
new sqlite3.Database(data.access.url);
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
|
|
153
|
+
if(operation == "read") {
|
|
154
|
+
|
|
155
|
+
if(!options.sync) {
|
|
156
|
+
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
|
|
159
|
+
db.all(query, (error, rows) => {
|
|
160
|
+
|
|
161
|
+
db.close();
|
|
162
|
+
|
|
163
|
+
if(error) {
|
|
164
|
+
|
|
165
|
+
console.error(error);
|
|
166
|
+
|
|
167
|
+
resolve(null);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
resolve(rows);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
else {
|
|
176
|
+
|
|
177
|
+
let rows = db.prepare(query).all();
|
|
178
|
+
db.close();
|
|
179
|
+
|
|
180
|
+
return rows;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
else {
|
|
185
|
+
|
|
186
|
+
if(!options.sync) {
|
|
187
|
+
|
|
188
|
+
return new Promise((resolve, reject) => {
|
|
189
|
+
|
|
190
|
+
db.exec(query, (error) => {
|
|
191
|
+
|
|
192
|
+
db.close();
|
|
193
|
+
|
|
194
|
+
if(error)
|
|
195
|
+
console.error(error);
|
|
196
|
+
|
|
197
|
+
resolve(null);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
else
|
|
203
|
+
db.exec(query);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
catch(error) {
|
|
208
|
+
console.error(error);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
db.close();
|
|
212
|
+
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
{ // STUB: JSON - READ / WRITE -- HTTP
|
|
217
|
+
match: (data) => {
|
|
218
|
+
|
|
219
|
+
},
|
|
220
|
+
query: (data, options) => {
|
|
221
|
+
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
],
|
|
225
|
+
query: (data, options) => {
|
|
226
|
+
|
|
227
|
+
options = options != null ? options : { };
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
|
|
231
|
+
return (
|
|
232
|
+
options.middleware != null ?
|
|
233
|
+
options.middleware : omniQuery.middleware
|
|
234
|
+
).filter(item => {
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
return item.match(data);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
catch(error) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
})[0].query(data, options);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
catch(error) {
|
|
247
|
+
|
|
248
|
+
console.log(error.stack);
|
|
249
|
+
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
subscribe: (target, selector) => {
|
|
254
|
+
// STUB
|
|
255
|
+
},
|
|
256
|
+
utils: {
|
|
257
|
+
general: {
|
|
258
|
+
getFilterTypes: (filters) => {
|
|
259
|
+
|
|
260
|
+
let types = {};
|
|
261
|
+
|
|
262
|
+
filters.forEach(item => {
|
|
263
|
+
|
|
264
|
+
let type = item.type.toLowerCase().trim();
|
|
265
|
+
|
|
266
|
+
types[type] = types[type] != null ? types[type] : [];
|
|
267
|
+
types[type].push(item.options);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return types
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
sql: {
|
|
274
|
+
constructSQL: (data, options) => { // STUB: PREVENT INJECTION
|
|
275
|
+
|
|
276
|
+
let types = omniQuery.utils.general.getFilterTypes(
|
|
277
|
+
data.filters
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
if(data.operation.type.toLowerCase().trim() == "read") {
|
|
281
|
+
|
|
282
|
+
return `SELECT ${
|
|
283
|
+
types["focus"] == null ?
|
|
284
|
+
"*" : types["focus"][0].value.join(",")
|
|
285
|
+
} FROM ${types["at"][0].value}${
|
|
286
|
+
types["filter"] != null ?
|
|
287
|
+
` WHERE ${
|
|
288
|
+
types["filter"].map(
|
|
289
|
+
item =>
|
|
290
|
+
omniQuery.utils.sql.constructSQLFilter(
|
|
291
|
+
item.value
|
|
292
|
+
)
|
|
293
|
+
).join(" AND ")
|
|
294
|
+
}` :
|
|
295
|
+
""
|
|
296
|
+
}${
|
|
297
|
+
types["crop"] != null ?
|
|
298
|
+
` LIMIT ${types["crop"][0].value}` :
|
|
299
|
+
""
|
|
300
|
+
};`;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if(data.operation.type.toLowerCase().trim() == "create" ||
|
|
304
|
+
(
|
|
305
|
+
data.operation.type.toLowerCase().trim() == "update" &&
|
|
306
|
+
types["filter"] == null
|
|
307
|
+
)
|
|
308
|
+
) {
|
|
309
|
+
|
|
310
|
+
let columns = { };
|
|
311
|
+
|
|
312
|
+
data.operation.data =
|
|
313
|
+
Array.isArray(data.operation.data[0]) ?
|
|
314
|
+
data.operation.data[0] : data.operation.data;
|
|
315
|
+
|
|
316
|
+
data.operation.data.forEach(item => {
|
|
317
|
+
columns = Object.assign(columns, item);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
return `${
|
|
321
|
+
data.operation.type.toLowerCase().trim() == "update" ?
|
|
322
|
+
`DROP TABLE IF EXISTS ${types["at"][0].value}; ` :
|
|
323
|
+
""
|
|
324
|
+
}CREATE TABLE IF NOT EXISTS ${
|
|
325
|
+
types["at"][0].value
|
|
326
|
+
} (${
|
|
327
|
+
Object.keys(columns).map(column =>
|
|
328
|
+
`${
|
|
329
|
+
column
|
|
330
|
+
} ${
|
|
331
|
+
{
|
|
332
|
+
"string": "TEXT",
|
|
333
|
+
"number": "DECIMAL",
|
|
334
|
+
"boolean": "BOOLEAN"
|
|
335
|
+
}[typeof columns[column]]
|
|
336
|
+
}`
|
|
337
|
+
).join(",")
|
|
338
|
+
}); ${options.addColumns ?
|
|
339
|
+
Object.keys(columns).map(column =>
|
|
340
|
+
`ALTER TABLE ${
|
|
341
|
+
types["at"][0].value
|
|
342
|
+
} ADD COLUMN IF NOT EXISTS ${
|
|
343
|
+
column
|
|
344
|
+
} ${
|
|
345
|
+
{
|
|
346
|
+
"string": "TEXT",
|
|
347
|
+
"number": "DECIMAL",
|
|
348
|
+
"boolean": "BOOLEAN"
|
|
349
|
+
}[typeof columns[column]]
|
|
350
|
+
};`
|
|
351
|
+
).join(" ") :
|
|
352
|
+
""
|
|
353
|
+
} INSERT INTO ${
|
|
354
|
+
types["at"][0].value
|
|
355
|
+
} (${
|
|
356
|
+
Object.keys(columns).join(",")
|
|
357
|
+
}) VALUES ${
|
|
358
|
+
data.operation.data.map(item => {
|
|
359
|
+
return `(${Object.keys(columns).map(
|
|
360
|
+
(column) => {
|
|
361
|
+
|
|
362
|
+
if(typeof item[column] == "string")
|
|
363
|
+
return `'${item[column]}'`;
|
|
364
|
+
|
|
365
|
+
if(typeof item[column] == "boolean")
|
|
366
|
+
return `${item[column]}`.toUpperCase();
|
|
367
|
+
|
|
368
|
+
if(typeof item[column] == "number") {
|
|
369
|
+
|
|
370
|
+
return `${
|
|
371
|
+
item[column]
|
|
372
|
+
}${
|
|
373
|
+
item[column] % 1 == 0 ? ".0" : ""
|
|
374
|
+
}`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return `${item[column]}`;
|
|
378
|
+
}
|
|
379
|
+
).join(",")})`
|
|
380
|
+
}).join(",")
|
|
381
|
+
};`;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if(
|
|
385
|
+
data.operation.type.toLowerCase().trim() == "update" &&
|
|
386
|
+
types["filter"] != null
|
|
387
|
+
) {
|
|
388
|
+
|
|
389
|
+
let columns = data.operation.data[0];
|
|
390
|
+
|
|
391
|
+
return `${options.addColumns ?
|
|
392
|
+
Object.keys(columns).map(column =>
|
|
393
|
+
`ALTER TABLE ${
|
|
394
|
+
types["at"][0].value
|
|
395
|
+
} ADD COLUMN IF NOT EXISTS ${
|
|
396
|
+
column
|
|
397
|
+
} ${
|
|
398
|
+
{
|
|
399
|
+
"string": "TEXT",
|
|
400
|
+
"number": "DECIMAL",
|
|
401
|
+
"boolean": "BOOLEAN"
|
|
402
|
+
}[typeof columns[column]]
|
|
403
|
+
};`
|
|
404
|
+
).join(" ") :
|
|
405
|
+
""
|
|
406
|
+
} UPDATE ${
|
|
407
|
+
types["at"][0].value
|
|
408
|
+
} SET ${
|
|
409
|
+
Object.keys(columns).map(
|
|
410
|
+
(column) => {
|
|
411
|
+
|
|
412
|
+
if(typeof columns[column] == "string")
|
|
413
|
+
return `${column} = '${columns[column]}'`;
|
|
414
|
+
|
|
415
|
+
if(typeof columns[column] == "boolean") {
|
|
416
|
+
|
|
417
|
+
return `${
|
|
418
|
+
column
|
|
419
|
+
} = ${
|
|
420
|
+
("" + columns[column]).toUpperCase()
|
|
421
|
+
}`;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if(typeof columns[column] == "number") {
|
|
425
|
+
|
|
426
|
+
return `${column} = ${
|
|
427
|
+
columns[column]
|
|
428
|
+
}${
|
|
429
|
+
columns[column] % 1 == 0 ? ".0" : ""
|
|
430
|
+
}`;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return `${column} = ${columns[column]}`;
|
|
434
|
+
}
|
|
435
|
+
).join(",")
|
|
436
|
+
} WHERE ${
|
|
437
|
+
types["filter"].map(
|
|
438
|
+
item =>
|
|
439
|
+
omniQuery.utils.sql.constructSQLFilter(
|
|
440
|
+
item.value
|
|
441
|
+
)
|
|
442
|
+
).join(" AND ")
|
|
443
|
+
};`;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if(data.operation.type.toLowerCase().trim() == "delete") {
|
|
447
|
+
|
|
448
|
+
return types["filter"] != null ?
|
|
449
|
+
`DELETE FROM ${types["at"][0].value} WHERE ${
|
|
450
|
+
types["filter"].map(
|
|
451
|
+
item =>
|
|
452
|
+
omniQuery.utils.sql.constructSQLFilter(
|
|
453
|
+
item.value
|
|
454
|
+
)
|
|
455
|
+
).join(" AND ")
|
|
456
|
+
}` :
|
|
457
|
+
`DROP TABLE ${types["at"][0].value};`;
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
constructSQLFilter: (filter) => {
|
|
461
|
+
|
|
462
|
+
filter = filter.map(
|
|
463
|
+
item => Array.isArray(item) ?
|
|
464
|
+
omniQuery.utils.sql.constructSQLFilter(item) : item
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
switch(filter[0].toLowerCase().trim()) {
|
|
468
|
+
|
|
469
|
+
case "and": return filter.slice(1).join(" AND ");
|
|
470
|
+
case "or": return filter.slice(1).join(" OR ");
|
|
471
|
+
|
|
472
|
+
case "equals": return filter.map((item, index) => {
|
|
473
|
+
return `${item} = ${filter[index + 1]}`;
|
|
474
|
+
}).slice(1, filter.length - 1).join(" AND ");
|
|
475
|
+
|
|
476
|
+
case "less": return filter.map((item, index) => {
|
|
477
|
+
return `${item} < ${filter[index + 1]}`;
|
|
478
|
+
}).slice(1, filter.length - 1).join(" AND ");
|
|
479
|
+
|
|
480
|
+
case "greater": return filter.map((item, index) => {
|
|
481
|
+
return `${item} > ${filter[index + 1]}`;
|
|
482
|
+
}).slice(1, filter.length - 1).join(" AND ");
|
|
483
|
+
|
|
484
|
+
case "gte": return filter.map((item, index) => {
|
|
485
|
+
return `${item} >= ${filter[index + 1]}`;
|
|
486
|
+
}).slice(1, filter.length - 1).join(" AND ");
|
|
487
|
+
|
|
488
|
+
case "lte": return filter.map((item, index) => {
|
|
489
|
+
return `${item} <= ${filter[index + 1]}`;
|
|
490
|
+
}).slice(1, filter.length - 1).join(" AND ");
|
|
491
|
+
|
|
492
|
+
default: return "";
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
if(typeof module == "object")
|
|
500
|
+
module.exports = omniQuery;
|
package/oql.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
let utils = {
|
|
2
|
+
normalizeContext: (context) => {
|
|
3
|
+
|
|
4
|
+
context.access = context.access != null ? context.access : {};
|
|
5
|
+
context.operation = context.operation != null ? context.operation : {};
|
|
6
|
+
context.filters = context.filters != null ? context.filters : [];
|
|
7
|
+
|
|
8
|
+
return context;
|
|
9
|
+
},
|
|
10
|
+
normalizeValue: (value) => {
|
|
11
|
+
|
|
12
|
+
value = JSON.parse("" + value);
|
|
13
|
+
|
|
14
|
+
if(typeof value == "string") {
|
|
15
|
+
|
|
16
|
+
if(value.startsWith("\"") && value.endsWith("\""))
|
|
17
|
+
value = value.substring(1, value.length - 1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
module.exports = [
|
|
25
|
+
{
|
|
26
|
+
process: (context, args) => {
|
|
27
|
+
|
|
28
|
+
if(context.local.operator != "access")
|
|
29
|
+
return null;
|
|
30
|
+
|
|
31
|
+
return JSON.stringify(utils.normalizeContext({
|
|
32
|
+
access: {
|
|
33
|
+
url: utils.normalizeValue(args[0]),
|
|
34
|
+
options: args[1] != null ?
|
|
35
|
+
JSON.parse("" + args[1]) :
|
|
36
|
+
null
|
|
37
|
+
},
|
|
38
|
+
operation: {
|
|
39
|
+
type: "read"
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
},
|
|
43
|
+
tags: ["oql", "access"]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
process: (context, args) => {
|
|
47
|
+
|
|
48
|
+
if(context.local.operator != "append")
|
|
49
|
+
return null;
|
|
50
|
+
|
|
51
|
+
if(!args[0].startsWith("{"))
|
|
52
|
+
return null; // STUB
|
|
53
|
+
|
|
54
|
+
let data = utils.normalizeContext(JSON.parse(args[0]));
|
|
55
|
+
|
|
56
|
+
data.operation.type = "create"
|
|
57
|
+
data.operation.data = args.slice(1).map(item => JSON.parse(item));
|
|
58
|
+
|
|
59
|
+
return JSON.stringify(data);
|
|
60
|
+
},
|
|
61
|
+
tags: ["oql", "append"]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
process: (context, args) => {
|
|
65
|
+
|
|
66
|
+
if(context.local.operator != "at")
|
|
67
|
+
return null;
|
|
68
|
+
|
|
69
|
+
if(!args[0].startsWith("{"))
|
|
70
|
+
return `${args[0]}[${args[1]}]`;
|
|
71
|
+
|
|
72
|
+
let data = utils.normalizeContext(JSON.parse(args[0]));
|
|
73
|
+
|
|
74
|
+
args.slice(1).forEach(item => {
|
|
75
|
+
|
|
76
|
+
data.filters.push({
|
|
77
|
+
type: "at",
|
|
78
|
+
options: {
|
|
79
|
+
value: utils.normalizeValue(item)
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return JSON.stringify(data);
|
|
85
|
+
},
|
|
86
|
+
tags: ["oql", "at"]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
process: (context, args) => {
|
|
90
|
+
|
|
91
|
+
if(context.local.operator != "filter")
|
|
92
|
+
return null;
|
|
93
|
+
|
|
94
|
+
let data = utils.normalizeContext(JSON.parse(args[0]));
|
|
95
|
+
|
|
96
|
+
data.filters.push({
|
|
97
|
+
type: "filter",
|
|
98
|
+
options: {
|
|
99
|
+
value: context.local.list[2]
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return JSON.stringify(data);
|
|
104
|
+
},
|
|
105
|
+
tags: ["oql", "filter"]
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
process: (context, args) => {
|
|
109
|
+
|
|
110
|
+
if(context.local.operator != "remove")
|
|
111
|
+
return null;
|
|
112
|
+
|
|
113
|
+
if(!args[0].startsWith("{"))
|
|
114
|
+
return null; // STUB
|
|
115
|
+
|
|
116
|
+
let data = utils.normalizeContext(JSON.parse(args[0]));
|
|
117
|
+
data.operation.type = "delete"
|
|
118
|
+
|
|
119
|
+
return JSON.stringify(data);
|
|
120
|
+
},
|
|
121
|
+
tags: ["oql", "remove"]
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
process: (context, args) => {
|
|
125
|
+
|
|
126
|
+
if(context.local.operator != "set")
|
|
127
|
+
return null;
|
|
128
|
+
|
|
129
|
+
if(!args[0].startsWith("{")) {
|
|
130
|
+
|
|
131
|
+
return context.state[args[0]] != null ?
|
|
132
|
+
`${args[0]}=context.state[${args[0]}];` :
|
|
133
|
+
`${
|
|
134
|
+
args[0]
|
|
135
|
+
}=${
|
|
136
|
+
args[1]
|
|
137
|
+
},context.state["${
|
|
138
|
+
args[0]
|
|
139
|
+
}"]=${
|
|
140
|
+
args[0]
|
|
141
|
+
};`
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let data = utils.normalizeContext(JSON.parse(args[0]));
|
|
145
|
+
|
|
146
|
+
data.operation.type = "update"
|
|
147
|
+
data.operation.data = args.slice(1).map(item => JSON.parse(item));
|
|
148
|
+
|
|
149
|
+
return JSON.stringify(data);
|
|
150
|
+
},
|
|
151
|
+
tags: ["oql", "set"]
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
process: (context, args) => {
|
|
155
|
+
|
|
156
|
+
if(context.local.operator != "sort")
|
|
157
|
+
return null;
|
|
158
|
+
|
|
159
|
+
let data = utils.normalizeContext(JSON.parse(args[0]));
|
|
160
|
+
|
|
161
|
+
data.filters.push({
|
|
162
|
+
type: "sort",
|
|
163
|
+
options: {
|
|
164
|
+
value: JSON.parse(args[1])
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return JSON.stringify(data);
|
|
169
|
+
},
|
|
170
|
+
tags: ["oql", "sort"]
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
process: (context, args) => {
|
|
174
|
+
|
|
175
|
+
if(context.local.operator != "query")
|
|
176
|
+
return null;
|
|
177
|
+
|
|
178
|
+
return `(require("telos-oql/omniQuery.js").query(${args[0]}))\n`;
|
|
179
|
+
},
|
|
180
|
+
tags: ["oql", "query"]
|
|
181
|
+
}
|
|
182
|
+
];
|
package/oql.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "telos-oql",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A LISP dialect for database-agnostic querying",
|
|
5
|
+
"main": "oql.json",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"fusion-lisp": "~1.0.0",
|
|
8
|
+
"mongodb": "~1.0.0",
|
|
9
|
+
"pg": "~1.0.0",
|
|
10
|
+
"sqlite3": "~1.0.0",
|
|
11
|
+
"telos-autocors": "~1.0.0"
|
|
12
|
+
},
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "Apache-2.0",
|
|
15
|
+
"homepage": "https://github.com/Telos-Project/OmniQuery"
|
|
16
|
+
}
|