├── .gitignore
├── README.md
├── composer.json
├── docs
├── api
│ ├── .htaccess
│ ├── classes
│ │ ├── IBT.JsonDB.Client.html
│ │ ├── IBT.JsonDB.Collection.html
│ │ ├── IBT.JsonDB.Exception.html
│ │ ├── IBT.JsonDB.Filter.html
│ │ ├── IBT.JsonDB.Indexer.Exception.html
│ │ ├── IBT.JsonDB.Indexer.Index.html
│ │ └── IBT.JsonDB.Indexer.Scanner.html
│ ├── css
│ │ ├── bootstrap-combined.no-icons.min.css
│ │ ├── font-awesome.min.css
│ │ ├── jquery.iviewer.css
│ │ ├── phpdocumentor-clean-icons
│ │ │ ├── Read Me.txt
│ │ │ ├── fonts
│ │ │ │ ├── phpdocumentor-clean-icons.dev.svg
│ │ │ │ ├── phpdocumentor-clean-icons.eot
│ │ │ │ ├── phpdocumentor-clean-icons.svg
│ │ │ │ ├── phpdocumentor-clean-icons.ttf
│ │ │ │ └── phpdocumentor-clean-icons.woff
│ │ │ ├── lte-ie7.js
│ │ │ └── style.css
│ │ ├── prism.css
│ │ └── template.css
│ ├── files
│ │ ├── Client.html
│ │ ├── Client.php.txt
│ │ ├── Collection.html
│ │ ├── Collection.php.txt
│ │ ├── Exception.html
│ │ ├── Exception.php.txt
│ │ ├── Filter.html
│ │ ├── Filter.php.txt
│ │ ├── Indexer.Exception.html
│ │ ├── Indexer.Index.html
│ │ ├── Indexer.Scanner.html
│ │ ├── Indexer
│ │ │ ├── Exception.php.txt
│ │ │ ├── Index.php.txt
│ │ │ └── Scanner.php.txt
│ │ ├── helpers.html
│ │ └── helpers.php.txt
│ ├── font
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ └── fontawesome-webfont.woff
│ ├── graphs
│ │ ├── class.html
│ │ └── classes.svg
│ ├── images
│ │ ├── apple-touch-icon-114x114.png
│ │ ├── apple-touch-icon-72x72.png
│ │ ├── apple-touch-icon.png
│ │ ├── custom-icons.svg
│ │ ├── favicon.ico
│ │ ├── hierarchy-item.png
│ │ ├── icon-class-13x13.png
│ │ ├── icon-class.svg
│ │ ├── icon-interface-13x13.png
│ │ ├── icon-interface.svg
│ │ ├── icon-trait-13x13.png
│ │ ├── icon-trait.svg
│ │ └── iviewer
│ │ │ ├── grab.cur
│ │ │ ├── hand.cur
│ │ │ ├── iviewer.rotate_left.png
│ │ │ ├── iviewer.rotate_right.png
│ │ │ ├── iviewer.zoom_fit.png
│ │ │ ├── iviewer.zoom_in.png
│ │ │ ├── iviewer.zoom_out.png
│ │ │ └── iviewer.zoom_zero.png
│ ├── index.html
│ ├── js
│ │ ├── bootstrap.min.js
│ │ ├── html5.js
│ │ ├── jquery-1.11.0.min.js
│ │ ├── jquery.dotdotdot-1.5.9.js
│ │ ├── jquery.dotdotdot-1.5.9.min.js
│ │ ├── jquery.iviewer.js
│ │ ├── jquery.iviewer.min.js
│ │ ├── jquery.mousewheel.js
│ │ ├── jquery.smooth-scroll.js
│ │ ├── prism.min.js
│ │ └── ui
│ │ │ └── 1.10.4
│ │ │ └── jquery-ui.min.js
│ ├── namespaces
│ │ ├── IBT.JsonDB.Indexer.html
│ │ ├── IBT.JsonDB.html
│ │ ├── IBT.html
│ │ └── default.html
│ └── reports
│ │ ├── deprecated.html
│ │ ├── errors.html
│ │ └── markers.html
├── getting_started.md
├── index.md
└── tutorial.md
├── jsondb.png
├── phpunit.xml.dist
├── src
├── Client.php
├── Collection.php
├── Exception.php
├── Filter.php
├── Indexer
│ ├── Exception.php
│ ├── Index.php
│ └── Scanner.php
└── helpers.php
└── tests
├── ClientTest.php
├── CollectionTest.php
├── IndexTest.php
├── ScannerTest.php
└── test.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Mac files
2 | .DS_Store
3 | .Trashes
4 |
5 | # editor files
6 | *.sublime-project
7 | *.sublime-workspace
8 | .idea
9 |
10 | # other
11 | vendor/
12 | composer.lock
13 | composer.phar
14 | tmp/
15 | *.sqlite
16 | *.db
17 | *.ini
18 | phpunit.xml
19 | docs/api/phpdoc-cache*
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JsonDB Library
2 |
3 | [](#JsonDB)
4 |
5 | ## About
6 |
7 | JSONDb is a PHP database abstraction library for MySQL which allows you to easily store and query JSON data. Because it doesn't require the Json Data type available in Mysql version 5.7, this means JSONDb can be used in hosted environments that offer older versions of Mysql for example.
8 |
9 | **JSONDb is primarily designed to be used for rapid application prototyping and is not advisable for use in production.**
10 |
11 | ## Quick Start
12 |
13 | ```PHP
14 |
15 | // Autoload
16 | require_once __DIR__ . '/vendor/autoload.php';
17 |
18 | // import namespace
19 | use IBT\JsonDB\Client;
20 | use IBT\JsonDB\Collection;
21 |
22 | // create client and initialize database
23 | $c = new Client([
24 | 'database' => 'database',
25 | 'username' => 'username',
26 | 'password' => 'password'
27 | ]);
28 | $c->setup();
29 |
30 | // create collection
31 | $col = $c->newCollection("users");
32 |
33 | // insert json
34 | $col->insert('{"name":"jason bourne", "category":"agent"}');
35 | $col->insert('{"name":"james bond", "category":"agent"}');
36 | $col->insert('{"name":"mathew murdock", "category":"superhero"}');
37 |
38 | // search data
39 | $f = $col->newFilter();
40 | $r = $f->whereIn('category', ['agent'])->run();
41 | var_dump($r)
42 | ```
43 |
44 | ## Learn More
45 |
46 | * View [documentation](https://github.com/johnwilson/jsondb/blob/master/docs/index.md) for additional information and tips.
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "johnwilson/jsondb",
3 | "type": "library",
4 | "description": "JSON document db abstraction layer over MySQL",
5 | "keywords": ["json", "database", "nosql", "persistence", "schemaless"],
6 | "license": "MIT",
7 | "authors": [
8 | {"name": "John Wilson", "email": "wilsonfiifi@gmail.com"}
9 | ],
10 | "require": {
11 | "illuminate/database": "5.2",
12 | "php": ">=5.4"
13 | },
14 | "autoload": {
15 | "psr-4": {
16 | "IBT\\JsonDB\\": "src"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/docs/api/.htaccess:
--------------------------------------------------------------------------------
1 | # Fixes a vulnerability in CentOS: http://stackoverflow.com/questions/20533279/prevent-php-from-parsing-non-php-files-such-as-somefile-php-txt
2 |
3 | RemoveHandler .php
4 | ForceType text/plain
5 |
--------------------------------------------------------------------------------
/docs/api/css/jquery.iviewer.css:
--------------------------------------------------------------------------------
1 | .viewer {
2 | -ms-touch-action: none;
3 | }
4 |
5 | .iviewer_common {
6 | position:absolute;
7 | bottom:10px;
8 | border: 1px solid #000;
9 | height: 28px;
10 | z-index: 5000;
11 | }
12 |
13 | .iviewer_cursor {
14 | cursor: url(../images/iviewer/hand.cur) 6 8, pointer;
15 | }
16 |
17 | .iviewer_drag_cursor {
18 | cursor: url(../images/iviewer/grab.cur) 6 8, pointer;
19 | }
20 |
21 | .iviewer_button {
22 | width: 28px;
23 | cursor: pointer;
24 | background-position: center center;
25 | background-repeat: no-repeat;
26 | }
27 |
28 | .iviewer_zoom_in {
29 | left: 20px;
30 | background: url(../images/iviewer/iviewer.zoom_in.png);
31 | }
32 |
33 | .iviewer_zoom_out {
34 | left: 55px;
35 | background: url(../images/iviewer/iviewer.zoom_out.png);
36 | }
37 |
38 | .iviewer_zoom_zero {
39 | left: 90px;
40 | background: url(../images/iviewer/iviewer.zoom_zero.png);
41 | }
42 |
43 | .iviewer_zoom_fit {
44 | left: 125px;
45 | background: url(../images/iviewer/iviewer.zoom_fit.png);
46 | }
47 |
48 | .iviewer_zoom_status {
49 | left: 160px;
50 | font: 1em/28px Sans;
51 | color: #000;
52 | background-color: #fff;
53 | text-align: center;
54 | width: 60px;
55 | }
56 |
57 | .iviewer_rotate_left {
58 | left: 227px;
59 | background: #fff url(../images/iviewer/iviewer.rotate_left.png) center center no-repeat;
60 | }
61 |
62 | .iviewer_rotate_right {
63 | left: 262px;
64 | background: #fff url(../images/iviewer/iviewer.rotate_right.png) center center no-repeat;
65 | }
66 |
--------------------------------------------------------------------------------
/docs/api/css/phpdocumentor-clean-icons/Read Me.txt:
--------------------------------------------------------------------------------
1 | To modify your generated font, use the *dev.svg* file, located in the *fonts* folder in this package. You can import this dev.svg file to the IcoMoon app. All the tags (class names) and the Unicode points of your glyphs are saved in this file.
2 |
3 | See the documentation for more info on how to use this package: http://icomoon.io/#docs/font-face
--------------------------------------------------------------------------------
/docs/api/css/phpdocumentor-clean-icons/fonts/phpdocumentor-clean-icons.dev.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/docs/api/css/phpdocumentor-clean-icons/fonts/phpdocumentor-clean-icons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/css/phpdocumentor-clean-icons/fonts/phpdocumentor-clean-icons.eot
--------------------------------------------------------------------------------
/docs/api/css/phpdocumentor-clean-icons/fonts/phpdocumentor-clean-icons.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/docs/api/css/phpdocumentor-clean-icons/fonts/phpdocumentor-clean-icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/css/phpdocumentor-clean-icons/fonts/phpdocumentor-clean-icons.ttf
--------------------------------------------------------------------------------
/docs/api/css/phpdocumentor-clean-icons/fonts/phpdocumentor-clean-icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/css/phpdocumentor-clean-icons/fonts/phpdocumentor-clean-icons.woff
--------------------------------------------------------------------------------
/docs/api/css/phpdocumentor-clean-icons/lte-ie7.js:
--------------------------------------------------------------------------------
1 | /* Load this script using conditional IE comments if you need to support IE 7 and IE 6. */
2 |
3 | window.onload = function() {
4 | function addIcon(el, entity) {
5 | var html = el.innerHTML;
6 | el.innerHTML = '' + entity + '' + html;
7 | }
8 | var icons = {
9 | 'icon-trait' : '',
10 | 'icon-interface' : '',
11 | 'icon-class' : ''
12 | },
13 | els = document.getElementsByTagName('*'),
14 | i, attr, html, c, el;
15 | for (i = 0; ; i += 1) {
16 | el = els[i];
17 | if(!el) {
18 | break;
19 | }
20 | attr = el.getAttribute('data-icon');
21 | if (attr) {
22 | addIcon(el, attr);
23 | }
24 | c = el.className;
25 | c = c.match(/icon-[^\s'"]+/);
26 | if (c && icons[c[0]]) {
27 | addIcon(el, icons[c[0]]);
28 | }
29 | }
30 | };
--------------------------------------------------------------------------------
/docs/api/css/phpdocumentor-clean-icons/style.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'phpdocumentor-clean-icons';
3 | src:url('fonts/phpdocumentor-clean-icons.eot');
4 | src:url('fonts/phpdocumentor-clean-icons.eot?#iefix') format('embedded-opentype'),
5 | url('fonts/phpdocumentor-clean-icons.woff') format('woff'),
6 | url('fonts/phpdocumentor-clean-icons.ttf') format('truetype'),
7 | url('fonts/phpdocumentor-clean-icons.svg#phpdocumentor-clean-icons') format('svg');
8 | font-weight: normal;
9 | font-style: normal;
10 | }
11 |
12 | /* Use the following CSS code if you want to use data attributes for inserting your icons */
13 | [data-icon]:before {
14 | font-family: 'phpdocumentor-clean-icons';
15 | content: attr(data-icon);
16 | speak: none;
17 | font-weight: normal;
18 | font-variant: normal;
19 | text-transform: none;
20 | line-height: 1;
21 | -webkit-font-smoothing: antialiased;
22 | }
23 |
24 | /* Use the following CSS code if you want to have a class per icon */
25 | /*
26 | Instead of a list of all class selectors,
27 | you can use the generic selector below, but it's slower:
28 | [class*="icon-"] {
29 | */
30 | .icon-trait, .icon-interface, .icon-class {
31 | font-family: 'phpdocumentor-clean-icons';
32 | speak: none;
33 | font-style: normal;
34 | font-weight: normal;
35 | font-variant: normal;
36 | text-transform: none;
37 | line-height: 1;
38 | -webkit-font-smoothing: antialiased;
39 | }
40 | .icon-trait:before {
41 | content: "\e000";
42 | }
43 | .icon-interface:before {
44 | content: "\e001";
45 | }
46 | .icon-class:before {
47 | content: "\e002";
48 | }
49 |
--------------------------------------------------------------------------------
/docs/api/css/prism.css:
--------------------------------------------------------------------------------
1 | /**
2 | * prism.js default theme for JavaScript, CSS and HTML
3 | * Based on dabblet (http://dabblet.com)
4 | * @author Lea Verou
5 | */
6 |
7 | code[class*="language-"],
8 | pre[class*="language-"] {
9 | color: black;
10 | text-shadow: 0 1px white;
11 | font-family: Consolas, Monaco, 'Andale Mono', monospace;
12 | direction: ltr;
13 | text-align: left;
14 | white-space: pre;
15 | word-spacing: normal;
16 |
17 | -moz-tab-size: 4;
18 | -o-tab-size: 4;
19 | tab-size: 4;
20 |
21 | -webkit-hyphens: none;
22 | -moz-hyphens: none;
23 | -ms-hyphens: none;
24 | hyphens: none;
25 | }
26 |
27 | ::-moz-selection {
28 | text-shadow: none;
29 | background: #b3d4fc;
30 | }
31 |
32 | ::selection {
33 | text-shadow: none;
34 | background: #b3d4fc;
35 | }
36 |
37 | @media print {
38 | code[class*="language-"],
39 | pre[class*="language-"] {
40 | text-shadow: none;
41 | }
42 | }
43 |
44 | /* Code blocks */
45 | pre[class*="language-"] {
46 | padding: 1em;
47 | margin: .5em 0;
48 | overflow: auto;
49 | }
50 |
51 | :not(pre) > code[class*="language-"],
52 | pre[class*="language-"] {
53 | background: #f5f2f0;
54 | }
55 |
56 | /* Inline code */
57 | :not(pre) > code[class*="language-"] {
58 | padding: .1em;
59 | border-radius: .3em;
60 | }
61 |
62 | .token.comment,
63 | .token.prolog,
64 | .token.doctype,
65 | .token.cdata {
66 | color: slategray;
67 | }
68 |
69 | .token.punctuation {
70 | color: #999;
71 | }
72 |
73 | .namespace {
74 | opacity: .7;
75 | }
76 |
77 | .token.property,
78 | .token.tag,
79 | .token.boolean,
80 | .token.number {
81 | color: #905;
82 | }
83 |
84 | .token.selector,
85 | .token.attr-name,
86 | .token.string {
87 | color: #690;
88 | }
89 |
90 | .token.operator,
91 | .token.entity,
92 | .token.url,
93 | .language-css .token.string,
94 | .style .token.string {
95 | color: #a67f59;
96 | background: hsla(0,0%,100%,.5);
97 | }
98 |
99 | .token.atrule,
100 | .token.attr-value,
101 | .token.keyword {
102 | color: #07a;
103 | }
104 |
105 |
106 | .token.regex,
107 | .token.important {
108 | color: #e90;
109 | }
110 |
111 | .token.important {
112 | font-weight: bold;
113 | }
114 |
115 | .token.entity {
116 | cursor: help;
117 | }
118 | pre[data-line] {
119 | position: relative;
120 | padding: 1em 0 1em 3em;
121 | }
122 |
123 | .line-highlight {
124 | position: absolute;
125 | left: 0;
126 | right: 0;
127 | padding: inherit 0;
128 | margin-top: 1em; /* Same as .prism’s padding-top */
129 |
130 | background: hsla(24, 20%, 50%,.08);
131 | background: -moz-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
132 | background: -webkit-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
133 | background: -o-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
134 | background: linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
135 |
136 | pointer-events: none;
137 |
138 | line-height: inherit;
139 | white-space: pre;
140 | }
141 |
142 | .line-highlight:before,
143 | .line-highlight[data-end]:after {
144 | content: attr(data-start);
145 | position: absolute;
146 | top: .4em;
147 | left: .6em;
148 | min-width: 1em;
149 | padding: 0 .5em;
150 | background-color: hsla(24, 20%, 50%,.4);
151 | color: hsl(24, 20%, 95%);
152 | font: bold 65%/1.5 sans-serif;
153 | text-align: center;
154 | vertical-align: .3em;
155 | border-radius: 999px;
156 | text-shadow: none;
157 | box-shadow: 0 1px white;
158 | }
159 |
160 | .line-highlight[data-end]:after {
161 | content: attr(data-end);
162 | top: auto;
163 | bottom: .4em;
164 | }
165 | pre.line-numbers {
166 | position: relative;
167 | padding-left: 3.8em;
168 | counter-reset: linenumber;
169 | }
170 |
171 | pre.line-numbers > code {
172 | position: relative;
173 | }
174 |
175 | .line-numbers .line-numbers-rows {
176 | position: absolute;
177 | pointer-events: none;
178 | top: 0;
179 | font-size: 100%;
180 | left: -3.8em;
181 | width: 3em; /* works for line-numbers below 1000 lines */
182 | letter-spacing: -1px;
183 | border-right: 1px solid #999;
184 |
185 | -webkit-user-select: none;
186 | -moz-user-select: none;
187 | -ms-user-select: none;
188 | user-select: none;
189 |
190 | }
191 |
192 | .line-numbers-rows > span {
193 | pointer-events: none;
194 | display: block;
195 | counter-increment: linenumber;
196 | }
197 |
198 | .line-numbers-rows > span:before {
199 | content: counter(linenumber);
200 | color: #999;
201 | display: block;
202 | padding-right: 0.8em;
203 | text-align: right;
204 | }
205 |
--------------------------------------------------------------------------------
/docs/api/css/template.css:
--------------------------------------------------------------------------------
1 | @import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro);
2 | @import url('phpdocumentor-clean-icons/style.css');
3 |
4 | body {
5 | padding-top: 40px;
6 | background-color: #333333;
7 | }
8 |
9 | a {
10 | color: #6495ed;
11 | }
12 | a.anchor {
13 | height: 40px;
14 | margin-top: -40px;
15 | display: block;
16 | }
17 |
18 | h1, h2, h3, h4, h5, h6, .brand {
19 | font-family: 'Source Sans Pro', sans-serif;
20 | font-weight: normal;
21 | letter-spacing: 0.05em;
22 | }
23 |
24 | h2, h3, .detailsbar h1 {
25 | overflow: hidden;
26 | white-space: nowrap;
27 | margin: 30px 0 20px 0;
28 | }
29 |
30 | h2:after, h3:after, .detailsbar h1:after {
31 | content: '';
32 | display: inline-block;
33 | vertical-align: middle;
34 | width: 100%;
35 | height: 2px;
36 | margin-left: 1em;
37 | background: silver;
38 | }
39 |
40 | h3 {
41 | margin: 10px 0 20px 0;
42 | }
43 |
44 | h4 {
45 | margin: 20px 0 10px 0;
46 | color: gray;
47 | font-size: 18.5px;
48 | }
49 |
50 | h3.public, h3.protected, h3.private {
51 | padding-left: 10px;
52 | text-overflow: ellipsis;
53 | }
54 |
55 | .table tr:first-of-type th, .table tr:first-of-type td {
56 | border-top: none;
57 | }
58 | .detailsbar {
59 | color: #eeeeee;
60 | background-color: #333333;
61 | font-size: 0.9em;
62 | overflow: hidden;
63 | border-left: 2px solid gray;
64 | }
65 |
66 | .detailsbar h1 {
67 | font-size: 1.5em;
68 | margin-bottom: 20px;
69 | margin-top: 0;
70 | }
71 |
72 | .detailsbar h2 {
73 | font-size: 1.2em;
74 | margin: 0;
75 | padding: 0;
76 | }
77 |
78 | .detailsbar h1:after {
79 | background: gray;
80 | }
81 | .detailsbar h2:after, .detailsbar h3:after {
82 | background: transparent;
83 | }
84 |
85 | .detailsbar dt {
86 | font-variant: small-caps;
87 | text-transform: lowercase;
88 | font-size: 1.1em;
89 | letter-spacing: 0.1em;
90 | color: silver;
91 | }
92 |
93 | .hierarchy div:nth-of-type(2) { margin-left: 11px; }
94 | .hierarchy div:nth-of-type(3) { margin-left: 22px; }
95 | .hierarchy div:nth-of-type(4) { margin-left: 33px; }
96 | .hierarchy div:nth-of-type(5) { margin-left: 44px; }
97 | .hierarchy div:nth-of-type(6) { margin-left: 55px; }
98 | .hierarchy div:nth-of-type(7) { margin-left: 66px; }
99 | .hierarchy div:nth-of-type(8) { margin-left: 77px; }
100 | .hierarchy div:nth-of-type(9) { margin-left: 88px; }
101 | .hierarchy div:before {
102 | content: "\f0da";
103 | font-family: FontAwesome;
104 | margin-right: 5px;
105 | }
106 |
107 | .row-fluid {
108 | background-color: white;
109 | overflow: hidden;
110 | }
111 |
112 | footer.row-fluid, footer.row-fluid * {
113 | background-color: #333333;
114 | color: white;
115 | }
116 |
117 | footer.row-fluid {
118 | border-top: 2px dashed #555;
119 | margin-top: 2px;
120 | }
121 |
122 | .footer-sections .span4 {
123 | border: 2px solid #555;
124 | text-align: center;
125 | border-radius: 10px;
126 | margin-top: 70px;
127 | margin-bottom: 20px;
128 | background: #373737;
129 | }
130 |
131 | .footer-sections .span4 h1 {
132 | background: transparent;
133 | margin-top: -30px;
134 | margin-bottom: 20px;
135 | font-size: 5em;
136 | }
137 |
138 | .footer-sections .span4 h1 * {
139 | background: transparent;
140 | }
141 |
142 | .footer-sections .span4 div {
143 | border-bottom-right-radius: 6px;
144 | border-bottom-left-radius: 6px;
145 | padding: 10px;
146 | min-height: 40px;
147 | }
148 | .footer-sections .span4 div, .footer-sections .span4 div * {
149 | background-color: #555;
150 | }
151 | .footer-sections .span4 ul {
152 | text-align: left;
153 | list-style: none;
154 | margin: 0;
155 | padding: 0;
156 | }
157 |
158 | .content {
159 | background-color: white;
160 | padding-right: 20px;
161 | }
162 |
163 | .content nav {
164 | text-align: center;
165 | border-bottom: 1px solid silver;
166 | margin: 5px 0 20px 0;
167 | padding-bottom: 5px;
168 | }
169 |
170 | .content > h1 {
171 | padding-bottom: 15px;
172 | }
173 |
174 | .content > h1 small {
175 | display: block;
176 | padding-bottom: 8px;
177 | font-size: 0.6em;
178 | }
179 |
180 | .deprecated {
181 | text-decoration: line-through;
182 | }
183 |
184 | .method {
185 | margin-bottom: 20px;
186 | }
187 |
188 | .method .signature .argument {
189 | color: maroon;
190 | font-weight: bold;
191 | }
192 |
193 | .class #summary section.row-fluid {
194 | overflow: hidden
195 | }
196 |
197 | .class #summary .heading {
198 | font-weight: bold;
199 | text-align: center;
200 | }
201 |
202 | .class #summary section .span4 {
203 | padding: 3px;
204 | overflow: hidden;
205 | margin-bottom: -9999px;
206 | padding-bottom: 9999px;
207 | white-space: nowrap;
208 | text-overflow: ellipsis;
209 | border-left: 5px solid transparent;
210 | }
211 |
212 | .class #summary section.public .span4:first-of-type:before,
213 | .class #summary section.public .span6:first-of-type:before,
214 | h3.public:before {
215 | font-family: FontAwesome;
216 | content: "\f046";
217 | color: green;
218 | display: inline-block;
219 | width: 1.2em;
220 | }
221 |
222 | .class #summary section .span4:first-of-type,
223 | .class #summary section .span6:first-of-type {
224 | padding-left: 21px;
225 | }
226 | .class #summary section .span4:first-of-type:before,
227 | .class #summary section .span6:first-of-type:before {
228 | margin-left: -21px;
229 | }
230 | .class #summary section.protected .span4:first-of-type:before,
231 | .class #summary section.protected .span6:first-of-type:before,
232 | h3.protected:before {
233 | font-family: FontAwesome;
234 | content: "\f132";
235 | color: orange;
236 | display: inline-block;
237 | width: 1.2em;
238 | }
239 |
240 | .class #summary section.private .span4:first-of-type:before,
241 | .class #summary section.private .span6:first-of-type:before,
242 | h3.private:before {
243 | font-family: FontAwesome;
244 | content: "\f023";
245 | color: red;
246 | display: inline-block;
247 | width: 1.2em;
248 | }
249 |
250 | .class #summary section em {
251 | font-size: 0.9em;
252 | color: silver;
253 | }
254 | .class #summary .inherited {
255 | color: gray;
256 | font-style: italic;
257 | }
258 |
259 | .accordion-group {
260 | border: none;
261 | }
262 |
263 | .accordion {
264 | margin-bottom: 0;
265 | }
266 |
267 | .accordion a:hover {
268 | text-decoration: none;
269 | background: #333333;
270 | color: #eeeeee;
271 | }
272 |
273 | .accordion-heading .accordion-toggle:before {
274 | content: "\f078";
275 | font-family: FontAwesome;
276 | margin-right: 5px;
277 | }
278 |
279 | .accordion-heading .accordion-toggle.collapsed:before {
280 | content: "\f054";
281 | }
282 | .accordion-heading .accordion-toggle {
283 | float: left;
284 | width: 16px;
285 | height: 16px;
286 | padding: 4px 2px 4px 12px;
287 | }
288 | .accordion-heading a {
289 | display: block;
290 | padding: 4px 12px;
291 | }
292 |
293 | .accordion-inner a {
294 | display: block;
295 | padding: 4px 12px;
296 | }
297 |
298 | .accordion-inner > ul a:before {
299 | font-family: 'phpdocumentor-clean-icons';
300 | content: "\e001";
301 | margin-right: 5px;
302 | }
303 |
304 | .accordion-inner li.class a:before {
305 | content: "\e002";
306 | }
307 |
308 | .accordion-inner li.interface a:before {
309 | content: "\e001";
310 | }
311 |
312 | .accordion-inner li.trait a:before {
313 | content: "\e000";
314 | }
315 |
316 | .accordion-inner {
317 | padding: 4px 0 4px 12px;
318 | }
319 | .accordion-inner ul {
320 | list-style: none;
321 | padding: 0;
322 | margin: 0;
323 | }
324 |
325 | .row-fluid .span2 {
326 | width: 16.5%;
327 | }
328 |
329 | body .modal {
330 | width: 90%; /* desired relative width */
331 | left: 5%; /* (100%-width)/2 */
332 | /* place center */
333 | margin-left:auto;
334 | margin-right:auto;
335 | }
336 |
337 | .side-nav.nav-list li a {
338 | overflow: hidden;
339 | white-space: nowrap;
340 | text-overflow: ellipsis;
341 | }
342 |
343 | @media (min-width: 767px) {
344 | .sidebar {
345 | position: fixed;
346 | top: 40px;
347 | bottom: 0;
348 | background-color: #f3f3f3;
349 | left: 0;
350 | border-right: 1px solid #e9e9e9;
351 | overflow-y: scroll;
352 | overflow-x: hidden;
353 | padding-top: 10px;
354 | }
355 |
356 | .sidebar::-webkit-scrollbar {
357 | width: 10px;
358 | }
359 |
360 | .sidebar::-webkit-scrollbar-thumb {
361 | background: #cccccc;
362 | background-clip: padding-box;
363 | border: 3px solid #f3f3f3;
364 | border-radius: 5px;
365 | }
366 |
367 | .sidebar::-webkit-scrollbar-button {
368 | display: none;
369 | }
370 |
371 | .sidebar::-webkit-scrollbar-track {
372 | background: #f3f3f3;
373 | }
374 | }
375 |
376 | @media (max-width: 979px) {
377 | body {
378 | padding-top: 0;
379 | }
380 | }
381 |
382 | @media (max-width: 767px) {
383 | .class #summary .heading {
384 | display: none;
385 | }
386 |
387 | .detailsbar h1 {
388 | display: none;
389 | }
390 |
391 | body {
392 | background-color: white;
393 | }
394 |
395 | footer.row-fluid, footer.row-fluid * {
396 | background-color: white;
397 | }
398 |
399 | .footer-sections .span4 h1 {
400 | color: #ccccd9;
401 | margin-top: 0;
402 | }
403 |
404 | .detailsbar {
405 | background-color: white;
406 | color: #333;
407 | border: none;
408 | }
409 |
410 | .row-fluid .span2 {
411 | width: 100%;
412 | }
413 | }
414 |
415 | @media (min-width: 767px) {
416 | .detailsbar {
417 | min-height: 100%;
418 | margin-bottom: -99999px;
419 | padding-bottom: 99999px;
420 | padding-left: 20px;
421 | padding-top: 10px;
422 | }
423 | }
424 |
425 | @media (min-width: 1200px) {
426 | .row-fluid .span2 {
427 | width: 16.5%;
428 | }
429 | }
430 |
--------------------------------------------------------------------------------
/docs/api/files/Client.php.txt:
--------------------------------------------------------------------------------
1 | setup();
24 | * ```
25 | */
26 | class Client {
27 |
28 | /**
29 | * Collection Table name
30 | */
31 | const T_COLLECTIONS = 'jsondb_collection';
32 |
33 | /**
34 | * JSON data Table name
35 | *
36 | */
37 | const T_JSON = 'jsondb_json';
38 |
39 | /**
40 | * JSON indices Table name
41 | *
42 | */
43 | const T_INDEX = 'jsondb_index';
44 |
45 | /**
46 | * Configuration manager for Illuminate framework
47 | *
48 | * @var \Illuminate\Database\Capsule\Manager
49 | * @see https://laravel.com/api/5.2/Illuminate/Database/Capsule/Manager.html.
50 | */
51 | private $capsule;
52 |
53 | /**
54 | * Constructor, creates a new Client
55 | *
56 | * @param array $config
57 | */
58 | public function __construct($config = array()) {
59 | // setup db connection
60 | $capsule = new Capsule();
61 |
62 | // default configuration
63 | $default = [
64 | 'driver' => env('DB_DRIVER', 'mysql'),
65 | 'host' => env('DB_HOST', 'localhost'),
66 | 'port' => env('DB_PORT', 3306),
67 | 'database' => env('DB_DATABASE', ''),
68 | 'username' => env('DB_USERNAME', ''),
69 | 'password' => env('DB_PASSWORD', ''),
70 | 'charset' => env('DB_CHARSET', 'utf8'),
71 | 'collation' => env('DB_COLLATION', 'utf8_unicode_ci')
72 | ];
73 |
74 | // create final config
75 | $config = array_merge($default, $config);
76 |
77 | $capsule->addConnection($config);
78 | $this->capsule = $capsule;
79 | }
80 |
81 | /**
82 | * Creates/Recreates required tables in database
83 | *
84 | */
85 | public function setup() {
86 | $sb = $this->connection()->getSchemaBuilder();
87 |
88 | // drop existing tables
89 | $sb->dropIfExists(self::T_INDEX);
90 | $sb->dropIfExists(self::T_JSON);
91 | $sb->dropIfExists(self::T_COLLECTIONS);
92 |
93 | // collections table
94 | $sb->create(self::T_COLLECTIONS, function(Blueprint $table){
95 | $table->increments('id');
96 | $table->string("name");
97 | $table->unique("name");
98 | });
99 |
100 | // json document table
101 | $sb->create(self::T_JSON, function(Blueprint $table){
102 | $table->increments('id');
103 |
104 | // document id
105 | $table->string("doc_id", 36);
106 | $table->unique("doc_id");
107 |
108 | // collection id
109 | $table->integer('cid')->unsigned();
110 | $table->foreign('cid')->references('id')
111 | ->on(self::T_COLLECTIONS)
112 | ->onDelete('cascade');
113 |
114 | $table->text('data');
115 | });
116 |
117 | // index table
118 | $sb->create(self::T_INDEX, function(Blueprint $table){
119 | $table->increments('id');
120 |
121 | $table->integer('cid')->unsigned();
122 | $table->foreign('cid')->references('id')
123 | ->on(self::T_COLLECTIONS)
124 | ->onDelete('cascade');
125 |
126 | // object id (primary key)
127 | $table->integer('oid')->unsigned();
128 | $table->foreign('oid')->references('id')
129 | ->on(self::T_JSON)
130 | ->onDelete('cascade');
131 |
132 | $table->integer('depth');
133 | $table->string('typ');
134 |
135 | $table->string('path');
136 | $table->index('path');
137 |
138 | // values
139 | $table->text('vjson')->nullable();
140 | $table->boolean('vboolean')->nullable();
141 | $table->text('vstring')->nullable();
142 | $table->float('vfloat')->nullable();
143 | $table->boolean('vnull')->nullable();
144 | });
145 | }
146 |
147 | /**
148 | * Current database connection
149 | * @return \Illuminate\Database\Connection
150 | */
151 | public function connection() {
152 | return $this->capsule->getConnection();
153 | }
154 |
155 | /**
156 | * Create a new Collection
157 | * @param string $name Collection name
158 | * @return \IBT\JsonDB\Collection
159 | * @throws Exception if the collection name already exists/is invalid
160 | */
161 | public function newCollection($name) {
162 | $name = strtolower($name);
163 |
164 | if(!Collection::isValidName($name)) {
165 | throw new Exception("The collection name $name isn't valid");
166 | }
167 |
168 | if(in_array($name, $this->listCollection())) {
169 | throw new Exception("The collection name $name already exists");
170 | }
171 |
172 | $conn = $this->connection();
173 | $id = $conn->table(self::T_COLLECTIONS)->insertGetId(
174 | ['name' => $name]
175 | );
176 |
177 | return new Collection($id, $name, $this);
178 | }
179 |
180 | /**
181 | * Return existing Collection
182 | * @param string $name Collection name
183 | * @return \IBT\JsonDB\Collection
184 | * @throws Exception if the collection name doesn't exist
185 | */
186 | public function getCollection($name) {
187 | $name = strtolower($name);
188 | $conn = $this->connection();
189 |
190 | // check for existence
191 | $row = $conn->table(self::T_COLLECTIONS)->select("id", "name")->where("name", $name)->first();
192 | if(!$row) {
193 | throw new Exception("The collection name $name doesn't exist");
194 | }
195 |
196 | return new Collection($row->id, $row->name, $this);
197 | }
198 |
199 | /**
200 | * List all collections
201 | * @return array
202 | */
203 | public function listCollection() {
204 | $conn = $this->connection();
205 | $name = $conn->table(self::T_COLLECTIONS)->pluck("name");
206 | return $name;
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/docs/api/files/Collection.php.txt:
--------------------------------------------------------------------------------
1 | setup();
21 | * $col = $c->newCollection("users");
22 | * $col->insert('{"name":"jason bourne", "category":"agent"}');
23 | * ```
24 | */
25 | class Collection {
26 |
27 | /**
28 | * @var string Name of the Collection
29 | */
30 | private $name;
31 |
32 | /**
33 | * @var int Collection database primary key
34 | */
35 | private $id;
36 |
37 | /**
38 | * @var Client JsonDB Client
39 | */
40 | private $client;
41 |
42 | /**
43 | * Constructor, creates a new Collection
44 | *
45 | * @param int $id Database primary key
46 | * @param string $name Collection name
47 | * @param Client $client
48 | */
49 | function __construct($id, $name, Client $client) {
50 | $this->id = $id;
51 | $this->name = $name;
52 | $this->client = $client;
53 | }
54 |
55 | /**
56 | * Validate Collection name
57 | *
58 | * @param string $name Collection name
59 | * @return boolean
60 | */
61 | static function isValidName($name) {
62 | // only accept alpha characters
63 | $p = '/^[a-zA-Z]+$/';
64 | $m = preg_match($p, $name);
65 | if($m) {
66 | return true;
67 | }
68 |
69 | return false;
70 | }
71 |
72 | /**
73 | * Collection name
74 | *
75 | * @return string
76 | */
77 | function getName() {
78 | return $this->name;
79 | }
80 |
81 | /**
82 | * Collection id
83 | *
84 | * @return int
85 | */
86 | function getId() {
87 | return $this->id;
88 | }
89 |
90 | /**
91 | * Column name from JSON type
92 | *
93 | * @param string $typ
94 | * @return string
95 | * @throws Exception if the JSON type isn't supported
96 | */
97 | public static function getColumn($typ) {
98 | switch ($typ) {
99 | case 'float':
100 | return 'vfloat';
101 | case 'boolean':
102 | return 'vboolean';
103 | case 'string':
104 | return 'vstring';
105 | case 'array':
106 | case 'object':
107 | return 'vjson';
108 | case 'null':
109 | return 'vnull';
110 | default:
111 | throw new Exception('Unknown JSON type');
112 | break;
113 | }
114 | }
115 |
116 | /**
117 | * Save JSON string (create/update)
118 | *
119 | * @param string $doc JSON string
120 | * @return string
121 | */
122 | private function save($doc) {
123 | // create index
124 | $scr = new Scanner();
125 | $index_list = $scr->scan($doc);
126 |
127 | $conn = $this->client->connection();
128 | $cid = $this->id;
129 |
130 | $conn->transaction(function() use ($index_list, $conn, $cid, $doc) {
131 | // add data
132 |
133 | $json = json_encode($doc);
134 | $id = $conn->table(Client::T_JSON)->insertGetId(
135 | ['doc_id' => $doc['_id'], 'cid' => $cid, 'data' => $json]
136 | );
137 |
138 | // add index
139 | $rows = array();
140 | foreach ($index_list as $item) {
141 | $row = [
142 | 'cid' => $cid,
143 | 'oid' => $id,
144 | 'depth' => $item->depth,
145 | 'typ' => $item->typ,
146 | 'path' => $item->path,
147 | 'vfloat' => NULL,
148 | 'vboolean' => NULL,
149 | 'vstring' => NULL,
150 | 'vjson' => NULL,
151 | 'vnull' => NULL
152 | ];
153 |
154 | // insert value in appropriate column
155 | $c = Collection::getColumn($item->typ);
156 | $row[$c] = $item->value;
157 | $rows[] = $row;
158 | }
159 | $conn->table(Client::T_INDEX)->insert($rows);
160 | });
161 |
162 | return $doc['_id'];
163 | }
164 |
165 | /**
166 | * Check if document exists
167 | *
168 | * @param string $id document id
169 | * @return boolean
170 | */
171 | private function exists($id) {
172 | $conn = $this->client->connection();
173 | $row = $conn->table(Client::T_JSON)->select("doc_id")->where("doc_id", $id)->first();
174 | if(!$row) {
175 | return false;
176 | }
177 |
178 | return true;
179 | }
180 |
181 | /**
182 | * Database manager
183 | *
184 | * @return Client
185 | */
186 | function getClient() {
187 | return $this->client;
188 | }
189 |
190 | /**
191 | * Inserts a json document into db.
192 | *
193 | * @param string $json JSON string to insert
194 | * @return string Returns document id
195 | * @throws Exception if the document id isn't valid
196 | */
197 | function insert($json) {
198 | $doc = json_decode($json, true);
199 |
200 | // check if json has id
201 | if(!array_key_exists('_id', $doc)) {
202 | $doc['_id'] = uniqid('oid');
203 | } else {
204 | // only accept alpha numeric characters
205 | $p = '/^[a-z0-9]+$/';
206 | $m = preg_match($p, $doc['_id']);
207 | if(!$m) {
208 | throw new Exception('Invalid document id.');
209 | }
210 | }
211 |
212 | return $this->save($doc);
213 | }
214 |
215 | /**
216 | * Retrieve JSON document
217 | *
218 | * @param string $id document id
219 | * @return mixed
220 | */
221 | function get($id) {
222 | $conn = $this->client->connection();
223 | $doc = $conn->table(Client::T_JSON)
224 | ->where('doc_id', $id)
225 | ->where('cid', $this->id)
226 | ->first();
227 |
228 | if(is_null($doc)) {
229 | return NULL;
230 | }
231 |
232 | return json_decode($doc->data);
233 | }
234 |
235 | /**
236 | * Delete JSON document
237 | *
238 | * @param string $oid document id
239 | * @return boolean
240 | */
241 | function delete($oid) {
242 | $conn = $this->client->connection();
243 | return $conn->table(Client::T_JSON)->where('doc_id', $oid)->delete() > 0;
244 | }
245 |
246 | /**
247 | * Updates a JSON document in db.
248 | *
249 | * @param string $json JSON string to insert
250 | * @return string Returns document id
251 | * @throws Exception if the original document id isn't present
252 | */
253 | function update($json) {
254 | $doc = json_decode($json, true);
255 |
256 | // check if json has id
257 | if(!array_key_exists('_id', $doc)) {
258 | throw new Exception('Missing \'_id\' in JSON document');
259 | }
260 |
261 | $oid = $doc['_id'];
262 |
263 | if($this->exists($oid)) {
264 | $this->delete($oid);
265 | $this->save($doc);
266 | return true;
267 | }
268 |
269 | return false;
270 | }
271 |
272 | /**
273 | * Total number of JSON documents
274 | *
275 | * @return int
276 | */
277 | function count() {
278 | $conn = $this->client->connection();
279 | $tbl = $conn->table(Client::T_JSON);
280 | return $tbl->select($conn->raw('distinct(doc_id)'))->count();
281 | }
282 |
283 | /**
284 | * New Filter to query JSON document content
285 | *
286 | * @return \IBT\JsonDB\Filter
287 | */
288 | function newFilter() {
289 | return new Filter($this);
290 | }
291 | }
292 |
--------------------------------------------------------------------------------
/docs/api/files/Exception.php.txt:
--------------------------------------------------------------------------------
1 | setup();
23 | * $col = $c->newCollection("users");
24 | * $col->insert('{"name":"jason bourne", "category":"agent"}');
25 | * $f = $col->newFilter();
26 | * $f->where('category', 'in', ['agent'])->run();
27 | * ```
28 | */
29 | class Filter {
30 |
31 | /**
32 | * @var Collection Collection the filter is based on
33 | */
34 | private $col;
35 |
36 | /**
37 | * @var \Illuminate\Database\Query\Builder Query builder instance
38 | */
39 | private $query;
40 |
41 | /**
42 | * @var string Name of the database View
43 | */
44 | private $view;
45 |
46 | /**
47 | * @var array Fields(or JSON paths) that will be used in the query
48 | */
49 | private $fields;
50 |
51 | /**
52 | * Constructor, creates a new Filter
53 | *
54 | * @param Collection $col Collection the filter is based on
55 | */
56 | public function __construct(Collection $col) {
57 | $this->fields = array();
58 | $this->col = $col;
59 |
60 | $this->view = uniqid('view_');
61 |
62 | $conn = $this->col->getClient()->connection();
63 | $this->query = $conn->table($this->view)
64 | ->join(Client::T_JSON, 'oid', '=', 'id')
65 | ->select('doc_id');
66 |
67 | }
68 |
69 | /**
70 | * Converts JSON value path into valid column name
71 | *
72 | * @param string $path JSON path
73 | * @return string
74 | */
75 | private function sanitizeColName($path) {
76 | return str_replace('.', '_', $path);
77 | }
78 |
79 | /**
80 | * SQL Where statement.
81 | *
82 | * @param string $path JSON path
83 | * @param string $op Operand such as =, <=, >
84 | * @param mixed $value Value
85 | * @return $this
86 | */
87 | public function where($path, $op, $value) {
88 | $this->fields[$path] = 1;
89 | $this->query->where($this->sanitizeColName($path), $op, $value);
90 | return $this;
91 | }
92 |
93 | /**
94 | * SQL Or Where statement.
95 | *
96 | * @param string $path JSON path
97 | * @param string $op Operand such as =, <=, >
98 | * @param mixed $value Value
99 | * @return $this
100 | */
101 | public function orWhere($path, $op, $value) {
102 | $this->fields[$path] = 1;
103 | $this->query->orWhere($this->sanitizeColName($path), $op, $value);
104 | return $this;
105 | }
106 |
107 | /**
108 | * SQL Where Between statement.
109 | *
110 | * @param string $path JSON path
111 | * @param array $value Values
112 | * @return $this
113 | */
114 | public function whereBetween($path, $value) {
115 | $this->fields[$path] = 1;
116 | $this->query->whereBetween($this->sanitizeColName($path), $value);
117 | return $this;
118 | }
119 |
120 | /**
121 | * SQL Where Not Between statement.
122 | *
123 | * @param string $path JSON path
124 | * @param array $value Values
125 | * @return $this
126 | */
127 | public function whereNotBetween($path, $value) {
128 | $this->fields[$path] = 1;
129 | $this->query->whereNotBetween($this->sanitizeColName($path), $value);
130 | return $this;
131 | }
132 |
133 | /**
134 | * SQL Where In statement.
135 | *
136 | * @param string $path JSON path
137 | * @param mixed $value Value(s)
138 | * @return $this
139 | */
140 | public function whereIn($path, $value) {
141 | $this->fields[$path] = 1;
142 | $this->query->whereIn($this->sanitizeColName($path), $value);
143 | return $this;
144 | }
145 |
146 | /**
147 | * SQL Where Not In statement.
148 | *
149 | * @param string $path JSON path
150 | * @param mixed $value Value(s)
151 | * @return $this
152 | */
153 | public function whereNotIn($path, $value) {
154 | $this->fields[$path] = 1;
155 | $this->query->whereNotIn($this->sanitizeColName($path), $value);
156 | return $this;
157 | }
158 |
159 | /**
160 | * SQL Order By statement.
161 | *
162 | * @param string $path JSON path
163 | * @param string $direction Direction i.e 'asc' or 'desc'
164 | * @return $this
165 | */
166 | public function orderBy($path, $direction) {
167 | $this->fields[$path] = 1;
168 | $this->query->orderBy($this->sanitizeColName($path), $direction);
169 | return $this;
170 | }
171 |
172 | /**
173 | * Return View name.
174 | *
175 | * @return string
176 | */
177 | public function viewName() {
178 | return $this->view;
179 | }
180 |
181 | /**
182 | * Make sure JSON paths specified in filter statements exist.
183 | * Prevent SQL injection.
184 | *
185 | * @return array
186 | * @throws Exception if the requested JSON path is invalid
187 | */
188 | private function validateFields() {
189 | // get paths and their data types
190 | $conn = $this->col->getClient()->connection();
191 | $rows = $conn->table(Client::T_INDEX)
192 | ->select($conn->raw('distinct(path)'), 'typ')
193 | ->whereIn('path', array_keys($this->fields))
194 | ->get();
195 |
196 | // quick comparison. can be changed for a more descriptive
197 | // error by comparing values in both lists
198 | if(sizeof($rows) != sizeof($this->fields)) {
199 | throw new Exception("Error: One or more JSON paths don't exist");
200 | }
201 |
202 | return $rows;
203 | }
204 |
205 | /**
206 | * View query string. This can be used for debugging or caching
207 | *
208 | * @return string
209 | */
210 | public function viewSQL() {
211 | $idx_table = Client::T_INDEX;
212 |
213 | // validate fields
214 | $fields = $this->validateFields();
215 |
216 | // oid table
217 | $main = "(select distinct(oid) from ${idx_table}) as a";
218 |
219 | $select = ['a.oid'];
220 |
221 | $join = [$main];
222 |
223 | $tc = 0; // table counter
224 |
225 | // add fields
226 | foreach ($fields as $item) {
227 | // get column name
228 | $col = Collection::getColumn($item->typ);
229 | $field = $item->path;
230 |
231 | $cn = $this->sanitizeColName($field); // column name
232 |
233 | $t = "select oid, case when path='${field}' then ${col} end as ${cn} ";
234 | $t .= "from ${idx_table} having ${cn} is not null";
235 |
236 | $tc++;
237 | $tn = "jn_tbl_" . $tc; // join table alias
238 |
239 | $select[] = "${tn}.${cn}";
240 | $join[] = '(' . $t . ") as $tn on a.oid = $tn.oid";
241 | }
242 |
243 | $sb = 'create view ' . $this->view .' as ';
244 | $sb .= 'select ' . join(",", $select) . ' from ';
245 | $sb .= join(" left join ", $join);
246 |
247 | return $sb;
248 | }
249 |
250 | /**
251 | * View select query string. complements the view function
252 | *
253 | * @return string
254 | */
255 | public function selectSQL() {
256 | return $this->query->toSql();
257 | }
258 |
259 | /**
260 | * Execute the filter
261 | *
262 | * @return array
263 | */
264 | public function run() {
265 | $res = [];
266 | $conn = $this->col->getClient()->connection();
267 | $view = $this->view;
268 | $viewSQL = $this->viewSQL();
269 | $query = $this->query;
270 |
271 | $conn->transaction(function() use (&$res, $conn, $view, $viewSQL, $query) {
272 | // create view
273 | $conn->statement($viewSQL);
274 |
275 | // select data
276 | $res = $query->pluck('doc_id');
277 |
278 | // drop view
279 | $conn->statement('drop view if exists ' . $view);
280 | });
281 |
282 | return $res;
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/docs/api/files/Indexer/Exception.php.txt:
--------------------------------------------------------------------------------
1 | typ = $typ;
48 | $this->value = $value;
49 | $this->path = $path;
50 | $this->depth = $depth;
51 | }
52 |
53 | /**
54 | * Retrieves a property if it exists.
55 | *
56 | * @see http://php.net/manual/en/language.oop5.overloading.php#object.get
57 | * @param string $property Name of property to retrieve
58 | * @return mixed
59 | * @throws Exception if the property isn't found
60 | */
61 | public function __get($property) {
62 | if(property_exists($this, $property)) {
63 | return $this->$property;
64 | }
65 | throw new Exception('Property not found');
66 | }
67 |
68 | /**
69 | * Return internal properties for debugging purposes.
70 | *
71 | * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo
72 | * @return mixed
73 | */
74 | public function __debugInfo()
75 | {
76 | return array(
77 | "depth" => $this->depth,
78 | "path" => $this->path,
79 | "value" => $this->value,
80 | "type" => $this->typ
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/docs/api/files/Indexer/Scanner.php.txt:
--------------------------------------------------------------------------------
1 | scan(json_decode($j));
19 | * ```
20 | */
21 | class Scanner {
22 |
23 | /**
24 | * Indexes a JSON string.
25 | *
26 | * @param array $json JSON value
27 | * @return array Path
28 | */
29 | public function scan($json) {
30 | $it = new \RecursiveArrayIterator($json); // call global namespace class
31 | $path = ""; // set object root path
32 | $depth = 0; // set path depth
33 | $result = array(); // array for indexes
34 | $fn = array($this, "walk"); // iterator callback function
35 |
36 | iterator_apply($it, $fn, array($it, $path, $depth, &$result));
37 |
38 | return $result;
39 | }
40 |
41 | /**
42 | * Indexes a JSON string.
43 | *
44 | * @param \RecursiveArrayIterator $it Iterator
45 | * @param string $path current JSON path
46 | * @param int $depth current JSON depth
47 | * @param array $result Reference to array containing scanned indexes
48 | */
49 | private function walk($it, $path, $depth, &$result) {
50 | while ($it->valid()) {
51 |
52 | $key = $path . $it->key();
53 | $value = $it->current();
54 | $typ = Scanner::jsonType(gettype($value));
55 | if($typ == "array" || $typ == "object") {
56 | $value = json_encode($value);
57 | }
58 |
59 | $result[] = new Index($typ, $value, $key, $depth);
60 |
61 | if($it->hasChildren()) {
62 | $c = $it->getChildren();
63 | $sub_path = $key . ".";
64 | $depth++;
65 | $this->walk($c, $sub_path, $depth, $result);
66 | }
67 | $it->next();
68 | }
69 | }
70 |
71 | /**
72 | * Returns the equivalent JSON type.
73 | *
74 | * @param string $typ
75 | * @return string
76 | * @throws Exception if the JSON type isn't supported
77 | */
78 | public static function jsonType($typ) {
79 | switch ($typ) {
80 | case 'integer':
81 | case 'double':
82 | return 'float';
83 | case 'NULL':
84 | return 'null';
85 | case 'boolean':
86 | case 'string':
87 | case 'array':
88 | case 'object':
89 | return $typ;
90 | default:
91 | throw new Exception("Unknown JSON type ${typ}");
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/docs/api/files/helpers.php.txt:
--------------------------------------------------------------------------------
1 | 1 && Str::startsWith($value, '"') && Str::endsWith($value, '"')) {
41 | return substr($value, 1, -1);
42 | }
43 | return $value;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/docs/api/font/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/font/FontAwesome.otf
--------------------------------------------------------------------------------
/docs/api/font/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/font/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/api/font/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/font/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/api/font/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/font/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/api/graphs/class.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | API Documentation
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
126 |
127 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/docs/api/graphs/classes.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
80 |
--------------------------------------------------------------------------------
/docs/api/images/apple-touch-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/apple-touch-icon-114x114.png
--------------------------------------------------------------------------------
/docs/api/images/apple-touch-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/apple-touch-icon-72x72.png
--------------------------------------------------------------------------------
/docs/api/images/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/apple-touch-icon.png
--------------------------------------------------------------------------------
/docs/api/images/custom-icons.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
117 |
--------------------------------------------------------------------------------
/docs/api/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/favicon.ico
--------------------------------------------------------------------------------
/docs/api/images/hierarchy-item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/hierarchy-item.png
--------------------------------------------------------------------------------
/docs/api/images/icon-class-13x13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/icon-class-13x13.png
--------------------------------------------------------------------------------
/docs/api/images/icon-class.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
78 |
--------------------------------------------------------------------------------
/docs/api/images/icon-interface-13x13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/icon-interface-13x13.png
--------------------------------------------------------------------------------
/docs/api/images/icon-interface.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
74 |
--------------------------------------------------------------------------------
/docs/api/images/icon-trait-13x13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/icon-trait-13x13.png
--------------------------------------------------------------------------------
/docs/api/images/icon-trait.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
74 |
--------------------------------------------------------------------------------
/docs/api/images/iviewer/grab.cur:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/iviewer/grab.cur
--------------------------------------------------------------------------------
/docs/api/images/iviewer/hand.cur:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/iviewer/hand.cur
--------------------------------------------------------------------------------
/docs/api/images/iviewer/iviewer.rotate_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/iviewer/iviewer.rotate_left.png
--------------------------------------------------------------------------------
/docs/api/images/iviewer/iviewer.rotate_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/iviewer/iviewer.rotate_right.png
--------------------------------------------------------------------------------
/docs/api/images/iviewer/iviewer.zoom_fit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/iviewer/iviewer.zoom_fit.png
--------------------------------------------------------------------------------
/docs/api/images/iviewer/iviewer.zoom_in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/iviewer/iviewer.zoom_in.png
--------------------------------------------------------------------------------
/docs/api/images/iviewer/iviewer.zoom_out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/iviewer/iviewer.zoom_out.png
--------------------------------------------------------------------------------
/docs/api/images/iviewer/iviewer.zoom_zero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/docs/api/images/iviewer/iviewer.zoom_zero.png
--------------------------------------------------------------------------------
/docs/api/js/html5.js:
--------------------------------------------------------------------------------
1 | /*
2 | HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x";
6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);
8 | if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d 1 )
33 | {
34 | return this.each(
35 | function()
36 | {
37 | $(this).dotdotdot( o );
38 | }
39 | );
40 | }
41 |
42 |
43 | var $dot = this;
44 |
45 | if ( $dot.data( 'dotdotdot' ) )
46 | {
47 | $dot.trigger( 'destroy.dot' );
48 | }
49 |
50 | $dot.data( 'dotdotdot-style', $dot.attr( 'style' ) );
51 | $dot.css( 'word-wrap', 'break-word' );
52 |
53 | $dot.bind_events = function()
54 | {
55 | $dot.bind(
56 | 'update.dot',
57 | function( e, c )
58 | {
59 | e.preventDefault();
60 | e.stopPropagation();
61 |
62 | opts.maxHeight = ( typeof opts.height == 'number' )
63 | ? opts.height
64 | : getTrueInnerHeight( $dot );
65 |
66 | opts.maxHeight += opts.tolerance;
67 |
68 | if ( typeof c != 'undefined' )
69 | {
70 | if ( typeof c == 'string' || c instanceof HTMLElement )
71 | {
72 | c = $('').append( c ).contents();
73 | }
74 | if ( c instanceof $ )
75 | {
76 | orgContent = c;
77 | }
78 | }
79 |
80 | $inr = $dot.wrapInner( '' ).children();
81 | $inr.empty()
82 | .append( orgContent.clone( true ) )
83 | .css({
84 | 'height' : 'auto',
85 | 'width' : 'auto',
86 | 'border' : 'none',
87 | 'padding' : 0,
88 | 'margin' : 0
89 | });
90 |
91 | var after = false,
92 | trunc = false;
93 |
94 | if ( conf.afterElement )
95 | {
96 | after = conf.afterElement.clone( true );
97 | conf.afterElement.remove();
98 | }
99 | if ( test( $inr, opts ) )
100 | {
101 | if ( opts.wrap == 'children' )
102 | {
103 | trunc = children( $inr, opts, after );
104 | }
105 | else
106 | {
107 | trunc = ellipsis( $inr, $dot, $inr, opts, after );
108 | }
109 | }
110 | $inr.replaceWith( $inr.contents() );
111 | $inr = null;
112 |
113 | if ( $.isFunction( opts.callback ) )
114 | {
115 | opts.callback.call( $dot[ 0 ], trunc, orgContent );
116 | }
117 |
118 | conf.isTruncated = trunc;
119 | return trunc;
120 | }
121 |
122 | ).bind(
123 | 'isTruncated.dot',
124 | function( e, fn )
125 | {
126 | e.preventDefault();
127 | e.stopPropagation();
128 |
129 | if ( typeof fn == 'function' )
130 | {
131 | fn.call( $dot[ 0 ], conf.isTruncated );
132 | }
133 | return conf.isTruncated;
134 | }
135 |
136 | ).bind(
137 | 'originalContent.dot',
138 | function( e, fn )
139 | {
140 | e.preventDefault();
141 | e.stopPropagation();
142 |
143 | if ( typeof fn == 'function' )
144 | {
145 | fn.call( $dot[ 0 ], orgContent );
146 | }
147 | return orgContent;
148 | }
149 |
150 | ).bind(
151 | 'destroy.dot',
152 | function( e )
153 | {
154 | e.preventDefault();
155 | e.stopPropagation();
156 |
157 | $dot.unwatch()
158 | .unbind_events()
159 | .empty()
160 | .append( orgContent )
161 | .attr( 'style', $dot.data( 'dotdotdot-style' ) )
162 | .data( 'dotdotdot', false );
163 | }
164 | );
165 | return $dot;
166 | }; // /bind_events
167 |
168 | $dot.unbind_events = function()
169 | {
170 | $dot.unbind('.dot');
171 | return $dot;
172 | }; // /unbind_events
173 |
174 | $dot.watch = function()
175 | {
176 | $dot.unwatch();
177 | if ( opts.watch == 'window' )
178 | {
179 | var $window = $(window),
180 | _wWidth = $window.width(),
181 | _wHeight = $window.height();
182 |
183 | $window.bind(
184 | 'resize.dot' + conf.dotId,
185 | function()
186 | {
187 | if ( _wWidth != $window.width() || _wHeight != $window.height() || !opts.windowResizeFix )
188 | {
189 | _wWidth = $window.width();
190 | _wHeight = $window.height();
191 |
192 | if ( watchInt )
193 | {
194 | clearInterval( watchInt );
195 | }
196 | watchInt = setTimeout(
197 | function()
198 | {
199 | $dot.trigger( 'update.dot' );
200 | }, 10
201 | );
202 | }
203 | }
204 | );
205 | }
206 | else
207 | {
208 | watchOrg = getSizes( $dot );
209 | watchInt = setInterval(
210 | function()
211 | {
212 | var watchNew = getSizes( $dot );
213 | if ( watchOrg.width != watchNew.width ||
214 | watchOrg.height != watchNew.height )
215 | {
216 | $dot.trigger( 'update.dot' );
217 | watchOrg = getSizes( $dot );
218 | }
219 | }, 100
220 | );
221 | }
222 | return $dot;
223 | };
224 | $dot.unwatch = function()
225 | {
226 | $(window).unbind( 'resize.dot' + conf.dotId );
227 | if ( watchInt )
228 | {
229 | clearInterval( watchInt );
230 | }
231 | return $dot;
232 | };
233 |
234 | var orgContent = $dot.contents(),
235 | opts = $.extend( true, {}, $.fn.dotdotdot.defaults, o ),
236 | conf = {},
237 | watchOrg = {},
238 | watchInt = null,
239 | $inr = null;
240 |
241 | conf.afterElement = getElement( opts.after, $dot );
242 | conf.isTruncated = false;
243 | conf.dotId = dotId++;
244 |
245 |
246 | $dot.data( 'dotdotdot', true )
247 | .bind_events()
248 | .trigger( 'update.dot' );
249 |
250 | if ( opts.watch )
251 | {
252 | $dot.watch();
253 | }
254 |
255 | return $dot;
256 | };
257 |
258 |
259 | // public
260 | $.fn.dotdotdot.defaults = {
261 | 'ellipsis' : '... ',
262 | 'wrap' : 'word',
263 | 'lastCharacter': {
264 | 'remove' : [ ' ', ',', ';', '.', '!', '?' ],
265 | 'noEllipsis' : []
266 | },
267 | 'tolerance' : 0,
268 | 'callback' : null,
269 | 'after' : null,
270 | 'height' : null,
271 | 'watch' : false,
272 | 'windowResizeFix': true,
273 | 'debug' : false
274 | };
275 |
276 |
277 | // private
278 | var dotId = 1;
279 |
280 | function children( $elem, o, after )
281 | {
282 | var $elements = $elem.children(),
283 | isTruncated = false;
284 |
285 | $elem.empty();
286 |
287 | for ( var a = 0, l = $elements.length; a < l; a++ )
288 | {
289 | var $e = $elements.eq( a );
290 | $elem.append( $e );
291 | if ( after )
292 | {
293 | $elem.append( after );
294 | }
295 | if ( test( $elem, o ) )
296 | {
297 | $e.remove();
298 | isTruncated = true;
299 | break;
300 | }
301 | else
302 | {
303 | if ( after )
304 | {
305 | after.remove();
306 | }
307 | }
308 | }
309 | return isTruncated;
310 | }
311 | function ellipsis( $elem, $d, $i, o, after )
312 | {
313 | var $elements = $elem.contents(),
314 | isTruncated = false;
315 |
316 | $elem.empty();
317 |
318 | var notx = 'table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, select, optgroup, option, textarea, script, style';
319 | for ( var a = 0, l = $elements.length; a < l; a++ )
320 | {
321 |
322 | if ( isTruncated )
323 | {
324 | break;
325 | }
326 |
327 | var e = $elements[ a ],
328 | $e = $(e);
329 |
330 | if ( typeof e == 'undefined' )
331 | {
332 | continue;
333 | }
334 |
335 | $elem.append( $e );
336 | if ( after )
337 | {
338 | $elem[ ( $elem.is( notx ) ) ? 'after' : 'append' ]( after );
339 | }
340 | if ( e.nodeType == 3 )
341 | {
342 | if ( test( $i, o ) )
343 | {
344 | isTruncated = ellipsisElement( $e, $d, $i, o, after );
345 | }
346 | }
347 | else
348 | {
349 | isTruncated = ellipsis( $e, $d, $i, o, after );
350 | }
351 |
352 | if ( !isTruncated )
353 | {
354 | if ( after )
355 | {
356 | after.remove();
357 | }
358 | }
359 | }
360 | return isTruncated;
361 | }
362 | function ellipsisElement( $e, $d, $i, o, after )
363 | {
364 | var isTruncated = false,
365 | e = $e[ 0 ];
366 |
367 | if ( typeof e == 'undefined' )
368 | {
369 | return false;
370 | }
371 |
372 | var seporator = ( o.wrap == 'letter' ) ? '' : ' ',
373 | textArr = getTextContent( e ).split( seporator ),
374 | position = -1,
375 | midPos = -1,
376 | startPos = 0,
377 | endPos = textArr.length - 1;
378 |
379 | while ( startPos <= endPos )
380 | {
381 | var m = Math.floor( ( startPos + endPos ) / 2 );
382 | if ( m == midPos )
383 | {
384 | break;
385 | }
386 | midPos = m;
387 |
388 | setTextContent( e, textArr.slice( 0, midPos + 1 ).join( seporator ) + o.ellipsis );
389 |
390 | if ( !test( $i, o ) )
391 | {
392 | position = midPos;
393 | startPos = midPos;
394 | }
395 | else
396 | {
397 | endPos = midPos;
398 | }
399 | }
400 |
401 | if ( position != -1 && !( textArr.length == 1 && textArr[ 0 ].length == 0 ) )
402 | {
403 | var txt = addEllipsis( textArr.slice( 0, position + 1 ).join( seporator ), o );
404 | isTruncated = true;
405 | setTextContent( e, txt );
406 | }
407 | else
408 | {
409 | var $w = $e.parent();
410 | $e.remove();
411 |
412 | var afterLength = ( after ) ? after.length : 0 ;
413 |
414 | if ( $w.contents().size() > afterLength )
415 | {
416 | var $n = $w.contents().eq( -1 - afterLength );
417 | isTruncated = ellipsisElement( $n, $d, $i, o, after );
418 | }
419 | else
420 | {
421 | var $p = $w.prev()
422 | var e = $p.contents().eq( -1 )[ 0 ];
423 |
424 | if ( typeof e != 'undefined' )
425 | {
426 | var txt = addEllipsis( getTextContent( e ), o );
427 | setTextContent( e, txt );
428 | if ( after )
429 | {
430 | $p.append( after );
431 | }
432 | $w.remove();
433 | isTruncated = true;
434 | }
435 |
436 | }
437 | }
438 |
439 | return isTruncated;
440 | }
441 | function test( $i, o )
442 | {
443 | return $i.innerHeight() > o.maxHeight;
444 | }
445 | function addEllipsis( txt, o )
446 | {
447 | while( $.inArray( txt.slice( -1 ), o.lastCharacter.remove ) > -1 )
448 | {
449 | txt = txt.slice( 0, -1 );
450 | }
451 | if ( $.inArray( txt.slice( -1 ), o.lastCharacter.noEllipsis ) < 0 )
452 | {
453 | txt += o.ellipsis;
454 | }
455 | return txt;
456 | }
457 | function getSizes( $d )
458 | {
459 | return {
460 | 'width' : $d.innerWidth(),
461 | 'height': $d.innerHeight()
462 | };
463 | }
464 | function setTextContent( e, content )
465 | {
466 | if ( e.innerText )
467 | {
468 | e.innerText = content;
469 | }
470 | else if ( e.nodeValue )
471 | {
472 | e.nodeValue = content;
473 | }
474 | else if (e.textContent)
475 | {
476 | e.textContent = content;
477 | }
478 |
479 | }
480 | function getTextContent( e )
481 | {
482 | if ( e.innerText )
483 | {
484 | return e.innerText;
485 | }
486 | else if ( e.nodeValue )
487 | {
488 | return e.nodeValue;
489 | }
490 | else if ( e.textContent )
491 | {
492 | return e.textContent;
493 | }
494 | else
495 | {
496 | return "";
497 | }
498 | }
499 | function getElement( e, $i )
500 | {
501 | if ( typeof e == 'undefined' )
502 | {
503 | return false;
504 | }
505 | if ( !e )
506 | {
507 | return false;
508 | }
509 | if ( typeof e == 'string' )
510 | {
511 | e = $(e, $i);
512 | return ( e.length )
513 | ? e
514 | : false;
515 | }
516 | if ( typeof e == 'object' )
517 | {
518 | return ( typeof e.jquery == 'undefined' )
519 | ? false
520 | : e;
521 | }
522 | return false;
523 | }
524 | function getTrueInnerHeight( $el )
525 | {
526 | var h = $el.innerHeight(),
527 | a = [ 'paddingTop', 'paddingBottom' ];
528 |
529 | for ( var z = 0, l = a.length; z < l; z++ ) {
530 | var m = parseInt( $el.css( a[ z ] ), 10 );
531 | if ( isNaN( m ) )
532 | {
533 | m = 0;
534 | }
535 | h -= m;
536 | }
537 | return h;
538 | }
539 | function debug( d, m )
540 | {
541 | if ( !d )
542 | {
543 | return false;
544 | }
545 | if ( typeof m == 'string' )
546 | {
547 | m = 'dotdotdot: ' + m;
548 | }
549 | else
550 | {
551 | m = [ 'dotdotdot:', m ];
552 | }
553 |
554 | if ( typeof window.console != 'undefined' )
555 | {
556 | if ( typeof window.console.log != 'undefined' )
557 | {
558 | window.console.log( m );
559 | }
560 | }
561 | return false;
562 | }
563 |
564 |
565 | // override jQuery.html
566 | var _orgHtml = $.fn.html;
567 | $.fn.html = function( str ) {
568 | if ( typeof str != 'undefined' )
569 | {
570 | if ( this.data( 'dotdotdot' ) )
571 | {
572 | if ( typeof str != 'function' )
573 | {
574 | return this.trigger( 'update', [ str ] );
575 | }
576 | }
577 | return _orgHtml.call( this, str );
578 | }
579 | return _orgHtml.call( this );
580 | };
581 |
582 |
583 | // override jQuery.text
584 | var _orgText = $.fn.text;
585 | $.fn.text = function( str ) {
586 | if ( typeof str != 'undefined' )
587 | {
588 | if ( this.data( 'dotdotdot' ) )
589 | {
590 | var temp = $( '' );
591 | temp.text( str );
592 | str = temp.html();
593 | temp.remove();
594 | return this.trigger( 'update', [ str ] );
595 | }
596 | return _orgText.call( this, str );
597 | }
598 | return _orgText.call( this );
599 | };
600 |
601 |
602 | })( jQuery );
603 |
--------------------------------------------------------------------------------
/docs/api/js/jquery.dotdotdot-1.5.9.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery dotdotdot 1.5.9
3 | *
4 | * Copyright (c) 2013 Fred Heusschen
5 | * www.frebsite.nl
6 | *
7 | * Plugin website:
8 | * dotdotdot.frebsite.nl
9 | *
10 | * Dual licensed under the MIT and GPL licenses.
11 | * http://en.wikipedia.org/wiki/MIT_License
12 | * http://en.wikipedia.org/wiki/GNU_General_Public_License
13 | */
14 |
15 | (function(a){function c(a,b,c){var d=a.children(),e=!1;a.empty();for(var g=0,h=d.length;h>g;g++){var i=d.eq(g);if(a.append(i),c&&a.append(c),f(a,b)){i.remove(),e=!0;break}c&&c.remove()}return e}function d(b,c,g,h,i){var j=b.contents(),k=!1;b.empty();for(var l="table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, select, optgroup, option, textarea, script, style",m=0,n=j.length;n>m&&!k;m++){var o=j[m],p=a(o);void 0!==o&&(b.append(p),i&&b[b.is(l)?"after":"append"](i),3==o.nodeType?f(g,h)&&(k=e(p,c,g,h,i)):k=d(p,c,g,h,i),k||i&&i.remove())}return k}function e(a,b,c,d,h){var k=!1,l=a[0];if(l===void 0)return!1;for(var m="letter"==d.wrap?"":" ",n=j(l).split(m),o=-1,p=-1,q=0,r=n.length-1;r>=q;){var s=Math.floor((q+r)/2);if(s==p)break;p=s,i(l,n.slice(0,p+1).join(m)+d.ellipsis),f(c,d)?r=p:(o=p,q=p)}if(-1==o||1==n.length&&0==n[0].length){var u=a.parent();a.remove();var v=h?h.length:0;if(u.contents().size()>v){var w=u.contents().eq(-1-v);k=e(w,b,c,d,h)}else{var x=u.prev(),l=x.contents().eq(-1)[0];if(l!==void 0){var t=g(j(l),d);i(l,t),h&&x.append(h),u.remove(),k=!0}}}else{var t=g(n.slice(0,o+1).join(m),d);k=!0,i(l,t)}return k}function f(a,b){return a.innerHeight()>b.maxHeight}function g(b,c){for(;a.inArray(b.slice(-1),c.lastCharacter.remove)>-1;)b=b.slice(0,-1);return 0>a.inArray(b.slice(-1),c.lastCharacter.noEllipsis)&&(b+=c.ellipsis),b}function h(a){return{width:a.innerWidth(),height:a.innerHeight()}}function i(a,b){a.innerText?a.innerText=b:a.nodeValue?a.nodeValue=b:a.textContent&&(a.textContent=b)}function j(a){return a.innerText?a.innerText:a.nodeValue?a.nodeValue:a.textContent?a.textContent:""}function k(b,c){return b===void 0?!1:b?"string"==typeof b?(b=a(b,c),b.length?b:!1):"object"==typeof b?b.jquery===void 0?!1:b:!1:!1}function l(a){for(var b=a.innerHeight(),c=["paddingTop","paddingBottom"],d=0,e=c.length;e>d;d++){var f=parseInt(a.css(c[d]),10);isNaN(f)&&(f=0),b-=f}return b}function m(a,b){return a?(b="string"==typeof b?"dotdotdot: "+b:["dotdotdot:",b],window.console!==void 0&&window.console.log!==void 0&&window.console.log(b),!1):!1}if(!a.fn.dotdotdot){a.fn.dotdotdot=function(e){if(0==this.length)return e&&e.debug===!1||m(!0,'No element found for "'+this.selector+'".'),this;if(this.length>1)return this.each(function(){a(this).dotdotdot(e)});var g=this;g.data("dotdotdot")&&g.trigger("destroy.dot"),g.data("dotdotdot-style",g.attr("style")),g.css("word-wrap","break-word"),g.bind_events=function(){return g.bind("update.dot",function(b,e){b.preventDefault(),b.stopPropagation(),j.maxHeight="number"==typeof j.height?j.height:l(g),j.maxHeight+=j.tolerance,e!==void 0&&(("string"==typeof e||e instanceof HTMLElement)&&(e=a("").append(e).contents()),e instanceof a&&(i=e)),q=g.wrapInner('').children(),q.empty().append(i.clone(!0)).css({height:"auto",width:"auto",border:"none",padding:0,margin:0});var h=!1,k=!1;return n.afterElement&&(h=n.afterElement.clone(!0),n.afterElement.remove()),f(q,j)&&(k="children"==j.wrap?c(q,j,h):d(q,g,q,j,h)),q.replaceWith(q.contents()),q=null,a.isFunction(j.callback)&&j.callback.call(g[0],k,i),n.isTruncated=k,k}).bind("isTruncated.dot",function(a,b){return a.preventDefault(),a.stopPropagation(),"function"==typeof b&&b.call(g[0],n.isTruncated),n.isTruncated}).bind("originalContent.dot",function(a,b){return a.preventDefault(),a.stopPropagation(),"function"==typeof b&&b.call(g[0],i),i}).bind("destroy.dot",function(a){a.preventDefault(),a.stopPropagation(),g.unwatch().unbind_events().empty().append(i).attr("style",g.data("dotdotdot-style")).data("dotdotdot",!1)}),g},g.unbind_events=function(){return g.unbind(".dot"),g},g.watch=function(){if(g.unwatch(),"window"==j.watch){var b=a(window),c=b.width(),d=b.height();b.bind("resize.dot"+n.dotId,function(){c==b.width()&&d==b.height()&&j.windowResizeFix||(c=b.width(),d=b.height(),p&&clearInterval(p),p=setTimeout(function(){g.trigger("update.dot")},10))})}else o=h(g),p=setInterval(function(){var a=h(g);(o.width!=a.width||o.height!=a.height)&&(g.trigger("update.dot"),o=h(g))},100);return g},g.unwatch=function(){return a(window).unbind("resize.dot"+n.dotId),p&&clearInterval(p),g};var i=g.contents(),j=a.extend(!0,{},a.fn.dotdotdot.defaults,e),n={},o={},p=null,q=null;return n.afterElement=k(j.after,g),n.isTruncated=!1,n.dotId=b++,g.data("dotdotdot",!0).bind_events().trigger("update.dot"),j.watch&&g.watch(),g},a.fn.dotdotdot.defaults={ellipsis:"... ",wrap:"word",lastCharacter:{remove:[" ",",",";",".","!","?"],noEllipsis:[]},tolerance:0,callback:null,after:null,height:null,watch:!1,windowResizeFix:!0,debug:!1};var b=1,n=a.fn.html;a.fn.html=function(a){return a!==void 0?this.data("dotdotdot")&&"function"!=typeof a?this.trigger("update",[a]):n.call(this,a):n.call(this)};var o=a.fn.text;a.fn.text=function(b){if(b!==void 0){if(this.data("dotdotdot")){var c=a("");return c.text(b),b=c.html(),c.remove(),this.trigger("update",[b])}return o.call(this,b)}return o.call(this)}}})(jQuery);
--------------------------------------------------------------------------------
/docs/api/js/jquery.mousewheel.js:
--------------------------------------------------------------------------------
1 | /*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
2 | * Licensed under the MIT License (LICENSE.txt).
3 | *
4 | * Version: 3.1.9
5 | *
6 | * Requires: jQuery 1.2.2+
7 | */
8 |
9 | (function (factory) {
10 | if ( typeof define === 'function' && define.amd ) {
11 | // AMD. Register as an anonymous module.
12 | define(['jquery'], factory);
13 | } else if (typeof exports === 'object') {
14 | // Node/CommonJS style for Browserify
15 | module.exports = factory;
16 | } else {
17 | // Browser globals
18 | factory(jQuery);
19 | }
20 | }(function ($) {
21 |
22 | var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
23 | toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
24 | ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
25 | slice = Array.prototype.slice,
26 | nullLowestDeltaTimeout, lowestDelta;
27 |
28 | if ( $.event.fixHooks ) {
29 | for ( var i = toFix.length; i; ) {
30 | $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
31 | }
32 | }
33 |
34 | var special = $.event.special.mousewheel = {
35 | version: '3.1.9',
36 |
37 | setup: function() {
38 | if ( this.addEventListener ) {
39 | for ( var i = toBind.length; i; ) {
40 | this.addEventListener( toBind[--i], handler, false );
41 | }
42 | } else {
43 | this.onmousewheel = handler;
44 | }
45 | // Store the line height and page height for this particular element
46 | $.data(this, 'mousewheel-line-height', special.getLineHeight(this));
47 | $.data(this, 'mousewheel-page-height', special.getPageHeight(this));
48 | },
49 |
50 | teardown: function() {
51 | if ( this.removeEventListener ) {
52 | for ( var i = toBind.length; i; ) {
53 | this.removeEventListener( toBind[--i], handler, false );
54 | }
55 | } else {
56 | this.onmousewheel = null;
57 | }
58 | },
59 |
60 | getLineHeight: function(elem) {
61 | return parseInt($(elem)['offsetParent' in $.fn ? 'offsetParent' : 'parent']().css('fontSize'), 10);
62 | },
63 |
64 | getPageHeight: function(elem) {
65 | return $(elem).height();
66 | },
67 |
68 | settings: {
69 | adjustOldDeltas: true
70 | }
71 | };
72 |
73 | $.fn.extend({
74 | mousewheel: function(fn) {
75 | return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
76 | },
77 |
78 | unmousewheel: function(fn) {
79 | return this.unbind('mousewheel', fn);
80 | }
81 | });
82 |
83 |
84 | function handler(event) {
85 | var orgEvent = event || window.event,
86 | args = slice.call(arguments, 1),
87 | delta = 0,
88 | deltaX = 0,
89 | deltaY = 0,
90 | absDelta = 0;
91 | event = $.event.fix(orgEvent);
92 | event.type = 'mousewheel';
93 |
94 | // Old school scrollwheel delta
95 | if ( 'detail' in orgEvent ) { deltaY = orgEvent.detail * -1; }
96 | if ( 'wheelDelta' in orgEvent ) { deltaY = orgEvent.wheelDelta; }
97 | if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY; }
98 | if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }
99 |
100 | // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
101 | if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
102 | deltaX = deltaY * -1;
103 | deltaY = 0;
104 | }
105 |
106 | // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
107 | delta = deltaY === 0 ? deltaX : deltaY;
108 |
109 | // New school wheel delta (wheel event)
110 | if ( 'deltaY' in orgEvent ) {
111 | deltaY = orgEvent.deltaY * -1;
112 | delta = deltaY;
113 | }
114 | if ( 'deltaX' in orgEvent ) {
115 | deltaX = orgEvent.deltaX;
116 | if ( deltaY === 0 ) { delta = deltaX * -1; }
117 | }
118 |
119 | // No change actually happened, no reason to go any further
120 | if ( deltaY === 0 && deltaX === 0 ) { return; }
121 |
122 | // Need to convert lines and pages to pixels if we aren't already in pixels
123 | // There are three delta modes:
124 | // * deltaMode 0 is by pixels, nothing to do
125 | // * deltaMode 1 is by lines
126 | // * deltaMode 2 is by pages
127 | if ( orgEvent.deltaMode === 1 ) {
128 | var lineHeight = $.data(this, 'mousewheel-line-height');
129 | delta *= lineHeight;
130 | deltaY *= lineHeight;
131 | deltaX *= lineHeight;
132 | } else if ( orgEvent.deltaMode === 2 ) {
133 | var pageHeight = $.data(this, 'mousewheel-page-height');
134 | delta *= pageHeight;
135 | deltaY *= pageHeight;
136 | deltaX *= pageHeight;
137 | }
138 |
139 | // Store lowest absolute delta to normalize the delta values
140 | absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
141 |
142 | if ( !lowestDelta || absDelta < lowestDelta ) {
143 | lowestDelta = absDelta;
144 |
145 | // Adjust older deltas if necessary
146 | if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
147 | lowestDelta /= 40;
148 | }
149 | }
150 |
151 | // Adjust older deltas if necessary
152 | if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
153 | // Divide all the things by 40!
154 | delta /= 40;
155 | deltaX /= 40;
156 | deltaY /= 40;
157 | }
158 |
159 | // Get a whole, normalized value for the deltas
160 | delta = Math[ delta >= 1 ? 'floor' : 'ceil' ](delta / lowestDelta);
161 | deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
162 | deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);
163 |
164 | // Add information to the event object
165 | event.deltaX = deltaX;
166 | event.deltaY = deltaY;
167 | event.deltaFactor = lowestDelta;
168 | // Go ahead and set deltaMode to 0 since we converted to pixels
169 | // Although this is a little odd since we overwrite the deltaX/Y
170 | // properties with normalized deltas.
171 | event.deltaMode = 0;
172 |
173 | // Add event and delta to the front of the arguments
174 | args.unshift(event, delta, deltaX, deltaY);
175 |
176 | // Clearout lowestDelta after sometime to better
177 | // handle multiple device types that give different
178 | // a different lowestDelta
179 | // Ex: trackpad = 3 and mouse wheel = 120
180 | if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
181 | nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);
182 |
183 | return ($.event.dispatch || $.event.handle).apply(this, args);
184 | }
185 |
186 | function nullLowestDelta() {
187 | lowestDelta = null;
188 | }
189 |
190 | function shouldAdjustOldDeltas(orgEvent, absDelta) {
191 | // If this is an older event and the delta is divisable by 120,
192 | // then we are assuming that the browser is treating this as an
193 | // older mouse wheel event and that we should divide the deltas
194 | // by 40 to try and get a more usable deltaFactor.
195 | // Side note, this actually impacts the reported scroll distance
196 | // in older browsers and can cause scrolling to be slower than native.
197 | // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
198 | return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
199 | }
200 |
201 | }));
202 |
--------------------------------------------------------------------------------
/docs/api/js/jquery.smooth-scroll.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | function filterPath(string) {
3 | return string
4 | .replace(/^\//,'')
5 | .replace(/(index|default).[a-zA-Z]{3,4}$/,'')
6 | .replace(/\/$/,'');
7 | }
8 | var locationPath = filterPath(location.pathname);
9 |
10 | $('a[href*=#]').each(function() {
11 | var thisPath = filterPath(this.pathname) || locationPath;
12 | if ( locationPath == thisPath
13 | && (location.hostname == this.hostname || !this.hostname)
14 | && this.hash.replace(/#/,'') ) {
15 | var $target = $(this.hash), target = this.hash;
16 | if (target) {
17 | $(this).click(function(event) {
18 | if (!$(this.hash).offset()) {
19 | return;
20 | }
21 |
22 | event.preventDefault();
23 | position = $(this.hash).offset().top;
24 |
25 | $('html,body').animate({scrollTop: position}, 400, function() {
26 | location.hash = target;
27 | });
28 | });
29 | }
30 | }
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/docs/api/js/prism.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Prism: Lightweight, robust, elegant syntax highlighting
3 | * MIT license http://www.opensource.org/licenses/mit-license.php/
4 | * @author Lea Verou http://lea.verou.me
5 | */(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var r={};for(var i in e)e.hasOwnProperty(i)&&(r[i]=t.util.clone(e[i]));return r;case"Array":return e.slice()}return e}},languages:{extend:function(e,n){var r=t.util.clone(t.languages[e]);for(var i in n)r[i]=n[i];return r},insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);t.util.type(e)==="Object"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"").replace(/\s+/g," ")+" language-"+o;a=r.parentNode;/pre/i.test(a.nodeName)&&(a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+o);var f=r.textContent;if(!f)return;f=f.replace(/&/g,"&").replace(/e.length)break e;if(p instanceof i)continue;a.lastIndex=0;var d=a.exec(p);if(d){l&&(c=d[1].length);var v=d.index-1+c,d=d[0].slice(c),m=d.length,g=v+m,y=p.slice(0,v+1),b=p.slice(g+1),w=[h,1];y&&w.push(y);var E=new i(u,f?t.tokenize(d,f):d);w.push(E);b&&w.push(b);Array.prototype.splice.apply(s,w)}}}return s},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e,r,i){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]")return e.map(function(t){return n.stringify(t,r,e)}).join("");var s={type:e.type,content:n.stringify(e.content,r,i),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:i};s.type=="comment"&&(s.attributes.spellcheck="true");t.hooks.run("wrap",s);var o="";for(var u in s.attributes)o+=u+'="'+(s.attributes[u]||"")+'"';return"<"+s.tag+' class="'+s.classes.join(" ")+'" '+o+">"+s.content+""+s.tag+">"};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.tokenize(i,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();;
6 | Prism.languages.markup={comment:/<!--[\w\W]*?-->/g,prolog:/<\?.+?\?>/,doctype:/<!DOCTYPE.+?>/,cdata:/<!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/&#?[\da-z]{1,8};/gi};Prism.hooks.add("wrap",function(e){e.type==="entity"&&(e.attributes.title=e.content.replace(/&/,"&"))});;
7 | Prism.languages.css={comment:/\/\*[\w\W]*?\*\//g,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*{))/gi,inside:{punctuation:/[;:]/g}},url:/url\((["']?).*?\1\)/gi,selector:/[^\{\}\s][^\{\};]*(?=\s*\{)/g,property:/(\b|\B)[\w-]+(?=\s*:)/ig,string:/("|')(\\?.)*?\1/g,important:/\B!important\b/gi,ignore:/&(lt|gt|amp);/gi,punctuation:/[\{\};:]/g};Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{style:{pattern:/(<|<)style[\w\W]*?(>|>)[\w\W]*?(<|<)\/style(>|>)/ig,inside:{tag:{pattern:/(<|<)style[\w\W]*?(>|>)|(<|<)\/style(>|>)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css}}});;
8 | Prism.languages.css.selector={pattern:/[^\{\}\s][^\{\}]*(?=\s*\{)/g,inside:{"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/g,"pseudo-class":/:[-\w]+(?:\(.*\))?/g,"class":/\.[-:\.\w]+/g,id:/#[-:\.\w]+/g}};Prism.languages.insertBefore("css","ignore",{hexcode:/#[\da-f]{3,6}/gi,entity:/\\[\da-f]{1,8}/gi,number:/[\d%\.]+/g,"function":/(attr|calc|cross-fade|cycle|element|hsla?|image|lang|linear-gradient|matrix3d|matrix|perspective|radial-gradient|repeating-linear-gradient|repeating-radial-gradient|rgba?|rotatex|rotatey|rotatez|rotate3d|rotate|scalex|scaley|scalez|scale3d|scale|skewx|skewy|skew|steps|translatex|translatey|translatez|translate3d|translate|url|var)/ig});;
9 | Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/ig,inside:{punctuation:/\(/}}, number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|(&){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};;
10 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(var|let|if|else|while|do|for|return|in|instanceof|function|new|with|typeof|try|catch|finally|null|break|continue)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g});Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}});Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(<|<)script[\w\W]*?(>|>)[\w\W]*?(<|<)\/script(>|>)/ig,inside:{tag:{pattern:/(<|<)script[\w\W]*?(>|>)|(<|<)\/script(>|>)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});;
11 | Prism.languages.php=Prism.languages.extend("clike",{keyword:/\b(and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|extends|private|protected|parent|static|throw|null|echo|print|trait|namespace|use|final|yield|goto|instanceof|finally|try|catch)\b/ig, constant:/\b[A-Z0-9_]{2,}\b/g});Prism.languages.insertBefore("php","keyword",{delimiter:/(\?>|<\?php|<\?)/ig,variable:/(\$\w+)\b/ig,"package":{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/g,lookbehind:!0,inside:{punctuation:/\\/}}});Prism.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/g,lookbehind:!0}}); Prism.languages.markup&&(Prism.hooks.add("before-highlight",function(a){"php"===a.language&&(a.tokenStack=[],a.code=a.code.replace(/(?:<\?php|<\?|<\?php|<\?)[\w\W]*?(?:\?>|\?>)/ig,function(b){a.tokenStack.push(b);return"{{{PHP"+a.tokenStack.length+"}}}"}))}),Prism.hooks.add("after-highlight",function(a){if("php"===a.language){for(var b=0,c;c=a.tokenStack[b];b++)a.highlightedCode=a.highlightedCode.replace("{{{PHP"+(b+1)+"}}}",Prism.highlight(c,a.grammar,"php"));a.element.innerHTML=a.highlightedCode}}), Prism.hooks.add("wrap",function(a){"php"===a.language&&"markup"===a.type&&(a.content=a.content.replace(/(\{\{\{PHP[0-9]+\}\}\})/g,'$1'))}),Prism.languages.insertBefore("php","comment",{markup:{pattern:/(<|<)[^?]\/?(.*?)(>|>)/g,inside:Prism.languages.markup},php:/\{\{\{PHP[0-9]+\}\}\}/g}));;
12 | Prism.languages.insertBefore("php","variable",{"this":/\$this/g,global:/\$_?(GLOBALS|SERVER|GET|POST|FILES|REQUEST|SESSION|ENV|COOKIE|HTTP_RAW_POST_DATA|argc|argv|php_errormsg|http_response_header)/g,scope:{pattern:/\b[\w\\]+::/g,inside:{keyword:/(static|self|parent)/,punctuation:/(::|\\)/}}});;
13 | (function(){function e(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function n(e,t,n){var r=t.replace(/\s+/g,"").split(","),i=+e.getAttribute("data-line-offset")||0,s=parseFloat(getComputedStyle(e).lineHeight);for(var o=0,u;u=r[o++];){u=u.split("-");var a=+u[0],f=+u[1]||a,l=document.createElement("div");l.textContent=Array(f-a+2).join(" \r\n");l.className=(n||"")+" line-highlight";l.setAttribute("data-start",a);f>a&&l.setAttribute("data-end",f);l.style.top=(a-i-1)*s+"px";(e.querySelector("code")||e).appendChild(l)}}function r(){var t=location.hash.slice(1);e(".temporary.line-highlight").forEach(function(e){e.parentNode.removeChild(e)});var r=(t.match(/\.([\d,-]+)$/)||[,""])[1];if(!r||document.getElementById(t))return;var i=t.slice(0,t.lastIndexOf(".")),s=document.getElementById(i);if(!s)return;s.hasAttribute("data-line")||s.setAttribute("data-line","");n(s,r,"temporary ");document.querySelector(".temporary.line-highlight").scrollIntoView()}if(!window.Prism)return;var t=crlf=/\r?\n|\r/g,i=0;Prism.hooks.add("after-highlight",function(t){var s=t.element.parentNode,o=s&&s.getAttribute("data-line");if(!s||!o||!/pre/i.test(s.nodeName))return;clearTimeout(i);e(".line-highlight",s).forEach(function(e){e.parentNode.removeChild(e)});n(s,o);i=setTimeout(r,1)});addEventListener("hashchange",r)})();;
14 | Prism.hooks.add("after-highlight",function(e){var t=e.element.parentNode;if(!t||!/pre/i.test(t.nodeName)||t.className.indexOf("line-numbers")===-1){return}var n=1+e.code.split("\n").length;var r;lines=new Array(n);lines=lines.join("");r=document.createElement("span");r.className="line-numbers-rows";r.innerHTML=lines;if(t.hasAttribute("data-start")){t.style.counterReset="linenumber "+(parseInt(t.getAttribute("data-start"),10)-1)}e.element.appendChild(r)})
15 | ;
16 | (function(){if(!self.Prism||!self.document||!document.querySelector)return;var e={js:"javascript",html:"markup",svg:"markup"};Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){var n=t.getAttribute("data-src"),r=(n.match(/\.(\w+)$/)||[,""])[1],i=e[r]||r,s=document.createElement("code");s.className="language-"+i;t.textContent="";s.textContent="Loading…";t.appendChild(s);var o=new XMLHttpRequest;o.open("GET",n,!0);o.onreadystatechange=function(){if(o.readyState==4)if(o.status<400&&o.responseText){s.textContent=o.responseText;Prism.highlightElement(s)}else o.status>=400?s.textContent="✖ Error "+o.status+" while fetching file: "+o.statusText:s.textContent="✖ Error: File does not exist or is empty"};o.send(null)})})();;
17 |
--------------------------------------------------------------------------------
/docs/api/reports/deprecated.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | » Deprecated elements
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
93 |
94 |
95 |
96 |
97 |
102 |
103 |
104 |
105 | - \
106 | - Deprecated elements
107 |
108 |
109 |
110 |
No deprecated elements have been found in this project.
111 |
112 |
113 |
114 |
115 |
116 |
117 |
159 |
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/docs/api/reports/errors.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | » Compilation errors
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
93 |
94 |
95 |
96 |
104 |
105 |
106 |
107 | - \
108 | - Compilation Errors
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | Exception.php
125 | 1
126 |
127 |
128 |
129 |
130 |
131 | Type |
132 | Line |
133 | Description |
134 |
135 |
136 |
137 |
138 | error |
139 | 12 |
140 | No summary for class \IBT\JsonDB\Exception |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | Indexer/Exception.php
155 | 1
156 |
157 |
158 |
159 |
160 |
161 | Type |
162 | Line |
163 | Description |
164 |
165 |
166 |
167 |
168 | error |
169 | 12 |
170 | No summary for class \IBT\JsonDB\Indexer\Exception |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
221 |
222 |
223 |
224 |
225 |
--------------------------------------------------------------------------------
/docs/api/reports/markers.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | » Markers
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
93 |
94 |
95 |
96 |
101 |
102 |
103 |
104 |
105 | - \
106 | - Markers
107 |
108 |
109 |
No markers have been found in this project.
110 |
111 |
112 |
113 |
114 |
115 |
116 |
158 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/docs/getting_started.md:
--------------------------------------------------------------------------------
1 | # Getting started
2 |
3 | ## Installation (new project)
4 |
5 | ```
6 | composer init --require="johnwilson/jsondb"
7 | ```
8 |
9 | ## Call autoloader
10 |
11 | ```PHP
12 | require_once __DIR__ . '/vendor/autoload.php';
13 | ```
14 |
15 | ## Import the relevant namespaces as required:
16 |
17 | ```PHP
18 | use IBT\JsonDB\Client;
19 | use IBT\JsonDB\Collection;
20 | ```
21 |
22 | [Continue to Tutorial](https://github.com/johnwilson/jsondb/blob/master/docs/tutorial.md) or [Back to Table of Contents](https://github.com/johnwilson/jsondb/blob/master/docs/index.md)
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | JsonDB borrows a few concepts from Mongodb such as:
2 |
3 | * **Document**: which is a valid JSON object stored in the database
4 | * **Collection**: which is a group of documents (comparable to a table but schema-less)
5 |
6 | JsonDB also gives you the power to query your JSON data using familiar SQL statements (`Where`, `Or Where`, `Order By`).
7 |
8 | ## Contents
9 |
10 | * [Getting Started](https://github.com/johnwilson/jsondb/blob/master/docs/getting_started.md)
11 | * [Tutorial](https://github.com/johnwilson/jsondb/blob/master/docs/tutorial.md)
--------------------------------------------------------------------------------
/docs/tutorial.md:
--------------------------------------------------------------------------------
1 | # Quick tutorial
2 |
3 | This tutorial will get you up and running with JsonDB by performing basic CRUD (Create, Read, Update and Delete) operations. For further usage guidelines you can have a look at the code in the `tests` directory.
4 |
5 | ## Preparation
6 |
7 | If you've setup your project according to the guidelines in the [getting started](https://github.com/johnwilson/jsondb/blob/master/docs/getting_started.md) section, you should be ready to follow this tutorial.
8 |
9 | The following code will get you ready to run your CRUD operations
10 |
11 | ```PHP
12 | 'database',
24 | 'username' => 'username',
25 | 'password' => 'password'
26 | ]);
27 |
28 | // setup
29 | $c->setup();
30 | ```
31 |
32 | ## Creating a document
33 |
34 | JSON documents reside in **Collections** which can be thought of as tables. Their main purpose is to keep your documents grouped in any logical manner you see fit.
35 |
36 | ```PHP
37 | // create collection
38 | $col = $c->newCollection("users");
39 | ```
40 |
41 | Each JSON document has a special `_id` field which you can add yourself or let the library create it for you. the `_id` is an alpha-numeric only string.
42 |
43 | ```PHP
44 | // insert json
45 | $j = '{"name":"jason bourne", "category":"agent", "_id": "1"}';
46 | $oid = $col->insert($j);
47 | ```
48 |
49 | The value returned on insert will be either the library generated `_id` which will be similar to `oid57b57b407fe93` or the value you supplied in the `_id` field of your JSON document.
50 |
51 | ## Reading a document
52 |
53 | Retrieving a document for which you have the `_id` is as simple as running the following which will return your document as a PHP `object`:
54 |
55 | ```PHP
56 | $obj = $col->get($oid);
57 | ```
58 |
59 | ## Updating a document
60 |
61 | You can modify your JSON document and save it to the database just as simply as doing the following:
62 |
63 | ```PHP
64 | $obj->category = "ghost";
65 | $col->update(json_encode($obj));
66 | ```
67 |
68 | A boolean is returned to indicate whether the operation was successful.
69 |
70 | Care should be taken not to change or remove the `_id` field or the library will assume the document is new and create a new entry.
71 |
72 | ## Deleting a document
73 |
74 | Finally to delete the document you'd do the following:
75 |
76 | ```PHP
77 | $col->delete($oid);
78 | ```
79 |
80 | Again a boolean value will indicate the success or failure of the operation.
81 |
82 | [Back to Table of Contents](https://github.com/johnwilson/jsondb/blob/master/docs/index.md)
83 |
84 |
--------------------------------------------------------------------------------
/jsondb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwilson/jsondb/7f72443c69128f24cb1cf7ec3adb31263729db3f/jsondb.png
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | ./tests/
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/Client.php:
--------------------------------------------------------------------------------
1 | setup();
24 | * ```
25 | */
26 | class Client {
27 |
28 | /**
29 | * Collection Table name
30 | */
31 | const T_COLLECTIONS = 'jsondb_collection';
32 |
33 | /**
34 | * JSON data Table name
35 | *
36 | */
37 | const T_JSON = 'jsondb_json';
38 |
39 | /**
40 | * JSON indices Table name
41 | *
42 | */
43 | const T_INDEX = 'jsondb_index';
44 |
45 | /**
46 | * Configuration manager for Illuminate framework
47 | *
48 | * @var \Illuminate\Database\Capsule\Manager
49 | * @see https://laravel.com/api/5.2/Illuminate/Database/Capsule/Manager.html.
50 | */
51 | private $capsule;
52 |
53 | /**
54 | * Constructor, creates a new Client
55 | *
56 | * @param array $config
57 | */
58 | public function __construct($config = array()) {
59 | // setup db connection
60 | $capsule = new Capsule();
61 |
62 | // default configuration
63 | $default = [
64 | 'driver' => env('DB_DRIVER', 'mysql'),
65 | 'host' => env('DB_HOST', 'localhost'),
66 | 'port' => env('DB_PORT', 3306),
67 | 'database' => env('DB_DATABASE', ''),
68 | 'username' => env('DB_USERNAME', ''),
69 | 'password' => env('DB_PASSWORD', ''),
70 | 'charset' => env('DB_CHARSET', 'utf8'),
71 | 'collation' => env('DB_COLLATION', 'utf8_unicode_ci')
72 | ];
73 |
74 | // create final config
75 | $config = array_merge($default, $config);
76 |
77 | $capsule->addConnection($config);
78 | $this->capsule = $capsule;
79 | }
80 |
81 | /**
82 | * Creates/Recreates required tables in database
83 | *
84 | */
85 | public function setup() {
86 | $sb = $this->connection()->getSchemaBuilder();
87 |
88 | // drop existing tables
89 | $sb->dropIfExists(self::T_INDEX);
90 | $sb->dropIfExists(self::T_JSON);
91 | $sb->dropIfExists(self::T_COLLECTIONS);
92 |
93 | // collections table
94 | $sb->create(self::T_COLLECTIONS, function(Blueprint $table){
95 | $table->increments('id');
96 | $table->string("name");
97 | $table->unique("name");
98 | });
99 |
100 | // json document table
101 | $sb->create(self::T_JSON, function(Blueprint $table){
102 | $table->increments('id');
103 |
104 | // document id
105 | $table->string("doc_id", 36);
106 | $table->unique("doc_id");
107 |
108 | // collection id
109 | $table->integer('cid')->unsigned();
110 | $table->foreign('cid')->references('id')
111 | ->on(self::T_COLLECTIONS)
112 | ->onDelete('cascade');
113 |
114 | $table->text('data');
115 | });
116 |
117 | // index table
118 | $sb->create(self::T_INDEX, function(Blueprint $table){
119 | $table->increments('id');
120 |
121 | $table->integer('cid')->unsigned();
122 | $table->foreign('cid')->references('id')
123 | ->on(self::T_COLLECTIONS)
124 | ->onDelete('cascade');
125 |
126 | // object id (primary key)
127 | $table->integer('oid')->unsigned();
128 | $table->foreign('oid')->references('id')
129 | ->on(self::T_JSON)
130 | ->onDelete('cascade');
131 |
132 | $table->integer('depth');
133 | $table->string('typ');
134 |
135 | $table->string('path');
136 | $table->index('path');
137 |
138 | // values
139 | $table->text('vjson')->nullable();
140 | $table->boolean('vboolean')->nullable();
141 | $table->text('vstring')->nullable();
142 | $table->float('vfloat')->nullable();
143 | $table->boolean('vnull')->nullable();
144 | });
145 | }
146 |
147 | /**
148 | * Current database connection
149 | * @return \Illuminate\Database\Connection
150 | */
151 | public function connection() {
152 | return $this->capsule->getConnection();
153 | }
154 |
155 | /**
156 | * Create a new Collection
157 | * @param string $name Collection name
158 | * @return \IBT\JsonDB\Collection
159 | * @throws Exception if the collection name already exists/is invalid
160 | */
161 | public function newCollection($name) {
162 | $name = strtolower($name);
163 |
164 | if(!Collection::isValidName($name)) {
165 | throw new Exception("The collection name $name isn't valid");
166 | }
167 |
168 | if(in_array($name, $this->listCollection())) {
169 | throw new Exception("The collection name $name already exists");
170 | }
171 |
172 | $conn = $this->connection();
173 | $id = $conn->table(self::T_COLLECTIONS)->insertGetId(
174 | ['name' => $name]
175 | );
176 |
177 | return new Collection($id, $name, $this);
178 | }
179 |
180 | /**
181 | * Return existing Collection
182 | * @param string $name Collection name
183 | * @return \IBT\JsonDB\Collection
184 | * @throws Exception if the collection name doesn't exist
185 | */
186 | public function getCollection($name) {
187 | $name = strtolower($name);
188 | $conn = $this->connection();
189 |
190 | // check for existence
191 | $row = $conn->table(self::T_COLLECTIONS)->select("id", "name")->where("name", $name)->first();
192 | if(!$row) {
193 | throw new Exception("The collection name $name doesn't exist");
194 | }
195 |
196 | return new Collection($row->id, $row->name, $this);
197 | }
198 |
199 | /**
200 | * List all collections
201 | * @return array
202 | */
203 | public function listCollection() {
204 | $conn = $this->connection();
205 | $name = $conn->table(self::T_COLLECTIONS)->pluck("name");
206 | return $name;
207 | }
208 | }
--------------------------------------------------------------------------------
/src/Collection.php:
--------------------------------------------------------------------------------
1 | setup();
21 | * $col = $c->newCollection("users");
22 | * $col->insert('{"name":"jason bourne", "category":"agent"}');
23 | * ```
24 | */
25 | class Collection {
26 |
27 | /**
28 | * @var string Name of the Collection
29 | */
30 | private $name;
31 |
32 | /**
33 | * @var int Collection database primary key
34 | */
35 | private $id;
36 |
37 | /**
38 | * @var Client JsonDB Client
39 | */
40 | private $client;
41 |
42 | /**
43 | * Constructor, creates a new Collection
44 | *
45 | * @param int $id Database primary key
46 | * @param string $name Collection name
47 | * @param Client $client
48 | */
49 | function __construct($id, $name, Client $client) {
50 | $this->id = $id;
51 | $this->name = $name;
52 | $this->client = $client;
53 | }
54 |
55 | /**
56 | * Validate Collection name
57 | *
58 | * @param string $name Collection name
59 | * @return boolean
60 | */
61 | static function isValidName($name) {
62 | // only accept alpha characters
63 | $p = '/^[a-zA-Z]+$/';
64 | $m = preg_match($p, $name);
65 | if($m) {
66 | return true;
67 | }
68 |
69 | return false;
70 | }
71 |
72 | /**
73 | * Collection name
74 | *
75 | * @return string
76 | */
77 | function getName() {
78 | return $this->name;
79 | }
80 |
81 | /**
82 | * Collection id
83 | *
84 | * @return int
85 | */
86 | function getId() {
87 | return $this->id;
88 | }
89 |
90 | /**
91 | * Column name from JSON type
92 | *
93 | * @param string $typ
94 | * @return string
95 | * @throws Exception if the JSON type isn't supported
96 | */
97 | public static function getColumn($typ) {
98 | switch ($typ) {
99 | case 'float':
100 | return 'vfloat';
101 | case 'boolean':
102 | return 'vboolean';
103 | case 'string':
104 | return 'vstring';
105 | case 'array':
106 | case 'object':
107 | return 'vjson';
108 | case 'null':
109 | return 'vnull';
110 | default:
111 | throw new Exception('Unknown JSON type');
112 | break;
113 | }
114 | }
115 |
116 | /**
117 | * Save JSON string (create/update)
118 | *
119 | * @param string $doc JSON string
120 | * @return string
121 | */
122 | private function save($doc) {
123 | // create index
124 | $scr = new Scanner();
125 | $index_list = $scr->scan($doc);
126 |
127 | $conn = $this->client->connection();
128 | $cid = $this->id;
129 |
130 | $conn->transaction(function() use ($index_list, $conn, $cid, $doc) {
131 | // add data
132 |
133 | $json = json_encode($doc);
134 | $id = $conn->table(Client::T_JSON)->insertGetId(
135 | ['doc_id' => $doc['_id'], 'cid' => $cid, 'data' => $json]
136 | );
137 |
138 | // add index
139 | $rows = array();
140 | foreach ($index_list as $item) {
141 | $row = [
142 | 'cid' => $cid,
143 | 'oid' => $id,
144 | 'depth' => $item->depth,
145 | 'typ' => $item->typ,
146 | 'path' => $item->path,
147 | 'vfloat' => NULL,
148 | 'vboolean' => NULL,
149 | 'vstring' => NULL,
150 | 'vjson' => NULL,
151 | 'vnull' => NULL
152 | ];
153 |
154 | // insert value in appropriate column
155 | $c = Collection::getColumn($item->typ);
156 | $row[$c] = $item->value;
157 | $rows[] = $row;
158 | }
159 | $conn->table(Client::T_INDEX)->insert($rows);
160 | });
161 |
162 | return $doc['_id'];
163 | }
164 |
165 | /**
166 | * Check if document exists
167 | *
168 | * @param string $id document id
169 | * @return boolean
170 | */
171 | private function exists($id) {
172 | $conn = $this->client->connection();
173 | $row = $conn->table(Client::T_JSON)->select("doc_id")->where("doc_id", $id)->first();
174 | if(!$row) {
175 | return false;
176 | }
177 |
178 | return true;
179 | }
180 |
181 | /**
182 | * Database manager
183 | *
184 | * @return Client
185 | */
186 | function getClient() {
187 | return $this->client;
188 | }
189 |
190 | /**
191 | * Inserts a json document into db.
192 | *
193 | * @param string $json JSON string to insert
194 | * @return string Returns document id
195 | * @throws Exception if the document id isn't valid
196 | */
197 | function insert($json) {
198 | $doc = json_decode($json, true);
199 |
200 | // check if json has id
201 | if(!array_key_exists('_id', $doc)) {
202 | $doc['_id'] = uniqid('oid');
203 | } else {
204 | // only accept alpha numeric characters
205 | $p = '/^[a-z0-9]+$/';
206 | $m = preg_match($p, $doc['_id']);
207 | if(!$m) {
208 | throw new Exception('Invalid document id.');
209 | }
210 | }
211 |
212 | return $this->save($doc);
213 | }
214 |
215 | /**
216 | * Retrieve JSON document
217 | *
218 | * @param string $id document id
219 | * @return mixed
220 | */
221 | function get($id) {
222 | $conn = $this->client->connection();
223 | $doc = $conn->table(Client::T_JSON)
224 | ->where('doc_id', $id)
225 | ->where('cid', $this->id)
226 | ->first();
227 |
228 | if(is_null($doc)) {
229 | return NULL;
230 | }
231 |
232 | return json_decode($doc->data);
233 | }
234 |
235 | /**
236 | * Delete JSON document
237 | *
238 | * @param string $oid document id
239 | * @return boolean
240 | */
241 | function delete($oid) {
242 | $conn = $this->client->connection();
243 | return $conn->table(Client::T_JSON)->where('doc_id', $oid)->delete() > 0;
244 | }
245 |
246 | /**
247 | * Updates a JSON document in db.
248 | *
249 | * @param string $json JSON string to insert
250 | * @return string Returns document id
251 | * @throws Exception if the original document id isn't present
252 | */
253 | function update($json) {
254 | $doc = json_decode($json, true);
255 |
256 | // check if json has id
257 | if(!array_key_exists('_id', $doc)) {
258 | throw new Exception('Missing \'_id\' in JSON document');
259 | }
260 |
261 | $oid = $doc['_id'];
262 |
263 | if($this->exists($oid)) {
264 | $this->delete($oid);
265 | $this->save($doc);
266 | return true;
267 | }
268 |
269 | return false;
270 | }
271 |
272 | /**
273 | * Total number of JSON documents
274 | *
275 | * @return int
276 | */
277 | function count() {
278 | $conn = $this->client->connection();
279 | $tbl = $conn->table(Client::T_JSON);
280 | return $tbl->select($conn->raw('distinct(doc_id)'))->count();
281 | }
282 |
283 | /**
284 | * New Filter to query JSON document content
285 | *
286 | * @return \IBT\JsonDB\Filter
287 | */
288 | function newFilter() {
289 | return new Filter($this);
290 | }
291 | }
--------------------------------------------------------------------------------
/src/Exception.php:
--------------------------------------------------------------------------------
1 | setup();
23 | * $col = $c->newCollection("users");
24 | * $col->insert('{"name":"jason bourne", "category":"agent"}');
25 | * $f = $col->newFilter();
26 | * $f->where('category', 'in', ['agent'])->run();
27 | * ```
28 | */
29 | class Filter {
30 |
31 | /**
32 | * @var Collection Collection the filter is based on
33 | */
34 | private $col;
35 |
36 | /**
37 | * @var \Illuminate\Database\Query\Builder Query builder instance
38 | */
39 | private $query;
40 |
41 | /**
42 | * @var string Name of the database View
43 | */
44 | private $view;
45 |
46 | /**
47 | * @var array Fields(or JSON paths) that will be used in the query
48 | */
49 | private $fields;
50 |
51 | /**
52 | * Constructor, creates a new Filter
53 | *
54 | * @param Collection $col Collection the filter is based on
55 | */
56 | public function __construct(Collection $col) {
57 | $this->fields = array();
58 | $this->col = $col;
59 |
60 | $this->view = uniqid('view_');
61 |
62 | $conn = $this->col->getClient()->connection();
63 | $this->query = $conn->table($this->view)
64 | ->join(Client::T_JSON, 'oid', '=', 'id')
65 | ->select('doc_id');
66 |
67 | }
68 |
69 | /**
70 | * Converts JSON value path into valid column name
71 | *
72 | * @param string $path JSON path
73 | * @return string
74 | */
75 | private function sanitizeColName($path) {
76 | return str_replace('.', '_', $path);
77 | }
78 |
79 | /**
80 | * SQL Where statement.
81 | *
82 | * @param string $path JSON path
83 | * @param string $op Operand such as =, <=, >
84 | * @param mixed $value Value
85 | * @return $this
86 | */
87 | public function where($path, $op, $value) {
88 | $this->fields[$path] = 1;
89 | $this->query->where($this->sanitizeColName($path), $op, $value);
90 | return $this;
91 | }
92 |
93 | /**
94 | * SQL Or Where statement.
95 | *
96 | * @param string $path JSON path
97 | * @param string $op Operand such as =, <=, >
98 | * @param mixed $value Value
99 | * @return $this
100 | */
101 | public function orWhere($path, $op, $value) {
102 | $this->fields[$path] = 1;
103 | $this->query->orWhere($this->sanitizeColName($path), $op, $value);
104 | return $this;
105 | }
106 |
107 | /**
108 | * SQL Where Between statement.
109 | *
110 | * @param string $path JSON path
111 | * @param array $value Values
112 | * @return $this
113 | */
114 | public function whereBetween($path, $value) {
115 | $this->fields[$path] = 1;
116 | $this->query->whereBetween($this->sanitizeColName($path), $value);
117 | return $this;
118 | }
119 |
120 | /**
121 | * SQL Where Not Between statement.
122 | *
123 | * @param string $path JSON path
124 | * @param array $value Values
125 | * @return $this
126 | */
127 | public function whereNotBetween($path, $value) {
128 | $this->fields[$path] = 1;
129 | $this->query->whereNotBetween($this->sanitizeColName($path), $value);
130 | return $this;
131 | }
132 |
133 | /**
134 | * SQL Where In statement.
135 | *
136 | * @param string $path JSON path
137 | * @param mixed $value Value(s)
138 | * @return $this
139 | */
140 | public function whereIn($path, $value) {
141 | $this->fields[$path] = 1;
142 | $this->query->whereIn($this->sanitizeColName($path), $value);
143 | return $this;
144 | }
145 |
146 | /**
147 | * SQL Where Not In statement.
148 | *
149 | * @param string $path JSON path
150 | * @param mixed $value Value(s)
151 | * @return $this
152 | */
153 | public function whereNotIn($path, $value) {
154 | $this->fields[$path] = 1;
155 | $this->query->whereNotIn($this->sanitizeColName($path), $value);
156 | return $this;
157 | }
158 |
159 | /**
160 | * SQL Order By statement.
161 | *
162 | * @param string $path JSON path
163 | * @param string $direction Direction i.e 'asc' or 'desc'
164 | * @return $this
165 | */
166 | public function orderBy($path, $direction) {
167 | $this->fields[$path] = 1;
168 | $this->query->orderBy($this->sanitizeColName($path), $direction);
169 | return $this;
170 | }
171 |
172 | /**
173 | * Return View name.
174 | *
175 | * @return string
176 | */
177 | public function viewName() {
178 | return $this->view;
179 | }
180 |
181 | /**
182 | * Make sure JSON paths specified in filter statements exist.
183 | * Prevent SQL injection.
184 | *
185 | * @return array
186 | * @throws Exception if the requested JSON path is invalid
187 | */
188 | private function validateFields() {
189 | // get paths and their data types
190 | $conn = $this->col->getClient()->connection();
191 | $rows = $conn->table(Client::T_INDEX)
192 | ->select($conn->raw('distinct(path)'), 'typ')
193 | ->whereIn('path', array_keys($this->fields))
194 | ->get();
195 |
196 | // quick comparison. can be changed for a more descriptive
197 | // error by comparing values in both lists
198 | if(sizeof($rows) != sizeof($this->fields)) {
199 | throw new Exception("Error: One or more JSON paths don't exist");
200 | }
201 |
202 | return $rows;
203 | }
204 |
205 | /**
206 | * View query string. This can be used for debugging or caching
207 | *
208 | * @return string
209 | */
210 | public function viewSQL() {
211 | $idx_table = Client::T_INDEX;
212 |
213 | // validate fields
214 | $fields = $this->validateFields();
215 |
216 | // oid table
217 | $main = "(select distinct(oid) from ${idx_table}) as a";
218 |
219 | $select = ['a.oid'];
220 |
221 | $join = [$main];
222 |
223 | $tc = 0; // table counter
224 |
225 | // add fields
226 | foreach ($fields as $item) {
227 | // get column name
228 | $col = Collection::getColumn($item->typ);
229 | $field = $item->path;
230 |
231 | $cn = $this->sanitizeColName($field); // column name
232 |
233 | $t = "select oid, case when path='${field}' then ${col} end as ${cn} ";
234 | $t .= "from ${idx_table} having ${cn} is not null";
235 |
236 | $tc++;
237 | $tn = "jn_tbl_" . $tc; // join table alias
238 |
239 | $select[] = "${tn}.${cn}";
240 | $join[] = '(' . $t . ") as $tn on a.oid = $tn.oid";
241 | }
242 |
243 | $sb = 'create view ' . $this->view .' as ';
244 | $sb .= 'select ' . join(",", $select) . ' from ';
245 | $sb .= join(" left join ", $join);
246 |
247 | return $sb;
248 | }
249 |
250 | /**
251 | * View select query string. complements the view function
252 | *
253 | * @return string
254 | */
255 | public function selectSQL() {
256 | return $this->query->toSql();
257 | }
258 |
259 | /**
260 | * Execute the filter
261 | *
262 | * @return array
263 | */
264 | public function run() {
265 | $res = [];
266 | $conn = $this->col->getClient()->connection();
267 | $view = $this->view;
268 | $viewSQL = $this->viewSQL();
269 | $query = $this->query;
270 |
271 | $conn->transaction(function() use (&$res, $conn, $view, $viewSQL, $query) {
272 | // create view
273 | $conn->statement($viewSQL);
274 |
275 | // select data
276 | $res = $query->pluck('doc_id');
277 |
278 | // drop view
279 | $conn->statement('drop view if exists ' . $view);
280 | });
281 |
282 | return $res;
283 | }
284 | }
--------------------------------------------------------------------------------
/src/Indexer/Exception.php:
--------------------------------------------------------------------------------
1 | typ = $typ;
48 | $this->value = $value;
49 | $this->path = $path;
50 | $this->depth = $depth;
51 | }
52 |
53 | /**
54 | * Retrieves a property if it exists.
55 | *
56 | * @see http://php.net/manual/en/language.oop5.overloading.php#object.get
57 | * @param string $property Name of property to retrieve
58 | * @return mixed
59 | * @throws Exception if the property isn't found
60 | */
61 | public function __get($property) {
62 | if(property_exists($this, $property)) {
63 | return $this->$property;
64 | }
65 | throw new Exception('Property not found');
66 | }
67 |
68 | /**
69 | * Return internal properties for debugging purposes.
70 | *
71 | * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo
72 | * @return mixed
73 | */
74 | public function __debugInfo()
75 | {
76 | return array(
77 | "depth" => $this->depth,
78 | "path" => $this->path,
79 | "value" => $this->value,
80 | "type" => $this->typ
81 | );
82 | }
83 | }
--------------------------------------------------------------------------------
/src/Indexer/Scanner.php:
--------------------------------------------------------------------------------
1 | scan(json_decode($j));
19 | * ```
20 | */
21 | class Scanner {
22 |
23 | /**
24 | * Indexes a JSON string.
25 | *
26 | * @param array $json JSON value
27 | * @return array Path
28 | */
29 | public function scan($json) {
30 | $it = new \RecursiveArrayIterator($json); // call global namespace class
31 | $path = ""; // set object root path
32 | $depth = 0; // set path depth
33 | $result = array(); // array for indexes
34 | $fn = array($this, "walk"); // iterator callback function
35 |
36 | iterator_apply($it, $fn, array($it, $path, $depth, &$result));
37 |
38 | return $result;
39 | }
40 |
41 | /**
42 | * Indexes a JSON string.
43 | *
44 | * @param \RecursiveArrayIterator $it Iterator
45 | * @param string $path current JSON path
46 | * @param int $depth current JSON depth
47 | * @param array $result Reference to array containing scanned indexes
48 | */
49 | private function walk($it, $path, $depth, &$result) {
50 | while ($it->valid()) {
51 |
52 | $key = $path . $it->key();
53 | $value = $it->current();
54 | $typ = Scanner::jsonType(gettype($value));
55 | if($typ == "array" || $typ == "object") {
56 | $value = json_encode($value);
57 | }
58 |
59 | $result[] = new Index($typ, $value, $key, $depth);
60 |
61 | if($it->hasChildren()) {
62 | $c = $it->getChildren();
63 | $sub_path = $key . ".";
64 | $depth++;
65 | $this->walk($c, $sub_path, $depth, $result);
66 | }
67 | $it->next();
68 | }
69 | }
70 |
71 | /**
72 | * Returns the equivalent JSON type.
73 | *
74 | * @param string $typ
75 | * @return string
76 | * @throws Exception if the JSON type isn't supported
77 | */
78 | public static function jsonType($typ) {
79 | switch ($typ) {
80 | case 'integer':
81 | case 'double':
82 | return 'float';
83 | case 'NULL':
84 | return 'null';
85 | case 'boolean':
86 | case 'string':
87 | case 'array':
88 | case 'object':
89 | return $typ;
90 | default:
91 | throw new Exception("Unknown JSON type ${typ}");
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/src/helpers.php:
--------------------------------------------------------------------------------
1 | 1 && Str::startsWith($value, '"') && Str::endsWith($value, '"')) {
41 | return substr($value, 1, -1);
42 | }
43 | return $value;
44 | }
45 | }
--------------------------------------------------------------------------------
/tests/ClientTest.php:
--------------------------------------------------------------------------------
1 | setup();
17 |
18 | // make sure collection list is empty
19 | $this->assertEmpty($c->listCollection());
20 |
21 | //create new collection
22 | $c->newCollection("users");
23 | $l = $c->listCollection();
24 | $this->assertContains("users", $l);
25 | $this->assertTrue(count($l) == 1);
26 | $col = $c->getCollection("users");
27 | $this->assertTrue($col->getName() == "users");
28 | }
29 | }
--------------------------------------------------------------------------------
/tests/CollectionTest.php:
--------------------------------------------------------------------------------
1 | setup();
18 |
19 | $col = $c->newCollection("users");
20 |
21 | // insert json
22 | $j = '{"name":"jason bourne", "category":"agent"}';
23 | $oid = $col->insert($j);
24 |
25 | // retrieve and check
26 | $obj = $col->get($oid);
27 | $this->assertEquals($obj->name, "jason bourne");
28 | $this->assertEquals($obj->category, "agent");
29 |
30 | // update
31 | $obj->category = "ghost";
32 | $ok = $col->update(json_encode($obj));
33 | $this->assertTrue($ok);
34 |
35 | // retrieve and check
36 | $obj = $col->get($oid);
37 | $this->assertEquals($obj->name, "jason bourne");
38 | $this->assertEquals($obj->category, "ghost");
39 |
40 | // delete
41 | $ok = $col->delete($oid);
42 | $this->assertTrue($ok);
43 | $obj = $col->get($oid);
44 | $this->assertNull($obj);
45 | }
46 |
47 | public function testCollectionFind() {
48 | // load test data
49 | $str = file_get_contents(__DIR__ . "/test.json");
50 | $j = json_decode($str, true);
51 |
52 | $c = new Client();
53 | $c->setup();
54 |
55 | $c->newCollection("dummy");
56 | $col = $c->newCollection("users");
57 |
58 | // insert json
59 | for ($i=0; $i < count($j); $i++) {
60 | $tmp = json_encode($j[$i]);
61 | $col->insert($tmp);
62 | }
63 |
64 | // count
65 | $this->assertEquals($col->count(), 11);
66 |
67 | // implement filters
68 | $f = $col->newFilter();
69 | $f->whereIn('gender', ['female']);
70 | $res = $f->run();
71 | $this->assertEquals(count($res), 3);
72 |
73 | $f = $col->newFilter();
74 | $f->where('gender', '=', 'male');
75 | $res = $f->run();
76 | $this->assertEquals(count($res), 8);
77 |
78 | $f = $col->newFilter();
79 | $f->where('name', '=', 'Lowery Sheppard');
80 | $res = $f->run();
81 | $this->assertEquals(count($res), 1);
82 | $this->assertEquals($res[0], '575e76e84a60553b98f42544');
83 |
84 | $f = $col->newFilter();
85 | $f->where('gender', '=', 'female')->where('age', '>', 30);
86 | $res = $f->run();
87 | $this->assertEquals(count($res), 2);
88 | }
89 | }
--------------------------------------------------------------------------------
/tests/IndexTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(1, $idx->depth);
19 | $this->assertEquals("user.greeting", $idx->path);
20 | $this->assertEquals("hello", $idx->value);
21 | $this->assertEquals("string", $idx->typ);
22 | }
23 | }
--------------------------------------------------------------------------------
/tests/ScannerTest.php:
--------------------------------------------------------------------------------
1 | scan(json_decode($j));
32 |
33 | $kv = array();
34 | foreach ($r as $index) {
35 | $kv[$index->path] = $index;
36 | }
37 |
38 | $this->assertEquals($kv["fighter"]->value, "iron monkey");
39 | $this->assertEquals($kv["points"]->value, 96);
40 | $this->assertEquals($kv["available"]->value, false);
41 | $this->assertEquals($kv["dob"]->value, null);
42 | $this->assertEquals($kv["styles.0.name"]->value, "功夫");
43 | $this->assertEquals($kv["styles.0.name_eng"]->value, "kung-fu");
44 | }
45 | }
--------------------------------------------------------------------------------
/tests/test.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "_id": "575e76e88bd9a7df73545929",
4 | "active": true,
5 | "balance": 2481.47,
6 | "age": 33,
7 | "name": "Poole Curtis",
8 | "gender": "male",
9 | "tags": [
10 | "laborum",
11 | "est",
12 | "ullamco",
13 | "dolore",
14 | "nostrud",
15 | "sint",
16 | "magna"
17 | ],
18 | "friends": [
19 | {
20 | "id": 0,
21 | "name": "Miller Ballard"
22 | },
23 | {
24 | "id": 1,
25 | "name": "Ashley Gross"
26 | },
27 | {
28 | "id": 2,
29 | "name": "Savannah Mueller"
30 | }
31 | ]
32 | },
33 | {
34 | "_id": "575e76e8cf735ff40b0b87cf",
35 | "active": true,
36 | "balance": 3649.24,
37 | "age": 40,
38 | "name": "Hoover Hudson",
39 | "gender": "male",
40 | "tags": [
41 | "enim",
42 | "id",
43 | "magna",
44 | "aliquip",
45 | "Lorem",
46 | "et",
47 | "mollit"
48 | ],
49 | "friends": [
50 | {
51 | "id": 0,
52 | "name": "Steele Hogan"
53 | },
54 | {
55 | "id": 1,
56 | "name": "Ray Moody"
57 | },
58 | {
59 | "id": 2,
60 | "name": "Ochoa Haynes"
61 | }
62 | ]
63 | },
64 | {
65 | "_id": "575e76e81911bebed4002c82",
66 | "active": true,
67 | "balance": 1289.98,
68 | "age": 40,
69 | "name": "Jannie Coleman",
70 | "gender": "female",
71 | "tags": [
72 | "tempor",
73 | "et",
74 | "duis",
75 | "ut",
76 | "pariatur",
77 | "consequat",
78 | "eu"
79 | ],
80 | "friends": [
81 | {
82 | "id": 0,
83 | "name": "Wilkinson Rodriguez"
84 | },
85 | {
86 | "id": 1,
87 | "name": "Delacruz Bonner"
88 | },
89 | {
90 | "id": 2,
91 | "name": "Lang Dean"
92 | }
93 | ]
94 | },
95 | {
96 | "_id": "575e76e865a5210bcfa8c0a7",
97 | "active": false,
98 | "balance": 2083.73,
99 | "age": 27,
100 | "name": "Walters Booker",
101 | "gender": "male",
102 | "tags": [
103 | "dolor",
104 | "reprehenderit",
105 | "reprehenderit",
106 | "eiusmod",
107 | "ipsum",
108 | "ullamco",
109 | "quis"
110 | ],
111 | "friends": [
112 | {
113 | "id": 0,
114 | "name": "Dorothy Simpson"
115 | },
116 | {
117 | "id": 1,
118 | "name": "Krystal Mccray"
119 | },
120 | {
121 | "id": 2,
122 | "name": "Sheila Beach"
123 | }
124 | ]
125 | },
126 | {
127 | "_id": "575e76e8395e74230d46b354",
128 | "active": false,
129 | "balance": 2863.92,
130 | "age": 32,
131 | "name": "Dorthy Cohen",
132 | "gender": "female",
133 | "tags": [
134 | "eu",
135 | "ipsum",
136 | "fugiat",
137 | "reprehenderit",
138 | "occaecat",
139 | "velit",
140 | "nostrud"
141 | ],
142 | "friends": [
143 | {
144 | "id": 0,
145 | "name": "Valarie Burt"
146 | },
147 | {
148 | "id": 1,
149 | "name": "Dana Shepherd"
150 | },
151 | {
152 | "id": 2,
153 | "name": "Shepherd Mcneil"
154 | }
155 | ]
156 | },
157 | {
158 | "_id": "575e76e84a60553b98f42544",
159 | "active": true,
160 | "balance": 1250.94,
161 | "age": 30,
162 | "name": "Lowery Sheppard",
163 | "gender": "male",
164 | "tags": [
165 | "ea",
166 | "occaecat",
167 | "eiusmod",
168 | "elit",
169 | "minim",
170 | "non",
171 | "proident"
172 | ],
173 | "friends": [
174 | {
175 | "id": 0,
176 | "name": "Holcomb Mcfarland"
177 | },
178 | {
179 | "id": 1,
180 | "name": "Cora Marshall"
181 | },
182 | {
183 | "id": 2,
184 | "name": "Stuart Powell"
185 | }
186 | ]
187 | },
188 | {
189 | "_id": "575e76e8b91bdf438beaec9d",
190 | "active": false,
191 | "balance": 2981.59,
192 | "age": 24,
193 | "name": "Cortez Martinez",
194 | "gender": "male",
195 | "tags": [
196 | "veniam",
197 | "qui",
198 | "pariatur",
199 | "sint",
200 | "pariatur",
201 | "ex",
202 | "et"
203 | ],
204 | "friends": [
205 | {
206 | "id": 0,
207 | "name": "Orr Doyle"
208 | },
209 | {
210 | "id": 1,
211 | "name": "Kristine York"
212 | },
213 | {
214 | "id": 2,
215 | "name": "Cotton Perez"
216 | }
217 | ]
218 | },
219 | {
220 | "_id": "575e76e8a70ce7bc09c4031a",
221 | "active": true,
222 | "balance": 3998.48,
223 | "age": 23,
224 | "name": "Yang Mcdaniel",
225 | "gender": "male",
226 | "tags": [
227 | "exercitation",
228 | "adipisicing",
229 | "id",
230 | "cupidatat",
231 | "cillum",
232 | "quis",
233 | "sunt"
234 | ],
235 | "friends": [
236 | {
237 | "id": 0,
238 | "name": "Suzanne Lindsay"
239 | },
240 | {
241 | "id": 1,
242 | "name": "Lorraine Greene"
243 | },
244 | {
245 | "id": 2,
246 | "name": "Marci Mayer"
247 | }
248 | ]
249 | },
250 | {
251 | "_id": "575e76e8634ee721c2f7eb6b",
252 | "active": false,
253 | "balance": 2192.61,
254 | "age": 21,
255 | "name": "Gina Barton",
256 | "gender": "female",
257 | "tags": [
258 | "minim",
259 | "non",
260 | "ipsum",
261 | "laboris",
262 | "commodo",
263 | "nostrud",
264 | "ad"
265 | ],
266 | "friends": [
267 | {
268 | "id": 0,
269 | "name": "Padilla Craig"
270 | },
271 | {
272 | "id": 1,
273 | "name": "Hamilton Hernandez"
274 | },
275 | {
276 | "id": 2,
277 | "name": "Stewart Spears"
278 | }
279 | ]
280 | },
281 | {
282 | "_id": "575e76e8d06206ce3a0c2a9c",
283 | "active": false,
284 | "balance": 1754.14,
285 | "age": 39,
286 | "name": "Rivas Marsh",
287 | "gender": "male",
288 | "tags": [
289 | "commodo",
290 | "eiusmod",
291 | "reprehenderit",
292 | "ad",
293 | "excepteur",
294 | "esse",
295 | "sint"
296 | ],
297 | "friends": [
298 | {
299 | "id": 0,
300 | "name": "Barron Palmer"
301 | },
302 | {
303 | "id": 1,
304 | "name": "Lisa Shepard"
305 | },
306 | {
307 | "id": 2,
308 | "name": "Guthrie Horne"
309 | }
310 | ]
311 | },
312 | {
313 | "_id": "575e76e8f8df79a2cb49ff49",
314 | "active": false,
315 | "balance": 2926.84,
316 | "age": 32,
317 | "name": "Flynn Gonzalez",
318 | "gender": "male",
319 | "tags": [
320 | "labore",
321 | "excepteur",
322 | "sunt",
323 | "sit",
324 | "esse",
325 | "irure",
326 | "ad"
327 | ],
328 | "friends": [
329 | {
330 | "id": 0,
331 | "name": "Shelby Mendoza"
332 | },
333 | {
334 | "id": 1,
335 | "name": "Horton Dawson"
336 | },
337 | {
338 | "id": 2,
339 | "name": "Casey Head"
340 | }
341 | ]
342 | }
343 | ]
--------------------------------------------------------------------------------