Skip to content

Commit 3c5b3c2

Browse files
Create generate-openrowset.html
1 parent b9b8830 commit 3c5b3c2

File tree

1 file changed

+211
-0
lines changed

1 file changed

+211
-0
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width">
6+
<title>Basic Test</title>
7+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
8+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
9+
</head>
10+
<body>
11+
<div class="container">
12+
<h1>Generating OPENROWSET statement for Cosmos DB documents</h1>
13+
<div class="form-group row">
14+
<label for="container" class="col-sm-2 col-form-label">Enter container name:</label>
15+
<div class="col-sm-10">
16+
<input name="container" id="container" type="text" placeholder="Enter container name here" value="Family"/>
17+
</div>
18+
</div>
19+
<div class="form-group">
20+
<label for="document">Enter a sample of a document from your container:</label>
21+
<br/>
22+
<textarea name="document" id="document" required="true" placeholder="Enter sample of JSON document" cols="120" rows="10">
23+
{
24+
"id": "WakefieldFamily",
25+
"parents": [
26+
{ "familyName": "Wakefield", "givenName": "Robin" },
27+
{ "familyName": "Miller", "givenName": "Ben" }
28+
],
29+
"children": [
30+
{
31+
"familyName": "Merriam",
32+
"givenName": "Jesse",
33+
"gender": "female", "grade": 1,
34+
"pets": [
35+
{ "givenName": "Goofy" },
36+
{ "givenName": "Shadow" }
37+
]
38+
},
39+
{
40+
"familyName": "Miller",
41+
"givenName": "Lisa",
42+
"gender": "female",
43+
"grade": 8 }
44+
],
45+
"address": { "state": "NY", "county": "Manhattan", "city": "NY" },
46+
"creationDate": 1431620462,
47+
"isRegistered": false
48+
}</textarea>
49+
50+
51+
</div>
52+
<div class="form-group">
53+
<button onclick="createSQL()" type="button" class="button btn-primary">Generate SQL script</button>
54+
</div>
55+
<h2>Copy the text below in some SQL editor once you generate the script</h2>
56+
<pre class="border"><code class="language-html" data-lang="html" id="sql"></code></pre>
57+
58+
</div>
59+
60+
61+
</div>
62+
<script>
63+
function js2sql(type, value){
64+
switch(type){
65+
case "undefined": return "VARCHAR(100)";
66+
case "string": return "VARCHAR(8000)";
67+
case "number":
68+
if(Number.isInteger(value))
69+
return "BIGINT";
70+
else
71+
return "FLOAT";
72+
case "bigint": return "BIGINT";
73+
case "boolean": return "BIT";
74+
case "object": return "VARCHAR(MAX)";
75+
}
76+
}
77+
78+
class Generator {
79+
80+
constructor(alias, col, parent, level) {
81+
this.columns = [];
82+
this.subArrays = [];
83+
this.col = col;
84+
this.parent = parent;
85+
this.alias = alias;
86+
this.level = level || 0;
87+
}
88+
89+
qoute_identifier(name){
90+
var pattern = /^[0-9a-zA-Z_]+$/;
91+
if(name.match(pattern)) {
92+
return name;
93+
}
94+
else
95+
{
96+
return "["+name+"]";
97+
}
98+
}
99+
generate(document, prefix) {
100+
if(prefix == null) prefix = [];
101+
var x;
102+
103+
let docType = typeof document;
104+
if(docType != "object") {
105+
// OUTER APPLY OPENJSON (...) WITH ( value type '$')
106+
this.columns.push("value " + js2sql(docType) + " '$'");
107+
return;
108+
}
109+
for (x in document) {
110+
let type = typeof document[x];
111+
if(type != "object") {
112+
113+
const objectColumnName = prefix.join("_") + ((prefix.length===0)?"":"_") + x;
114+
const objectPath = prefix.join(".") + ((prefix.length===0)?"":".") + x;
115+
116+
this.columns.push(this.qoute_identifier( prefix.join("_") + ((prefix.length===0)?"":"_") + x ) + " " +
117+
js2sql(type, document[x]) +
118+
(
119+
(objectColumnName == objectPath)?
120+
"":
121+
(" '$" + ((prefix.length===0)?"":".") + prefix.join(".") + "." + x + "'")
122+
)
123+
124+
);
125+
}
126+
else if(type == "object" && !Array.isArray(document[x]) ) {
127+
/* Expand nested sub-objects */
128+
prefix.push(x);
129+
this.generate(document[x], prefix);
130+
prefix.pop();
131+
}
132+
else if(type == "object" && Array.isArray(document[x]) ) {
133+
134+
const columnType = (this.level==0) ? " VARCHAR(MAX)" : " NVARCHAR(MAX)";
135+
136+
const objectColumnName = prefix.join("_") + ((prefix.length===0)?"":"_") + x;
137+
const objectPath = prefix.join(".") + ((prefix.length===0)?"":".") + x;
138+
139+
this.columns.push(this.qoute_identifier( objectColumnName ) +
140+
columnType +
141+
((objectColumnName == objectPath)?
142+
"":
143+
" '$" + ((prefix.length===0)?"":".") + objectPath + "'") +
144+
((this.level>0) ? " AS JSON " : ""))
145+
;
146+
147+
prefix.push(x);
148+
var child = new Generator( this.alias + "_" + objectColumnName,
149+
objectColumnName,
150+
this.alias,
151+
this.level + 1);
152+
this.subArrays.push(child);
153+
child.generate(document[x][0], []);
154+
prefix.pop();
155+
}
156+
}
157+
}
158+
159+
indent(tab) { return "\t".repeat(this.level+1+(tab||0)); }
160+
161+
query() {
162+
var sql = "";
163+
if(this.col == null) {
164+
var sql = "SELECT * \nFROM OPENROWSET('CosmosDB',\n\t\t--> Insert account, database, region, and key values here when you execute the query\n\t\t'" + connectionstring + "',\n\t\t" + collection +")\n\tWITH (\n";
165+
sql += this.indent(1) + this.columns.join(",\n"+this.indent(1));;
166+
sql += "\n" + this.indent() + ") AS " + this.qoute_identifier(this.alias) + " ";
167+
} else {
168+
sql += "\n" +this.indent(-1) + "OUTER APPLY OPENJSON ( " + this.qoute_identifier( this.parent ) + "." + this.qoute_identifier(this.col) +" )\n" + this.indent() + " WITH (\n" + this.indent(1) + this.columns.join(",\n" + this.indent(1));
169+
sql += this.indent() + "\n" + this.indent() + ") AS " + this.qoute_identifier( this.alias );
170+
}
171+
for(let child in this.subArrays) {
172+
sql += this.subArrays[child].query();
173+
}
174+
return sql;
175+
}
176+
177+
toString() {
178+
var sql = this.query();
179+
return sql;
180+
}
181+
}
182+
183+
var connectionstring;
184+
var collection;
185+
186+
function createSQL() {
187+
connectionstring = "account={account};database={db};region={region like westus2};key={key}";
188+
collection = document.querySelector("#container").value; //'People';
189+
var doc = eval("("+document.querySelector("#document").value+")");
190+
191+
192+
193+
if(collection == "") {
194+
alert("Enter collection");
195+
return;
196+
}
197+
198+
if(doc == "") {
199+
alert("Enter sample document");
200+
return;
201+
}
202+
203+
var g = new Generator(collection, null, null, 0);
204+
g.generate(doc, []);
205+
document.querySelector("#sql").innerHTML = "--NOTE: To optimize performance, try to replace types with smaller - for example VARCHAR(MAX) with VARCHAR(600), etc.";
206+
document.querySelector("#sql").innerHTML += "--NOTE: This is best effort type mapping. Sometime you would need to replace FLOAT with VARCHAR(400), BIGINT wiht FLOAT or vice versa.";
207+
document.querySelector("#sql").innerHTML += g.toString();
208+
}
209+
</script>
210+
</body>
211+
</html>

0 commit comments

Comments
 (0)