├── logseqTestGraph.zip ├── style.css ├── README.md ├── package.json ├── index.html ├── index.test.js └── index.js /logseqTestGraph.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adxsoft/logseqadvancedquerybuilder/HEAD/logseqTestGraph.zip -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* theme colors */ 2 | 3 | :root { 4 | 5 | 6 | /* Dark theme */ 7 | --dark-bg: #00384d; 8 | --dark-fg: #b0e1f4c3; 9 | 10 | /* Light theme */ 11 | --light-bg: #d1f0f995; 12 | --light-fg: rgb(59, 44, 44); 13 | 14 | /* Defaults 15 | 16 | For a dark theme use these lines 17 | --current-bg: var(--dark-bg); 18 | --current-fg: var(--dark-fg); 19 | 20 | For a light theme use these lines 21 | --current-bg: var(--light-bg); 22 | --current-fg: var(--light-fg); 23 | 24 | */ 25 | 26 | --current-bg: var(--light-bg); 27 | --current-fg: var(--light-fg); 28 | } 29 | 30 | 31 | /* clear all padding, margins */ 32 | * { 33 | padding: 0; 34 | margin: 0; 35 | box-sizing: border-box; 36 | } 37 | 38 | html { 39 | font-size: 14px; 40 | } 41 | 42 | body { 43 | padding: 1rem; 44 | font-family: 'Roboto', sans-serif; 45 | background-color: var(--current-bg); 46 | color: var(--current-fg); 47 | } 48 | 49 | a { 50 | background-color: var(--current-bg); 51 | color: var(--current-fg); 52 | 53 | } 54 | 55 | 56 | .title { 57 | margin-bottom: 2rem; 58 | font-size: 2rem; 59 | } 60 | 61 | h3 { 62 | font-family: 'Roboto', sans-serif; 63 | font-size: 1.1rem; 64 | color: var(--current-fg); 65 | } 66 | 67 | h5 { 68 | font-family: 'Roboto', sans-serif; 69 | font-size: 0.9rem; 70 | color: var(--current-fg); 71 | } 72 | 73 | 74 | hr { 75 | margin: 1rem 0; 76 | } 77 | 78 | /* Details/Summary Indentation levels */ 79 | 80 | .level1 { 81 | /* list-style: disc; */ 82 | background-color: var(--current-bg); 83 | color: var(--current-fg); 84 | padding: rem; 85 | font-size: 1rem; 86 | cursor: pointer; 87 | } 88 | 89 | .level2 { 90 | /* list-style: disc; */ 91 | background-color: var(--current-bg); 92 | color: var(--current-fg); 93 | padding: rem; 94 | font-size: 1rem; 95 | cursor: pointer; 96 | margin-left: 1rem; 97 | } 98 | 99 | .level3 { 100 | /* list-style: disc; */ 101 | background-color: var(--current-bg); 102 | color: var(--current-fg); 103 | padding: rem; 104 | font-size: 2rem; 105 | cursor: pointer; 106 | margin-left: 1rem; 107 | } 108 | 109 | .details-content { 110 | background-color: var(--current-bg); 111 | color: var(--current-fg); 112 | padding: 1rem; 113 | font-size: 0.8rem; 114 | overflow-wrap: break-word; 115 | cursor: pointer; 116 | } 117 | 118 | button { 119 | font-size: 0.9rem; 120 | padding: 3px 3px; 121 | background-color: rgb(215, 210, 206); 122 | border-radius: 6px; 123 | } 124 | 125 | select { 126 | font-size: 0.8rem; 127 | padding: 1px 1px; 128 | width: 25rem; 129 | background-color: #00384d; 130 | color: white; 131 | text-align: center; 132 | } 133 | 134 | textarea { 135 | color: rgb(7, 7, 0); 136 | width: 25rem; 137 | background-color: rgb(243, 239, 136); 138 | font-size: 0.8rem; 139 | font-weight: bold; 140 | font-family: 'Courier New', Courier, monospace; 141 | padding: 1px 1px; 142 | text-align: left; 143 | } 144 | 145 | .generatedquery { 146 | color: black; 147 | width: 25rem; 148 | background-color: rgb(203, 241, 165); 149 | font-size: 0.8rem; 150 | font-weight: bold; 151 | font-family: 'Courier New', Courier, monospace; 152 | text-align: left; 153 | } 154 | 155 | 156 | 157 | .checkbox { 158 | font-size: 0.8rem; 159 | } 160 | 161 | .statusbar { 162 | font-size: 0.9rem; 163 | padding: 4px 1px; 164 | background-color: var(--current-bg); 165 | color: var(--current-fg); 166 | 167 | text-align: left; 168 | border: 1; 169 | 170 | } 171 | 172 | blockquote { 173 | background-color: var(--current-bg); 174 | color: var(--current-fg); 175 | text-align: left; 176 | } 177 | 178 | table { 179 | background-color: var(--current-bg); 180 | color: var(--current-fg); 181 | border-collapse: collapse; 182 | border: 1px solid var(--current-fg); 183 | } 184 | 185 | th { 186 | padding: 2px; 187 | font-size: 1.8rem; 188 | border: 1px solid var(--current-fg); 189 | } 190 | 191 | td { 192 | padding: 2px; 193 | font-size: 0.8rem; 194 | border: 1px solid var(--current-fg); 195 | } 196 | 197 | /* zebra striping tbody tr:nth-child(odd) { 198 | background-color: var(--current-bg); 199 | color: var(--current-fg); 200 | 201 | } 202 | 203 | tbody tr:nth-child(even) { 204 | background-color: var(--current-bg); 205 | color: var(--current-fg); 206 | } */ 207 | 208 | ul { 209 | list-style-type: disc !important; 210 | padding-left: 1em; 211 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # logseqadvancedquerybuilder 2 | 3 | An Experimental online tool to help Logseq Users build advanced queries from simple commands. 4 | 5 | **News Flash 3rd March 2023** 6 | Logseq plugin (**logseq-query-builder-plugin**) now officially in the Logseq Marketplace 7 | 8 | **News Flash 30th Dec 2022** 9 | **Logseq plugin with same functionality now developed.** You can get it [here](https://github.com/adxsoft/logseq-query-builder-plugin) and see it in action [here](https://youtu.be/EA2jLSQ_WMA) 10 | 11 | **Updated 14th Dec 2022 to v0.4 see Releases section below** 12 | **SITE HAS BEEN COMPLETELY REWRITTEN IN JAVASCRIPT FOR STABILITY** 13 | 14 | You can run this tool online at https://adxsoft.github.io/logseqadvancedquerybuilder/ 15 | 16 | To include this tool in a logseq page use this line 17 | ```html 18 | 19 | ``` 20 | 21 | See the FAQ for instructions and examples of how to use the tool 22 | 23 | Also see https://github.com/adxsoft/buildlogseqtestgraph for building a test graph to checkout the advanced queries you build with this tool 24 | 25 | ## Technical information 26 | The tool is built in javascript. 27 | 28 | To test locally set the mode variable to 'local'. Once this is set you can use Jest Testing Library with the included _index.tests.js_ file. 29 | 30 | To deploy to the web set the mode to 'website' 31 | 32 | 2. To input a set of simple commands to generate an advanced query, add the following code at the end of logseqquerybuilder.py 33 | 34 | ```python 35 | testQueryBuild("""yourcommands""") 36 | ``` 37 | 38 | Where yourcommands could be for example 39 | 40 | ```python 41 | testQueryBuild("""title: blocktags and pages command combined - test 3 42 | - pages 43 | - test* 44 | - *2 45 | - not *age1 46 | - blocktags 47 | - tag1 48 | - not tag2 49 | - tag3 50 | - not tag4 51 | """) 52 | ``` 53 | which will print the following advanced query that you can copy and paste (using Cmd/Ctrl Shift V) directly into Logseq 54 | 55 | ```clojure 56 | #+BEGIN_QUERY 57 | { 58 | :title [:b "blocktags and pages command combined - test 3"] 59 | :query [:find (pull ?block [*]) 60 | :where 61 | [?block :block/content ?blockcontent] 62 | [?block :block/page ?page] 63 | [?page :block/name ?pagename] 64 | ( or 65 | [(clojure.string/starts-with? ?pagename "test")] 66 | [(clojure.string/ends-with? ?pagename "2")] 67 | ) 68 | (not [(clojure.string/ends-with? ?pagename "age1")]) 69 | ( or 70 | (page-ref ?block "tag1") 71 | (page-ref ?block "tag3") 72 | ) 73 | (not (page-ref ?block "tag2")) 74 | (not (page-ref ?block "tag4")) 75 | ] 76 | } 77 | #+END_QUERY 78 | ``` 79 | 3. To run the unit tests you must do step 1 above and then run *test.py* 80 | 81 | # Releases 82 | _Version 0.1_ 83 | - Original release Oct 2022 84 | 85 | 86 | _Version 0.2_ 87 | - clarify pages retrieval vs blocks retrieval in FAQ, Simple Commands 88 | - Added 'and' and 'or' keywords in arguments for a command, For example can now say 89 | ``` 90 | - tagA 91 | - or tagA 92 | ``` 93 | and also 94 | ``` 95 | - property category, "fiction" 96 | - and property category, "western" 97 | ``` 98 | - Added a logseq test graph download button to help user test advanced queries 99 | - Force user to choose either pages or block retrieval, Default to block retrieval 100 | - improved error messaging 101 | - improved descriptions for the generated advanced query lines 102 | - bug fixes 103 | 104 | _Version 0.3_ Dec 8th 2022 105 | - added the pagelinks command which will select blocks that have links to specific pages or journals 106 | 107 | Example: To select blocks referring to page1 OR page 2 108 | ``` 109 | - blocks 110 | - * 111 | - pagelinks 112 | - page1 113 | - page2 114 | ``` 115 | Example: To select blocks referring to page1 AND page 2 116 | ``` 117 | - blocks 118 | - * 119 | - pagelinks 120 | - page1 121 | - and page2 122 | ``` 123 | Example: To select blocks referring to Xmas day journal 124 | (assumes your date format is the default format) 125 | ``` 126 | - blocks 127 | - * 128 | - pagelinks 129 | - Dec 25th, 2022 130 | ``` 131 | If you use a different date format use that in the journal reference 132 | 133 | _Version 0.4_ Dec 14th 2022 134 | Redeveloped in Javascript for speed and stability 135 | - pyscript is still in alpha and early days so changes are happening all the time 136 | - javascript is stable 137 | - code base has been changed so that the core functions can operate in one of three ways 138 | (mode variable controls which operation is chosen _website_,_local_,_logseq-plugin_ 139 | - as this website, 140 | - locally for testing 141 | - as a logseq plugin (distributed to a separate repository) 142 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logseq-advanced-query-builder", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "functions.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "dependencies": { 10 | "abortcontroller-polyfill": "^1.7.3", 11 | "acorn": "^8.8.0", 12 | "ansi-styles": "^4.3.0", 13 | "asn1.js": "^5.4.1", 14 | "assert": "^2.0.0", 15 | "available-typed-arrays": "^1.0.5", 16 | "base-x": "^3.0.9", 17 | "base64-js": "^1.5.1", 18 | "bn.js": "^4.12.0", 19 | "boolbase": "^1.0.0", 20 | "brorand": "^1.1.0", 21 | "browserify-aes": "^1.2.0", 22 | "browserify-cipher": "^1.0.1", 23 | "browserify-des": "^1.0.2", 24 | "browserify-rsa": "^4.1.0", 25 | "browserify-sign": "^4.2.1", 26 | "browserslist": "^4.21.4", 27 | "buffer": "^6.0.3", 28 | "buffer-from": "^1.1.2", 29 | "buffer-xor": "^1.0.3", 30 | "builtin-status-codes": "^3.0.0", 31 | "call-bind": "^1.0.2", 32 | "callsites": "^3.1.0", 33 | "caniuse-lite": "^1.0.30001418", 34 | "chalk": "^4.1.2", 35 | "chrome-trace-event": "^1.0.3", 36 | "cipher-base": "^1.0.4", 37 | "clone": "^2.1.2", 38 | "color-convert": "^2.0.1", 39 | "color-name": "^1.1.4", 40 | "commander": "^7.2.0", 41 | "cosmiconfig": "^7.0.1", 42 | "create-ecdh": "^4.0.4", 43 | "create-hash": "^1.2.0", 44 | "create-hmac": "^1.1.7", 45 | "crypto-browserify": "^3.12.0", 46 | "css-select": "^4.3.0", 47 | "css-tree": "^1.1.3", 48 | "css-what": "^6.1.0", 49 | "csso": "^4.2.0", 50 | "csstype": "^3.0.8", 51 | "debug": "^4.3.4", 52 | "define-properties": "^1.1.4", 53 | "des.js": "^1.0.1", 54 | "detect-libc": "^1.0.3", 55 | "diffie-hellman": "^5.0.3", 56 | "dom-serializer": "^1.4.1", 57 | "domelementtype": "^2.3.0", 58 | "domhandler": "^4.3.1", 59 | "dompurify": "^2.3.1", 60 | "domutils": "^2.8.0", 61 | "dot-case": "^3.0.4", 62 | "dotenv": "^7.0.0", 63 | "dotenv-expand": "^5.1.0", 64 | "electron-to-chromium": "^1.4.275", 65 | "elliptic": "^6.5.4", 66 | "engine.io-client": "^6.2.2", 67 | "engine.io-parser": "^5.0.4", 68 | "entities": "^3.0.1", 69 | "error-ex": "^1.3.2", 70 | "es-abstract": "^1.20.4", 71 | "es-to-primitive": "^1.2.1", 72 | "es6-object-assign": "^1.1.0", 73 | "escalade": "^3.1.1", 74 | "escape-string-regexp": "^1.0.5", 75 | "eventemitter3": "^4.0.7", 76 | "events": "^3.3.0", 77 | "evp_bytestokey": "^1.0.3", 78 | "fast-deep-equal": "^3.1.3", 79 | "for-each": "^0.3.3", 80 | "function-bind": "^1.1.1", 81 | "function.prototype.name": "^1.1.5", 82 | "functions-have-names": "^1.2.3", 83 | "get-intrinsic": "^1.1.3", 84 | "get-port": "^4.2.0", 85 | "get-symbol-description": "^1.0.0", 86 | "globals": "^13.17.0", 87 | "has": "^1.0.3", 88 | "has-bigints": "^1.0.2", 89 | "has-flag": "^4.0.0", 90 | "has-property-descriptors": "^1.0.0", 91 | "has-symbols": "^1.0.3", 92 | "has-tostringtag": "^1.0.0", 93 | "hash-base": "^3.1.0", 94 | "hash.js": "^1.1.7", 95 | "hmac-drbg": "^1.0.1", 96 | "htmlnano": "^2.0.2", 97 | "htmlparser2": "^7.2.0", 98 | "https-browserify": "^1.0.0", 99 | "ieee754": "^1.2.1", 100 | "import-fresh": "^3.3.0", 101 | "inherits": "^2.0.4", 102 | "internal-slot": "^1.0.3", 103 | "is-arguments": "^1.1.1", 104 | "is-arrayish": "^0.2.1", 105 | "is-bigint": "^1.0.4", 106 | "is-boolean-object": "^1.1.2", 107 | "is-callable": "^1.2.7", 108 | "is-date-object": "^1.0.5", 109 | "is-generator-function": "^1.0.10", 110 | "is-json": "^2.0.1", 111 | "is-nan": "^1.3.2", 112 | "is-negative-zero": "^2.0.2", 113 | "is-number-object": "^1.0.7", 114 | "is-regex": "^1.1.4", 115 | "is-shared-array-buffer": "^1.0.2", 116 | "is-string": "^1.0.7", 117 | "is-symbol": "^1.0.4", 118 | "is-typed-array": "^1.1.9", 119 | "is-weakref": "^1.0.2", 120 | "js-tokens": "^4.0.0", 121 | "json-parse-even-better-errors": "^2.3.1", 122 | "json5": "^2.2.1", 123 | "lightningcss": "^1.16.0", 124 | "lightningcss-darwin-x64": "^1.16.0", 125 | "lines-and-columns": "^1.2.4", 126 | "lmdb": "^2.5.2", 127 | "lodash-es": "^4.17.21", 128 | "lower-case": "^2.0.2", 129 | "md5.js": "^1.3.5", 130 | "mdn-data": "^2.0.14", 131 | "miller-rabin": "^4.0.1", 132 | "minimalistic-assert": "^1.0.1", 133 | "minimalistic-crypto-utils": "^1.0.1", 134 | "ms": "^2.1.2", 135 | "msgpackr": "^1.7.2", 136 | "msgpackr-extract": "^2.1.2", 137 | "no-case": "^3.0.4", 138 | "node-addon-api": "^3.2.1", 139 | "node-gyp-build": "^4.5.0", 140 | "node-gyp-build-optional-packages": "^5.0.3", 141 | "node-releases": "^2.0.6", 142 | "nth-check": "^2.1.1", 143 | "nullthrows": "^1.1.1", 144 | "object-inspect": "^1.12.2", 145 | "object-is": "^1.1.5", 146 | "object-keys": "^1.1.1", 147 | "object.assign": "^4.1.4", 148 | "ordered-binary": "^1.4.0", 149 | "parcel": "^2.7.0", 150 | "parent-module": "^1.0.1", 151 | "parse-asn1": "^5.1.6", 152 | "parse-json": "^5.2.0", 153 | "path": "^0.12.7", 154 | "path-type": "^4.0.0", 155 | "pbkdf2": "^3.1.2", 156 | "picocolors": "^1.0.0", 157 | "postcss-value-parser": "^4.2.0", 158 | "posthtml": "^0.16.6", 159 | "posthtml-parser": "^0.10.2", 160 | "posthtml-render": "^3.0.0", 161 | "process": "^0.11.10", 162 | "public-encrypt": "^4.0.3", 163 | "punycode": "^1.3.2", 164 | "querystring": "^0.2.0", 165 | "querystring-es3": "^0.2.1", 166 | "randombytes": "^2.1.0", 167 | "randomfill": "^1.0.4", 168 | "react-error-overlay": "^6.0.9", 169 | "react-refresh": "^0.9.0", 170 | "readable-stream": "^3.6.0", 171 | "regenerator-runtime": "^0.13.9", 172 | "regexp.prototype.flags": "^1.4.3", 173 | "resolve-from": "^4.0.0", 174 | "ripemd160": "^2.0.2", 175 | "safe-buffer": "^5.2.1", 176 | "safe-regex-test": "^1.0.0", 177 | "safer-buffer": "^2.1.2", 178 | "semver": "^5.7.1", 179 | "sha.js": "^2.4.11", 180 | "side-channel": "^1.0.4", 181 | "snake-case": "^3.0.4", 182 | "socket.io-client": "^4.5.2", 183 | "socket.io-parser": "^4.2.1", 184 | "source-map": "^0.6.1", 185 | "source-map-support": "^0.5.21", 186 | "stable": "^0.1.8", 187 | "stream-browserify": "^3.0.0", 188 | "stream-http": "^3.2.0", 189 | "string_decoder": "^1.3.0", 190 | "string.prototype.trimend": "^1.0.5", 191 | "string.prototype.trimstart": "^1.0.5", 192 | "supports-color": "^7.2.0", 193 | "svgo": "^2.8.0", 194 | "term-size": "^2.2.1", 195 | "terser": "^5.15.1", 196 | "timsort": "^0.3.0", 197 | "tslib": "^2.4.0", 198 | "type-fest": "^0.20.2", 199 | "unbox-primitive": "^1.0.2", 200 | "update-browserslist-db": "^1.0.10", 201 | "url": "^0.11.0", 202 | "util": "^0.12.4", 203 | "util-deprecate": "^1.0.2", 204 | "utility-types": "^3.10.0", 205 | "v8-compile-cache": "^2.3.0", 206 | "weak-lru-cache": "^1.2.2", 207 | "which-boxed-primitive": "^1.0.2", 208 | "which-typed-array": "^1.1.8", 209 | "ws": "^8.2.3", 210 | "xmlhttprequest-ssl": "^2.0.0", 211 | "xtend": "^4.0.2", 212 | "xxhash-wasm": "^0.4.2", 213 | "yaml": "^1.10.2" 214 | }, 215 | "devDependencies": { 216 | "jest": "^29.3.1" 217 | }, 218 | "scripts": { 219 | "test": "jest" 220 | }, 221 | "keywords": [], 222 | "author": "", 223 | "license": "ISC" 224 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Logseq Advanced Query Builder

5 | v0.5 Javascript Version 6 | 7 | 8 | Download a test graph 9 | 10 |

11 | 12 | 13 | 14 | 15 | 16 | 17 | Logseq Query Builder 18 | 19 | 20 | 21 |

22 |
23 | An Experimental online tool to help Logseq Users build advanced queries from simple commands. 24 | 25 |
26 |
27 |
28 | FAQ 29 |
30 | How to use 31 |
32 | The simple commands are entered as a text outline in the Command Input area
33 |
34 |
 35 | - commandname
 36 |     - argument
 37 |     - argument
38 | Optionally arguments can begin with any of the words and,or,and, 39 |
40 |
41 |
    42 |
  • Enter commands in the yellow area below or choose an example.
  • 43 |
  • Tick Include Query Comments to show query line comments
  • 44 |
  • Tick Copy as code block to wrap query in a Logseq code block
  • 45 |
  • Click the Generate Advanced Query button
  • 46 | to build the query. 47 |
      48 |
    • Optionally you can include comments for each query line. 49 |
    50 |
  • Copy the generated advanced query to the clipboard>
  • 51 |
      52 |
    • (if necessary scroll down to see the result) 53 | 54 | 55 |
    • Paste into Logseq using Cmd(or Ctrl) Shift V

    • 56 |
    57 |
58 |
59 |
60 | Why did I build this tool 61 |
62 | The reason I created this query builder was for the following reasons 63 |
    64 |
  • Advanced Queries have a complicated syntax that causes errors eg. missing brackets
  • 65 |
  • For non developers they can build advanced queries and avoid having to learn programming in clojure and datalog
  • 66 |
  • Logseq users can learn by using the examples to generate queries with detailed comments
  • 67 |
  • As the generator is web based it can be accessed from desktop or mobile devices as generated queries are always copied to the clipboard
  • 68 |
69 |
70 |
71 |
72 | Simple Command List 73 |
74 |
75 | Queries filter in two ways - pages or blocks

76 | pages command retrieves the special blocks that have ONLY the page information
such as name, page tags, page properties
77 | - these page blocks are placed into the ?block variable

78 | blocks command retrieves every single block in the graph including the special page blocks
79 | - these page blocks are placed into the ?block variable and the page this block belongs to is placed in the ?page variable

80 | You must choose a pages command OR a blocks command (you cannot use noth together) 81 |
82 |
83 | 84 | Note. Wildcards can be full name or partial name using *
85 | test* - starts with text 'test'
86 | *end - ends with text 'end'
87 | *tax* - contains text 'tax'
88 |
89 |
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
blocksselect logseq blocks by wildcards
blockpropertiesselect blocks by property values
blocktagsselect blocks by tag
deadlineselect pages or blocks that have a deadline
deadlinebetweenselect pages or blocks that have a deadline in a date range
journalsbetweenonly select journal pages in a date range
journalonlyonly select journal pages
namespaceselect pages or blocks within a namespace
pagesselect pages by wildcards
pagepropertiesselect pages by page properties
pagetagsselect pages by tag
pagelinksselect blocks that have links to pages
- note. Journal page link is your chosen format in your settings. For example Dec 25th, 2022
tasksselect tasks
scheduledselect pages or blocks that are scheduled
scheduledbetweenselect pages or blocks that are scheduled in a date range
106 |
107 |
108 |
109 | About 110 |
111 | This web site is hosted on github pages
112 | source code is available here 113 |
114 |
115 |
116 | Release History 117 |
118 | v0.1
119 |
    120 |
  • Original release
  • 121 |
122 | v0.2
123 |
    124 |
  • clarify pages retrieval vs blocks retrieval in FAQ, Simple Commands
  • 125 |
  • Added 'and' and 'or' keywords in arguments for a command, For example
    126 | can now say
    127 | - tagA
    128 | - or tagA
    129 | and also
    130 | - property category, "fiction"
    131 | - and property category, "western"
    132 |
  • 133 |
  • Added a logseq test graph for download to help user test advanced queries
  • 134 |
  • Force user to choose either pages or block retrieval, Default to block retrieval
  • 135 |
  • improved error messaging
  • 136 |
  • improved descriptions for the generated advanced query lines
  • 137 |
  • bug fixes
  • 138 |
139 | v0.3
140 |
    141 |
  • Added pagelinks command to select blocks with specific pagelinks
  • 142 |
143 | v0.4
144 |
    145 |
  • Conversion from pyscript to javascript
  • 146 |
147 |
148 |
149 |
150 | Licence 151 |
152 | This tool is free to use by anyone. I built it for my own educational purposes to learn advanced queries and I'm still a novice so the generated queries reflect my current understanding of advanced queries that I have been able to test on Logseq 0.8.2 in October 2022
153 | The advanced queries generated are my interpretation of advanced queries from researching the logseq discord forum and links on the logseq site.
154 | I have not yet included the result-transform or view sections of advanced queries as I am still trying to understand them sufficiently to generate meaningful queries. 155 |
156 |
DISCLAIMER
157 |
158 | logseqquerybuilder is distributed under GNU General Public License v3.
159 |
160 | This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to
161 | redistribute it under certain conditions.
162 |
163 |
LICENSE
164 |
165 | This program is free software: you can redistribute it and/or modify
166 | it under the terms of the GNU General Public License as published by
167 | the Free Software Foundation, either version 3 of the License, or
168 | (at your option) any later version.
169 |
170 | This program is distributed in the hope that it will be useful,
171 | but WITHOUT ANY WARRANTY; without even the implied warranty of
172 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
173 |
174 | Please see http://www.gnu.org/licenses/ http://www.gnu.org/licenses/ for details.
175 |
176 |
WARRANTY
177 |
178 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
179 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
180 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY
181 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
182 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
183 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
184 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
185 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
186 |
187 |
LIABILITY
188 |
189 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
190 | ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE
191 | PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
192 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
193 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
194 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
195 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
196 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
197 | SUCH DAMAGES. 198 |
199 |
200 |
201 |
202 | 203 |

Command Input

204 | 236 |
237 |

238 |


240 |             
241 |

242 | 243 |


244 |

Generated Query

245 |
246 | Include Query Comments 247 | Copy as code block

248 |

249 | 250 | 251 |

252 |

253 |
254 |
255 |

256 |


257 |
Status
258 | 260 | 261 | 262 | 263 | 264 | 265 | 303 | 336 | 337 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | const functions = require('./index'); 2 | const fs = require('fs'); 3 | 4 | //------------------------------------------------------- 5 | // local functions to assist testing 6 | //------------------------------------------------------- 7 | 8 | function saveAutomatedTestOutput(msg) { 9 | fs.writeFile('testing_results.txt', msg, function (err) { 10 | if (err) 11 | return console.log(err); 12 | console.log('Test Results written to testing_results.txt'); 13 | }); 14 | } 15 | 16 | function setGlobals({ 17 | querygroup = 'blocks-querylines', 18 | showcommandcomments = false, 19 | codeblock = false } = {}) { 20 | functions.setquerygroup(querygroup) 21 | functions.setshowcommandcomments(showcommandcomments) 22 | functions.setcodeblock(codeblock) 23 | } 24 | 25 | function resetGlobalsToDefault() { 26 | setGlobals({ 27 | querygroup: 'blocks-querylines', 28 | showcommandcoments: false, 29 | codeblock: false 30 | }) 31 | } 32 | 33 | function gettestcommands(testno) { 34 | testcases = functions.getquerytestcases() 35 | var testdata = testcases[testno].split("#+BEGIN") 36 | return testdata[0].trim() 37 | } 38 | 39 | 40 | function gettestexpectedresults(testno) { 41 | testcases = functions.getquerytestcases() 42 | var testdata = testcases[testno].split("#+BEGIN") 43 | var expectedresult = "#+BEGIN" + testdata[1] 44 | returnval = expectedresult 45 | return returnval 46 | } 47 | 48 | 49 | //------------------------------------------------------- 50 | // test any old function 51 | //------------------------------------------------------- 52 | 53 | 54 | describe('junk tests', () => { 55 | test('add 2 numbers', () => { 56 | returnval = functions.add(1, 1) 57 | expect(returnval).toBe(2) 58 | }) 59 | }) // end describe 60 | 61 | describe('Manual tests', () => { 62 | // ------------------------------------ 63 | 64 | 65 | // ------------------------------------ 66 | 67 | // ------------------------------------ 68 | // ------------------------------------ 69 | 70 | 71 | // ------------------------------------ 72 | 73 | 74 | 75 | // ------------------------------------ 76 | test('pages test 1', () => { 77 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 78 | setGlobals() 79 | commands = `title: pages command - select all pages 80 | - pages 81 | - * 82 | ` 83 | expectedresults = `#+BEGIN_QUERY 84 | { 85 | :title [:b "pages command - select all pages"] 86 | :query [:find (pull ?block [*]) 87 | :where 88 | [?block :block/name ?pagename] 89 | ] 90 | } 91 | #+END_QUERY 92 | ` 93 | functions.initialiseQuery() 94 | functions.processCommandList(commands) 95 | advancedquery = functions.constructQuery() 96 | resetGlobalsToDefault() 97 | expect(advancedquery).toEqual(expectedresults) 98 | }) 99 | 100 | // ------------------------------------ 101 | test('pages - test 2', () => { 102 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 103 | setGlobals() 104 | commands = `title: pages command - pages by wildcards 105 | - pages 106 | - abc* 107 | - *de* 108 | - not 2* 109 | ` 110 | expectedresults = `#+BEGIN_QUERY 111 | { 112 | :title [:b "pages command - pages by wildcards"] 113 | :query [:find (pull ?block [*]) 114 | :where 115 | [?block :block/name ?pagename] 116 | ( or 117 | [(clojure.string/starts-with? ?pagename "abc")] 118 | [(clojure.string/includes? ?pagename "de")] 119 | ) 120 | (not [(clojure.string/starts-with? ?pagename "2")]) 121 | ] 122 | } 123 | #+END_QUERY 124 | ` 125 | functions.initialiseQuery() 126 | functions.processCommandList(commands) 127 | advancedquery = functions.constructQuery() 128 | resetGlobalsToDefault() 129 | expect(advancedquery).toEqual(expectedresults) 130 | }) 131 | 132 | 133 | // ------------------------------------ 134 | test('pages - test 3', () => { 135 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 136 | setGlobals() 137 | commands = `title: pages - wildcards but exclude specific page 138 | - pages 139 | - testpage* 140 | - not testpage3 141 | ` 142 | expectedresults = `#+BEGIN_QUERY 143 | { 144 | :title [:b "pages - wildcards but exclude specific page"] 145 | :query [:find (pull ?block [*]) 146 | :where 147 | [?block :block/name ?pagename] 148 | [(clojure.string/starts-with? ?pagename "testpage")] 149 | (not [?block :block/name "testpage3"]) 150 | ] 151 | } 152 | #+END_QUERY 153 | ` 154 | functions.initialiseQuery() 155 | functions.processCommandList(commands) 156 | advancedquery = functions.constructQuery() 157 | resetGlobalsToDefault() 158 | expect(advancedquery).toEqual(expectedresults) 159 | }) 160 | 161 | // ------------------------------------ 162 | test('blocks - test 1', () => { 163 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 164 | setGlobals() 165 | commands = `title: blocks command - blocks by wildcards 166 | - blocks 167 | - testblock* 168 | - *123* 169 | - 6* 170 | - not *456* 171 | ` 172 | expectedresults = `#+BEGIN_QUERY 173 | { 174 | :title [:b "blocks command - blocks by wildcards"] 175 | :query [:find (pull ?block [*]) 176 | :where 177 | [?block :block/content ?blockcontent] 178 | [?block :block/page ?page] 179 | [?page :block/name ?pagename] 180 | ( or 181 | [(clojure.string/ends-with? ?blockcontent "testblock")] 182 | [(clojure.string/includes? ?blockcontent "123")] 183 | [(clojure.string/ends-with? ?blockcontent "6")] 184 | ) 185 | (not [(clojure.string/includes? ?blockcontent "456")]) 186 | ] 187 | } 188 | #+END_QUERY 189 | ` 190 | functions.initialiseQuery() 191 | functions.processCommandList(commands) 192 | advancedquery = functions.constructQuery() 193 | resetGlobalsToDefault() 194 | expect(advancedquery).toEqual(expectedresults) 195 | }) 196 | 197 | // ------------------------------------ 198 | 199 | 200 | 201 | // ------------------------------------ 202 | 203 | 204 | // ------------------------------------ 205 | 206 | 207 | // ------------------------------------ 208 | test('blocktags - test 4', () => { 209 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 210 | setGlobals() 211 | commands = `title: multiple blocks tags 212 | - blocks 213 | - * 214 | - blocktags 215 | - tag1 216 | - tag2 217 | - tag3 218 | - not tag2 219 | ` 220 | expectedresults = `#+BEGIN_QUERY 221 | { 222 | :title [:b "multiple blocks tags"] 223 | :query [:find (pull ?block [*]) 224 | :where 225 | [?block :block/content ?blockcontent] 226 | [?block :block/page ?page] 227 | [?page :block/name ?pagename] 228 | ( or 229 | (page-ref ?block "tag1") 230 | (page-ref ?block "tag2") 231 | (page-ref ?block "tag3") 232 | ) 233 | (not (page-ref ?block "tag2")) 234 | ] 235 | } 236 | #+END_QUERY 237 | ` 238 | functions.initialiseQuery() 239 | functions.processCommandList(commands) 240 | advancedquery = functions.constructQuery() 241 | resetGlobalsToDefault() 242 | expect(advancedquery).toEqual(expectedresults) 243 | }) 244 | 245 | // ------------------------------------ 246 | 247 | 248 | // ------------------------------------ 249 | test('pagestags - test 2', () => { 250 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 251 | setGlobals() 252 | commands = `title: pagetags and pages combined - test 1 253 | - pages 254 | - not age7* 255 | - pagetags 256 | - pagetag1 257 | - pagetag2 258 | - not pagetag4 259 | ` 260 | expectedresults = `#+BEGIN_QUERY 261 | { 262 | :title [:b "pagetags and pages combined - test 1"] 263 | :query [:find (pull ?block [*]) 264 | :where 265 | [?block :block/name ?pagename] 266 | (not [(clojure.string/starts-with? ?pagename "age7")]) 267 | [?block :block/journal? false] 268 | ( or 269 | (page-tags ?block #{"pagetag1"}) 270 | (page-tags ?block #{"pagetag2"}) 271 | ) 272 | (not (page-tags ?block #{"pagetag4"})) 273 | ] 274 | } 275 | #+END_QUERY 276 | ` 277 | functions.initialiseQuery() 278 | functions.processCommandList(commands) 279 | advancedquery = functions.constructQuery() 280 | setGlobals({ showcommandcomments: false }) // reset so other tests will be unaffected 281 | resetGlobalsToDefault() 282 | expect(advancedquery).toEqual(expectedresults) 283 | }) 284 | 285 | // ------------------------------------ 286 | test('pagetags - test 3', () => { 287 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 288 | setGlobals() 289 | commands = `title: pages with multiple tags 290 | - pages 291 | - * 292 | - pagetags 293 | - classA 294 | - classB 295 | ` 296 | expectedresults = `#+BEGIN_QUERY 297 | { 298 | :title [:b "pages with multiple tags"] 299 | :query [:find (pull ?block [*]) 300 | :where 301 | [?block :block/name ?pagename] 302 | [?block :block/journal? false] 303 | ( or 304 | (page-tags ?block #{"classa"}) 305 | (page-tags ?block #{"classb"}) 306 | ) 307 | ] 308 | } 309 | #+END_QUERY 310 | ` 311 | functions.initialiseQuery() 312 | functions.processCommandList(commands) 313 | advancedquery = functions.constructQuery() 314 | resetGlobalsToDefault() 315 | expect(advancedquery).toEqual(expectedresults) 316 | }) 317 | 318 | // ------------------------------------ 319 | 320 | 321 | // ------------------------------------ 322 | test('pageproperties - test 1', () => { 323 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 324 | setGlobals() 325 | commands = `title: pageproperties - pagetype only missing pages command 326 | - pages 327 | - * 328 | - pageproperties 329 | - pagetype, "testA" 330 | ` 331 | expectedresults = `#+BEGIN_QUERY 332 | { 333 | :title [:b "pageproperties - pagetype only missing pages command"] 334 | :query [:find (pull ?block [*]) 335 | :where 336 | [?block :block/name ?pagename] 337 | (page-property ?block :pagetype "testA") 338 | ] 339 | } 340 | #+END_QUERY 341 | ` 342 | functions.initialiseQuery() 343 | functions.processCommandList(commands) 344 | advancedquery = functions.constructQuery() 345 | resetGlobalsToDefault() 346 | expect(advancedquery).toEqual(expectedresults) 347 | }) 348 | 349 | 350 | // ------------------------------------ 351 | test('pageproperties - test 2', () => { 352 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 353 | setGlobals() 354 | commands = `title: pageproperties - pagetype testA and testB 355 | - pages 356 | - * 357 | - pageproperties 358 | - pagetype, "testA" 359 | - pagetype, "testB" 360 | ` 361 | expectedresults = `#+BEGIN_QUERY 362 | { 363 | :title [:b "pageproperties - pagetype testA and testB"] 364 | :query [:find (pull ?block [*]) 365 | :where 366 | [?block :block/name ?pagename] 367 | ( or 368 | (page-property ?block :pagetype "testA") 369 | (page-property ?block :pagetype "testB") 370 | ) 371 | ] 372 | } 373 | #+END_QUERY 374 | ` 375 | functions.initialiseQuery() 376 | functions.processCommandList(commands) 377 | advancedquery = functions.constructQuery() 378 | resetGlobalsToDefault() 379 | expect(advancedquery).toEqual(expectedresults) 380 | }) 381 | 382 | // ------------------------------------ 383 | test('pageproperties - test 3', () => { 384 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 385 | setGlobals() 386 | commands = `title: pageproperties - pagetype testA not testB 387 | - pages 388 | - * 389 | - pageproperties 390 | - pagetype, "testA" 391 | - not pagetype, "testB" 392 | ` 393 | expectedresults = `#+BEGIN_QUERY 394 | { 395 | :title [:b "pageproperties - pagetype testA not testB"] 396 | :query [:find (pull ?block [*]) 397 | :where 398 | [?block :block/name ?pagename] 399 | (page-property ?block :pagetype "testA") 400 | (not (page-property ?block :pagetype "testB")) 401 | ] 402 | } 403 | #+END_QUERY 404 | ` 405 | functions.initialiseQuery() 406 | functions.processCommandList(commands) 407 | advancedquery = functions.constructQuery() 408 | resetGlobalsToDefault() 409 | expect(advancedquery).toEqual(expectedresults) 410 | }) 411 | // ------------------------------------ 412 | 413 | // ------------------------------------ 414 | test('blockproperties - pagetype only', () => { 415 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 416 | setGlobals() 417 | commands = `title: blockproperties - pagetype only 418 | - blocks 419 | - * 420 | - pageproperties 421 | - pagetype, "testA" 422 | ` 423 | expectedresults = `#+BEGIN_QUERY 424 | { 425 | :title [:b "blockproperties - pagetype only"] 426 | :query [:find (pull ?block [*]) 427 | :where 428 | [?block :block/content ?blockcontent] 429 | [?block :block/page ?page] 430 | [?page :block/name ?pagename] 431 | (page-property ?page :pagetype "testA") 432 | ] 433 | } 434 | #+END_QUERY 435 | ` 436 | functions.initialiseQuery() 437 | functions.processCommandList(commands) 438 | advancedquery = functions.constructQuery() 439 | resetGlobalsToDefault() 440 | expect(advancedquery).toEqual(expectedresults) 441 | }) 442 | 443 | // ------------------------------------ 444 | test('blockproperties - blockprop3 b1', () => { 445 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 446 | setGlobals() 447 | commands = `title: blockproperties - blockprop3 b1 448 | - blocks 449 | - * 450 | - blockproperties 451 | - blockprop3, "b1" 452 | ` 453 | expectedresults = `#+BEGIN_QUERY 454 | { 455 | :title [:b "blockproperties - blockprop3 b1"] 456 | :query [:find (pull ?block [*]) 457 | :where 458 | [?block :block/content ?blockcontent] 459 | [?block :block/page ?page] 460 | [?page :block/name ?pagename] 461 | (property ?block :blockprop3 "b1") 462 | ] 463 | } 464 | #+END_QUERY 465 | ` 466 | functions.initialiseQuery() 467 | functions.processCommandList(commands) 468 | advancedquery = functions.constructQuery() 469 | resetGlobalsToDefault() 470 | expect(advancedquery).toEqual(expectedresults) 471 | }) 472 | 473 | // ------------------------------------ 474 | test('blockproperties - blockprop3 b1 and b2 and b3', () => { 475 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 476 | setGlobals() 477 | commands = `title: blockproperties - blockprop3 b1 and b2 and b3 478 | - blocks 479 | - * 480 | - blockproperties 481 | - blockprop3, "b1" 482 | - blockprop3, "b2" 483 | - blockprop3, "b3" 484 | ` 485 | expectedresults = `#+BEGIN_QUERY 486 | { 487 | :title [:b "blockproperties - blockprop3 b1 and b2 and b3"] 488 | :query [:find (pull ?block [*]) 489 | :where 490 | [?block :block/content ?blockcontent] 491 | [?block :block/page ?page] 492 | [?page :block/name ?pagename] 493 | ( or 494 | (property ?block :blockprop3 "b1") 495 | (property ?block :blockprop3 "b2") 496 | (property ?block :blockprop3 "b3") 497 | ) 498 | ] 499 | } 500 | #+END_QUERY 501 | ` 502 | functions.initialiseQuery() 503 | functions.processCommandList(commands) 504 | advancedquery = functions.constructQuery() 505 | resetGlobalsToDefault() 506 | expect(advancedquery).toEqual(expectedresults) 507 | }) 508 | 509 | // ------------------------------------ 510 | test('blockproperties - blockprop3 b1 and b2 not b3', () => { 511 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 512 | setGlobals() 513 | commands = `title: blockproperties - blockprop3 b1 and b2 not b3 514 | - blocks 515 | - * 516 | - blockproperties 517 | - blockprop3, "b1" 518 | - blockprop3, "b2" 519 | - not blockprop3, "b3" 520 | ` 521 | expectedresults = `#+BEGIN_QUERY 522 | { 523 | :title [:b "blockproperties - blockprop3 b1 and b2 not b3"] 524 | :query [:find (pull ?block [*]) 525 | :where 526 | [?block :block/content ?blockcontent] 527 | [?block :block/page ?page] 528 | [?page :block/name ?pagename] 529 | ( or 530 | (property ?block :blockprop3 "b1") 531 | (property ?block :blockprop3 "b2") 532 | ) 533 | (not (property ?block :blockprop3 "b3")) 534 | ] 535 | } 536 | #+END_QUERY 537 | ` 538 | functions.initialiseQuery() 539 | functions.processCommandList(commands) 540 | advancedquery = functions.constructQuery() 541 | resetGlobalsToDefault() 542 | expect(advancedquery).toEqual(expectedresults) 543 | }) 544 | 545 | // ------------------------------------ 546 | test('namespace1 block properties', () => { 547 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 548 | setGlobals() 549 | commands = `title: namespace1 block properties 550 | - blocks 551 | - * 552 | - namespace 553 | - namespace1 554 | - blockproperties 555 | - ns1p1blocktype, "nsp1blockvalue" 556 | - ns1p2blocktype, "nsp2blockvalue" 557 | ` 558 | expectedresults = `#+BEGIN_QUERY 559 | { 560 | :title [:b "namespace1 block properties"] 561 | :query [:find (pull ?block [*]) 562 | :where 563 | [?block :block/content ?blockcontent] 564 | [?block :block/page ?page] 565 | [?page :block/name ?pagename] 566 | (namespace ?page "namespace1") 567 | ( or 568 | (property ?block :ns1p1blocktype "nsp1blockvalue") 569 | (property ?block :ns1p2blocktype "nsp2blockvalue") 570 | ) 571 | ] 572 | } 573 | #+END_QUERY 574 | ` 575 | functions.initialiseQuery() 576 | functions.processCommandList(commands) 577 | advancedquery = functions.constructQuery() 578 | resetGlobalsToDefault() 579 | expect(advancedquery).toEqual(expectedresults) 580 | }) 581 | 582 | // ------------------------------------ 583 | test('not namespace1 block properties', () => { 584 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 585 | setGlobals() 586 | commands = `title: not namespace1 block properties 587 | - blocks 588 | - * 589 | - namespace 590 | - not namespace1 591 | - blockproperties 592 | - ns1p1blocktype, "nsp1blockvalue" 593 | - ns1p2blocktype, "nsp2blockvalue" 594 | ` 595 | expectedresults = `#+BEGIN_QUERY 596 | { 597 | :title [:b "not namespace1 block properties"] 598 | :query [:find (pull ?block [*]) 599 | :where 600 | [?block :block/content ?blockcontent] 601 | [?block :block/page ?page] 602 | [?page :block/name ?pagename] 603 | (not (namespace ?page "namespace1")) 604 | ( or 605 | (property ?block :ns1p1blocktype "nsp1blockvalue") 606 | (property ?block :ns1p2blocktype "nsp2blockvalue") 607 | ) 608 | ] 609 | } 610 | #+END_QUERY 611 | ` 612 | functions.initialiseQuery() 613 | functions.processCommandList(commands) 614 | advancedquery = functions.constructQuery() 615 | resetGlobalsToDefault() 616 | expect(advancedquery).toEqual(expectedresults) 617 | }) 618 | 619 | // ------------------------------------ 620 | test('namespace1 block properties that are scheduled', () => { 621 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 622 | setGlobals() 623 | commands = `title: namespace1 block properties that are scheduled 624 | - blocks 625 | - * 626 | - namespace 627 | - namespace1 628 | - blockproperties 629 | - ns1p1blocktype, "nsp1blockvalue" 630 | - ns1p2blocktype, "nsp2blockvalue" 631 | - ns1p3blocktype, "nsp3blockvalue" 632 | - scheduled 633 | ` 634 | expectedresults = `#+BEGIN_QUERY 635 | { 636 | :title [:b "namespace1 block properties that are scheduled"] 637 | :query [:find (pull ?block [*]) 638 | :where 639 | [?block :block/content ?blockcontent] 640 | [?block :block/page ?page] 641 | [?page :block/name ?pagename] 642 | (namespace ?page "namespace1") 643 | ( or 644 | (property ?block :ns1p1blocktype "nsp1blockvalue") 645 | (property ?block :ns1p2blocktype "nsp2blockvalue") 646 | (property ?block :ns1p3blocktype "nsp3blockvalue") 647 | ) 648 | [?block :block/scheduled ?scheduleddate] 649 | ] 650 | } 651 | #+END_QUERY 652 | ` 653 | functions.initialiseQuery() 654 | functions.processCommandList(commands) 655 | advancedquery = functions.constructQuery() 656 | resetGlobalsToDefault() 657 | expect(advancedquery).toEqual(expectedresults) 658 | }) 659 | 660 | // ------------------------------------ 661 | test('namespace1 block properties that have a deadline', () => { 662 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 663 | setGlobals() 664 | commands = `title: namespace1 block properties that have a deadline 665 | - blocks 666 | - * 667 | - namespace 668 | - namespace1 669 | - blockproperties 670 | - ns1p1blocktype, "nsp1blockvalue" 671 | - ns1p2blocktype, "nsp2blockvalue" 672 | - ns1p3blocktype, "nsp3blockvalue" 673 | - ns1p4blocktype, "nsp4blockvalue" 674 | - ns1p5blocktype, "nsp5blockvalue" 675 | - deadline 676 | ` 677 | expectedresults = `#+BEGIN_QUERY 678 | { 679 | :title [:b "namespace1 block properties that have a deadline"] 680 | :query [:find (pull ?block [*]) 681 | :where 682 | [?block :block/content ?blockcontent] 683 | [?block :block/page ?page] 684 | [?page :block/name ?pagename] 685 | (namespace ?page "namespace1") 686 | ( or 687 | (property ?block :ns1p1blocktype "nsp1blockvalue") 688 | (property ?block :ns1p2blocktype "nsp2blockvalue") 689 | (property ?block :ns1p3blocktype "nsp3blockvalue") 690 | (property ?block :ns1p4blocktype "nsp4blockvalue") 691 | (property ?block :ns1p5blocktype "nsp5blockvalue") 692 | ) 693 | [?block :block/deadline ?deadlinedate] 694 | ] 695 | } 696 | #+END_QUERY 697 | ` 698 | functions.initialiseQuery() 699 | functions.processCommandList(commands) 700 | advancedquery = functions.constructQuery() 701 | resetGlobalsToDefault() 702 | expect(advancedquery).toEqual(expectedresults) 703 | }) 704 | 705 | // ------------------------------------ 706 | 707 | 708 | // ------------------------------------ 709 | test('deadline blocks in date range', () => { 710 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 711 | setGlobals() 712 | commands = `title: deadline blocks in date range 713 | - deadlinebetween 714 | - :today :30d-after 715 | ` 716 | expectedresults = `#+BEGIN_QUERY 717 | ;; WARNING: Must have 'pages' command or 'blocks' Command 718 | ;; otherwise the query cannot get any information 719 | ;; Inserting a blocks command for you 720 | 721 | { 722 | :title [:b "deadline blocks in date range"] 723 | :query [:find (pull ?block [*]) 724 | :in $ ?startdate ?enddate 725 | :where 726 | [?block :block/content ?blockcontent] 727 | [?block :block/page ?page] 728 | [?page :block/name ?pagename] 729 | [?block :block/deadline ?deadlinedate] 730 | [(>= ?deadlinedate ?startdate)] 731 | [(<= ?deadlinedate ?enddate)] 732 | ] 733 | :inputs [:today :30d-after] 734 | } 735 | #+END_QUERY 736 | ` 737 | functions.initialiseQuery() 738 | functions.processCommandList(commands) 739 | advancedquery = functions.constructQuery() 740 | resetGlobalsToDefault() 741 | expect(advancedquery).toEqual(expectedresults) 742 | }) 743 | 744 | // ------------------------------------ 745 | 746 | 747 | // ------------------------------------ 748 | test('find journals in a date range', () => { 749 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 750 | setGlobals() 751 | commands = `title: find journals in a date range 752 | - journalsbetween 753 | - :today :30d-after 754 | ` 755 | expectedresults = `#+BEGIN_QUERY 756 | ;; WARNING: Must have 'pages' command or 'blocks' Command 757 | ;; otherwise the query cannot get any information 758 | ;; Inserting a blocks command for you 759 | 760 | { 761 | :title [:b "find journals in a date range"] 762 | :query [:find (pull ?block [*]) 763 | :in $ ?startdate ?enddate 764 | :where 765 | [?block :block/content ?blockcontent] 766 | [?block :block/page ?page] 767 | [?page :block/name ?pagename] 768 | [?page :block/journal-day ?journaldate] 769 | [(>= ?journaldate ?startdate)] 770 | [(<= ?journaldate ?enddate)] 771 | ] 772 | :inputs [:today :30d-after] 773 | } 774 | #+END_QUERY 775 | ` 776 | functions.initialiseQuery() 777 | functions.processCommandList(commands) 778 | advancedquery = functions.constructQuery() 779 | resetGlobalsToDefault() 780 | expect(advancedquery).toEqual(expectedresults) 781 | }) 782 | 783 | 784 | // ------------------------------------ 785 | test('collapse found blocks', () => { 786 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 787 | setGlobals() 788 | commands = `title: collapse found blocks 789 | - pages 790 | - * 791 | - collapse 792 | ` 793 | expectedresults = `#+BEGIN_QUERY 794 | { 795 | :title [:b "collapse found blocks"] 796 | :query [:find (pull ?block [*]) 797 | :where 798 | [?block :block/name ?pagename] 799 | ] 800 | :collapsed? true 801 | } 802 | #+END_QUERY 803 | ` 804 | functions.initialiseQuery() 805 | functions.processCommandList(commands) 806 | advancedquery = functions.constructQuery() 807 | resetGlobalsToDefault() 808 | expect(advancedquery).toEqual(expectedresults) 809 | }) 810 | 811 | // ------------------------------------ 812 | test('expand found blocks', () => { 813 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 814 | setGlobals() 815 | commands = `title: expand found blocks 816 | - pages 817 | - * 818 | - expand 819 | ` 820 | expectedresults = `#+BEGIN_QUERY 821 | { 822 | :title [:b "expand found blocks"] 823 | :query [:find (pull ?block [*]) 824 | :where 825 | [?block :block/name ?pagename] 826 | ] 827 | :collapsed? false 828 | } 829 | #+END_QUERY 830 | ` 831 | functions.initialiseQuery() 832 | functions.processCommandList(commands) 833 | advancedquery = functions.constructQuery() 834 | resetGlobalsToDefault() 835 | expect(advancedquery).toEqual(expectedresults) 836 | }) 837 | 838 | // ------------------------------------ 839 | test('show breadcrumb for found blocks', () => { 840 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 841 | setGlobals() 842 | commands = `title: show breadcrumb for found blocks 843 | - pages 844 | - * 845 | - showbreadcrumb 846 | ` 847 | expectedresults = `#+BEGIN_QUERY 848 | { 849 | :title [:b "show breadcrumb for found blocks"] 850 | :query [:find (pull ?block [*]) 851 | :where 852 | [?block :block/name ?pagename] 853 | ] 854 | :breadcrumb-show? true 855 | } 856 | #+END_QUERY 857 | ` 858 | functions.initialiseQuery() 859 | functions.processCommandList(commands) 860 | advancedquery = functions.constructQuery() 861 | resetGlobalsToDefault() 862 | expect(advancedquery).toEqual(expectedresults) 863 | }) 864 | 865 | // ------------------------------------ 866 | test('hide breadcrumbs for found blocks', () => { 867 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 868 | setGlobals() 869 | commands = `title: hide breadcrumbs for found blocks 870 | - pages 871 | - * 872 | - hidebreadcrumb 873 | ` 874 | expectedresults = `#+BEGIN_QUERY 875 | { 876 | :title [:b "hide breadcrumbs for found blocks"] 877 | :query [:find (pull ?block [*]) 878 | :where 879 | [?block :block/name ?pagename] 880 | ] 881 | :breadcrumb-show? false 882 | } 883 | #+END_QUERY 884 | ` 885 | functions.initialiseQuery() 886 | functions.processCommandList(commands) 887 | advancedquery = functions.constructQuery() 888 | resetGlobalsToDefault() 889 | expect(advancedquery).toEqual(expectedresults) 890 | }) 891 | 892 | test('option command - show command comments', () => { 893 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 894 | setGlobals({ showcommandcomments: true }) 895 | commands = `title: select blocks with links to journals 896 | option: includecomments 897 | - blocks 898 | - * 899 | - pagelinks 900 | - Dec 25th, 2022 901 | - Jan 1st, 2019 902 | ` 903 | expectedresults = `#+BEGIN_QUERY 904 | { 905 | :title [:b "select blocks with links to journals"] 906 | 907 | ;; ---- Get every block into variable ?block 908 | :query [:find (pull ?block [*]) 909 | 910 | ;; ---- filter command 911 | :where 912 | 913 | ;; ---- get block content into variable ?blockcontent 914 | [?block :block/content ?blockcontent] 915 | 916 | ;; ---- get page (special type of block) into variable ?page (used later) 917 | [?block :block/page ?page] 918 | 919 | ;; ---- get page name (lowercase) from the page block into variable ?pagename 920 | [?page :block/name ?pagename] 921 | ( or 922 | 923 | ;; ---- Select block if it has one or more links to other pages 924 | [?block :block/path-refs [:block/name "dec 25th, 2022"]] 925 | [?block :block/path-refs [:block/name "jan 1st, 2019"]] 926 | ) 927 | ] 928 | } 929 | #+END_QUERY 930 | ` 931 | functions.initialiseQuery() 932 | functions.processCommandList(commands) 933 | advancedquery = functions.constructQuery() 934 | expect(advancedquery).toEqual(expectedresults) 935 | resetGlobalsToDefault() 936 | }) 937 | 938 | test('option command - bad option parameter', () => { 939 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 940 | setGlobals({ showcommandcomments: true }) 941 | commands = `title: select blocks with links to journals 942 | option: iamabadoption 943 | - blocks 944 | - * 945 | - pagelinks 946 | - Dec 25th, 2022 947 | - Jan 1st, 2019 948 | ` 949 | expectedresults = `#+BEGIN_QUERY 950 | ;; WARNING: 'option: iamabadoption' is not valid option. 951 | ;; Valid options: includecomments 952 | { 953 | :title [:b \"select blocks with links to journals\"] 954 | 955 | ;; ---- Get every block into variable ?block 956 | :query [:find (pull ?block [*]) 957 | 958 | ;; ---- filter command 959 | :where 960 | 961 | ;; ---- get block content into variable ?blockcontent 962 | [?block :block/content ?blockcontent] 963 | 964 | ;; ---- get page (special type of block) into variable ?page (used later) 965 | [?block :block/page ?page] 966 | 967 | ;; ---- get page name (lowercase) from the page block into variable ?pagename 968 | [?page :block/name ?pagename] 969 | ( or 970 | 971 | ;; ---- Select block if it has one or more links to other pages 972 | [?block :block/path-refs [:block/name \"dec 25th, 2022\"]] 973 | [?block :block/path-refs [:block/name \"jan 1st, 2019\"]] 974 | ) 975 | ] 976 | } 977 | #+END_QUERY 978 | ` 979 | functions.initialiseQuery() 980 | functions.processCommandList(commands) 981 | advancedquery = functions.constructQuery() 982 | expect(advancedquery).toEqual(expectedresults) 983 | resetGlobalsToDefault() 984 | }) 985 | 986 | 987 | 988 | }) // end manual tests 989 | 990 | 991 | describe('Positive function Tests', () => { 992 | 993 | test('initialiseQuery()', () => { 994 | returnval = functions.initialiseQuery() 995 | var expectedvariable = { 996 | "start": [], 997 | "errors": [], 998 | "open": [], 999 | "title": [], 1000 | "query": [], 1001 | "in": [], 1002 | "where": [], 1003 | "filters": [], 1004 | "closefind": [], 1005 | "inputs": [], 1006 | "view": [], 1007 | "options": [], 1008 | "closequery": [], 1009 | "end": [] 1010 | } 1011 | expect(returnval).toEqual( 1012 | expectedvariable) 1013 | }) 1014 | 1015 | test('queryTestDBRead queryTestDB', () => { 1016 | returnval = functions.test_queryTestDBRead() 1017 | expect(returnval).toEqual( 1018 | `title: pages command - select all pages 1019 | - pages 1020 | - * 1021 | #+BEGIN_QUERY 1022 | { 1023 | :title [:b "pages command - select all pages"] 1024 | :query [:find (pull ?block [*]) 1025 | :where 1026 | [?block :block/name ?pagename] 1027 | ] 1028 | } 1029 | #+END_QUERY 1030 | `) 1031 | }) 1032 | 1033 | //------------------------------------------------------- 1034 | // logseqadvancedqueryplugin function tests 1035 | //------------------------------------------------------- 1036 | 1037 | 1038 | test('checkCommandValid for querygroup pages-querylines', () => { 1039 | setGlobals({ querygroup: "pages-querylines" }) 1040 | expect(functions.checkCommandValid("pages")).toEqual( 1041 | [true, ""]) 1042 | expect(functions.checkCommandValid("blocktags")).toEqual( 1043 | [false, '\n' + ';; **ERROR: blocktags not valid with pages command use blocks command instead\n']) 1044 | expect(functions.checkCommandValid("tasks")).toEqual( 1045 | [false, '\n' + ';; **ERROR: tasks not valid with pages command use blocks command instead\n']) 1046 | resetGlobalsToDefault() 1047 | }) 1048 | 1049 | test('checkCommandValid for querygroup blocks-querylines', () => { 1050 | // set global variables 1051 | setGlobals() 1052 | expect(functions.checkCommandValid("blocks")).toEqual( 1053 | [true, ""]) 1054 | expect(functions.checkCommandValid("tasks")).toEqual( 1055 | [true, ""]) 1056 | expect(functions.checkCommandValid("anyoldvalue")).toEqual( 1057 | [true, ""]) 1058 | resetGlobalsToDefault() 1059 | }) 1060 | 1061 | test('initialisteQueryLineDict', () => { 1062 | var tempDict = functions.initialisteQueryLineDict() 1063 | expect(tempDict['blocks-querylines']['where']).toEqual( 1064 | { 1065 | 'name': 'where', 1066 | 'useincommands': ['common'], 1067 | 'segment': 'where', 1068 | 'datalog': ':where', 1069 | 'comment': 'filter command' 1070 | } 1071 | ) 1072 | }) 1073 | 1074 | test('getQueryLine', () => { 1075 | setGlobals() 1076 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1077 | expect(functions.getQueryLine('block_properties_are', "filters")).toEqual( 1078 | '(property ?block :$$ARG1 $$ARG2)' 1079 | ) 1080 | resetGlobalsToDefault() 1081 | }) 1082 | 1083 | test('getQueryLineSegment', () => { 1084 | setGlobals() 1085 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1086 | resetGlobalsToDefault() 1087 | expect(functions.getQueryLineSegment('block_properties_are')).toEqual(["ok", "filters"]) 1088 | }) 1089 | 1090 | test('getQueryLineComment', () => { 1091 | setGlobals({ showcommandcomments: true }) 1092 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1093 | expect(functions.getQueryLineComment('block_properties_are')).toEqual( 1094 | ';; ---- Select if block has a single property (arg1) with value arg2' 1095 | ) 1096 | resetGlobalsToDefault() 1097 | }) 1098 | 1099 | 1100 | 1101 | test('getCommandQueryLineKeys', () => { 1102 | setGlobals({ querygroup: "pages-querylines" }) 1103 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1104 | expect(functions.getCommandQueryLineKeys('pages')).toEqual( 1105 | ['findblocks', 'where', 'pagename'] 1106 | ) 1107 | resetGlobalsToDefault() 1108 | }) 1109 | 1110 | test('processCommandLists', () => { 1111 | setGlobals() 1112 | commandslists = '\ntitle: select and exclude blocks with block properties using and or\n- blocks\n - *\n- blockproperties\n - grade, "b-fiction"\n - or grade, "b-western"\n - and designation, "b-thriller"\n' 1113 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1114 | functions.processCommandList(commandslists) 1115 | resetGlobalsToDefault() 1116 | expect(functions.constructQuery()).toEqual( 1117 | `#+BEGIN_QUERY 1118 | { 1119 | :title [:b "select and exclude blocks with block properties using and or"] 1120 | :query [:find (pull ?block [*]) 1121 | :where 1122 | [?block :block/content ?blockcontent] 1123 | [?block :block/page ?page] 1124 | [?page :block/name ?pagename] 1125 | ( or 1126 | (property ?block :grade "b-fiction") 1127 | (property ?block :grade "b-western") 1128 | ) 1129 | (property ?block :designation "b-thriller") 1130 | ] 1131 | } 1132 | #+END_QUERY 1133 | ` 1134 | ) 1135 | }) 1136 | 1137 | //TODO: Add manual tests from py version here 1138 | 1139 | 1140 | }) // end describe 1141 | 1142 | 1143 | describe('Negative function Tests', () => { 1144 | 1145 | test('pageproperties - test 4', () => { 1146 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1147 | setGlobals() 1148 | commands = `title: pageproperties - missing pages command 1149 | - pageproperties 1150 | - pagetype, "testA" 1151 | - pagetype, "testB" 1152 | - not pagekey, 1 1153 | ` 1154 | expectedresults = `#+BEGIN_QUERY 1155 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1156 | ;; otherwise the query cannot get any information 1157 | ;; Inserting a blocks command for you 1158 | 1159 | { 1160 | :title [:b "pageproperties - missing pages command"] 1161 | :query [:find (pull ?block [*]) 1162 | :where 1163 | [?block :block/content ?blockcontent] 1164 | [?block :block/page ?page] 1165 | [?page :block/name ?pagename] 1166 | ( or 1167 | (page-property ?page :pagetype "testA") 1168 | (page-property ?page :pagetype "testB") 1169 | ) 1170 | (not (page-property ?page :pagekey 1)) 1171 | ] 1172 | } 1173 | #+END_QUERY 1174 | ` 1175 | functions.initialiseQuery() 1176 | functions.processCommandList(commands) 1177 | advancedquery = functions.constructQuery() 1178 | resetGlobalsToDefault() 1179 | expect(advancedquery).toEqual(expectedresults) 1180 | }) 1181 | 1182 | test('missing pages and blocks commands 1', () => { 1183 | commands = `title: missing pages and blocks commands` 1184 | expectedresults = `#+BEGIN_QUERY 1185 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1186 | ;; otherwise the query cannot get any information 1187 | ;; Inserting a blocks command for you 1188 | 1189 | { 1190 | :title [:b "missing pages and blocks commands"] 1191 | :query [:find (pull ?block [*]) 1192 | :where 1193 | [?block :block/content ?blockcontent] 1194 | [?block :block/page ?page] 1195 | [?page :block/name ?pagename] 1196 | ] 1197 | } 1198 | #+END_QUERY 1199 | ` 1200 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1201 | setGlobals() 1202 | functions.initialiseQuery() 1203 | functions.processCommandList(commands) 1204 | advancedquery = functions.constructQuery() 1205 | resetGlobalsToDefault() 1206 | expect(advancedquery).toEqual(expectedresults) 1207 | }) 1208 | 1209 | test('bad commands', () => { 1210 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1211 | setGlobals() 1212 | commands = `title: bad commands 1213 | - badcommand1 1214 | - badcommand2 1215 | forgottenthedash 1216 | ` 1217 | expectedresults = `#+BEGIN_QUERY 1218 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1219 | ;; otherwise the query cannot get any information 1220 | ;; Inserting a blocks command for you 1221 | 1222 | ;; WARNING: '- badcommand1' is not valid command. 1223 | ;; Either a mispelt command or no leading dash 1224 | ;; WARNING: '- badcommand2' is not valid command. 1225 | ;; Either a mispelt command or no leading dash 1226 | ;; WARNING: forgottenthedash has no leading hypen eg '- pages' 1227 | { 1228 | :title [:b "bad commands"] 1229 | :query [:find (pull ?block [*]) 1230 | :where 1231 | [?block :block/content ?blockcontent] 1232 | [?block :block/page ?page] 1233 | [?page :block/name ?pagename] 1234 | ] 1235 | } 1236 | #+END_QUERY 1237 | ` 1238 | functions.initialiseQuery() 1239 | functions.processCommandList(commands) 1240 | advancedquery = functions.constructQuery() 1241 | resetGlobalsToDefault() 1242 | expect(advancedquery).toEqual(expectedresults) 1243 | }) 1244 | 1245 | test('bad title', () => { 1246 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1247 | setGlobals() 1248 | commands = `title pages command - select all pages 1249 | - pages 1250 | - * 1251 | ` 1252 | expectedresults = `#+BEGIN_QUERY 1253 | ;; WARNING: title line should start with title: 1254 | { 1255 | :query [:find (pull ?block [*]) 1256 | :where 1257 | [?block :block/name ?pagename] 1258 | ] 1259 | } 1260 | #+END_QUERY 1261 | ` 1262 | functions.initialiseQuery() 1263 | functions.processCommandList(commands) 1264 | advancedquery = functions.constructQuery() 1265 | resetGlobalsToDefault() 1266 | expect(advancedquery).toEqual(expectedresults) 1267 | }) 1268 | 1269 | test('equal rather than dash', () => { 1270 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1271 | setGlobals() 1272 | commands = `title: equal sign used instead of dash 1273 | - pages 1274 | = testpage00* 1275 | = pagetags 1276 | ` 1277 | expectedresults = `#+BEGIN_QUERY 1278 | ;; WARNING: = testpage00* has no leading hypen eg '- pages' 1279 | ;; WARNING: = pagetags has no leading hypen eg '- pages' 1280 | { 1281 | :title [:b "equal sign used instead of dash"] 1282 | :query [:find (pull ?block [*]) 1283 | :where 1284 | [?block :block/name ?pagename] 1285 | ] 1286 | } 1287 | #+END_QUERY 1288 | ` 1289 | functions.initialiseQuery() 1290 | functions.processCommandList(commands) 1291 | advancedquery = functions.constructQuery() 1292 | resetGlobalsToDefault() 1293 | expect(advancedquery).toEqual(expectedresults) 1294 | }) 1295 | 1296 | test('only an argument line', () => { 1297 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1298 | setGlobals() 1299 | commands = `title: missing command 1300 | - testpage00* 1301 | ` 1302 | expectedresults = `#+BEGIN_QUERY 1303 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1304 | ;; otherwise the query cannot get any information 1305 | ;; Inserting a blocks command for you 1306 | 1307 | { 1308 | :title [:b "missing command"] 1309 | :query [:find (pull ?block [*]) 1310 | :where 1311 | [?block :block/content ?blockcontent] 1312 | [?block :block/page ?page] 1313 | [?page :block/name ?pagename] 1314 | ( or 1315 | [(clojure.string/ends-with? ?blockcontent "testpage00")] 1316 | ) 1317 | ] 1318 | } 1319 | #+END_QUERY 1320 | ` 1321 | functions.initialiseQuery() 1322 | functions.processCommandList(commands) 1323 | advancedquery = functions.constructQuery() 1324 | resetGlobalsToDefault() 1325 | expect(advancedquery).toEqual(expectedresults) 1326 | }) 1327 | 1328 | test('blocktags - test 1', () => { 1329 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1330 | setGlobals() 1331 | commands = `title: blocktags command - test 1 1332 | - blocktags 1333 | - tag1 1334 | - tag2 1335 | - not tag3 1336 | ` 1337 | expectedresults = `#+BEGIN_QUERY 1338 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1339 | ;; otherwise the query cannot get any information 1340 | ;; Inserting a blocks command for you 1341 | 1342 | { 1343 | :title [:b "blocktags command - test 1"] 1344 | :query [:find (pull ?block [*]) 1345 | :where 1346 | [?block :block/content ?blockcontent] 1347 | [?block :block/page ?page] 1348 | [?page :block/name ?pagename] 1349 | ( or 1350 | (page-ref ?block "tag1") 1351 | (page-ref ?block "tag2") 1352 | ) 1353 | (not (page-ref ?block "tag3")) 1354 | ] 1355 | } 1356 | #+END_QUERY 1357 | ` 1358 | functions.initialiseQuery() 1359 | functions.processCommandList(commands) 1360 | advancedquery = functions.constructQuery() 1361 | resetGlobalsToDefault() 1362 | expect(advancedquery).toEqual(expectedresults) 1363 | }) 1364 | 1365 | test('blocktags - test 2', () => { 1366 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1367 | setGlobals() 1368 | commands = `title: blocktags and pages command combined - test 2 1369 | - pages 1370 | - test* 1371 | - *an 1372 | - blocktags 1373 | - tag1 1374 | - tag2 1375 | ` 1376 | expectedresults = `#+BEGIN_QUERY 1377 | 1378 | ;; **ERROR: blocktags not valid with pages command use blocks command instead 1379 | 1380 | { 1381 | :title [:b "blocktags and pages command combined - test 2"] 1382 | :query [:find (pull ?block [*]) 1383 | :where 1384 | [?block :block/name ?pagename] 1385 | ( or 1386 | [(clojure.string/starts-with? ?pagename "test")] 1387 | [(clojure.string/ends-with? ?pagename "an")] 1388 | ) 1389 | ] 1390 | } 1391 | #+END_QUERY 1392 | ` 1393 | functions.initialiseQuery() 1394 | functions.processCommandList(commands) 1395 | advancedquery = functions.constructQuery() 1396 | resetGlobalsToDefault() 1397 | expect(advancedquery).toEqual(expectedresults) 1398 | }) 1399 | 1400 | test('pagetags - test 1', () => { 1401 | commands = `title: pagetags - test 1 1402 | - pagetags 1403 | - pagetag1 1404 | - pagetag2 1405 | - not pagetag4 1406 | ` 1407 | expectedresults = `#+BEGIN_QUERY 1408 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1409 | ;; otherwise the query cannot get any information 1410 | ;; Inserting a blocks command for you 1411 | 1412 | { 1413 | :title [:b "pagetags - test 1"] 1414 | :query [:find (pull ?block [*]) 1415 | :where 1416 | [?block :block/content ?blockcontent] 1417 | [?block :block/page ?page] 1418 | [?page :block/name ?pagename] 1419 | [?page :block/journal? false] 1420 | ( or 1421 | (page-tags ?page #{"pagetag1"}) 1422 | (page-tags ?page #{"pagetag2"}) 1423 | ) 1424 | (not (page-tags ?page #{"pagetag4"})) 1425 | ] 1426 | } 1427 | #+END_QUERY 1428 | ` 1429 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1430 | setGlobals() 1431 | functions.initialiseQuery() 1432 | functions.processCommandList(commands) 1433 | advancedquery = functions.constructQuery() 1434 | resetGlobalsToDefault() 1435 | expect(advancedquery).toEqual(expectedresults) 1436 | }) 1437 | 1438 | 1439 | test('blocktags - test 3', () => { 1440 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1441 | setGlobals() 1442 | commands = `title: blocktags and pages command combined - test 3 1443 | - pages 1444 | - test* 1445 | - *2 1446 | - not *age1 1447 | - blocktags 1448 | - tag1 1449 | - not tag2 1450 | - tag3 1451 | - not tag4 1452 | ` 1453 | expectedresults = `#+BEGIN_QUERY 1454 | 1455 | ;; **ERROR: blocktags not valid with pages command use blocks command instead 1456 | 1457 | { 1458 | :title [:b "blocktags and pages command combined - test 3"] 1459 | :query [:find (pull ?block [*]) 1460 | :where 1461 | [?block :block/name ?pagename] 1462 | ( or 1463 | [(clojure.string/starts-with? ?pagename "test")] 1464 | [(clojure.string/ends-with? ?pagename "2")] 1465 | ) 1466 | (not [(clojure.string/ends-with? ?pagename "age1")]) 1467 | ] 1468 | } 1469 | #+END_QUERY 1470 | ` 1471 | functions.initialiseQuery() 1472 | functions.processCommandList(commands) 1473 | advancedquery = functions.constructQuery() 1474 | resetGlobalsToDefault() 1475 | expect(advancedquery).toEqual(expectedresults) 1476 | }) 1477 | 1478 | test('tasks - test 1', () => { 1479 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1480 | setGlobals() 1481 | commands = `title: tasks - select and exclude task types 1482 | - tasks 1483 | - TODO 1484 | - not DOING 1485 | ` 1486 | expectedresults = `#+BEGIN_QUERY 1487 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1488 | ;; otherwise the query cannot get any information 1489 | ;; Inserting a blocks command for you 1490 | 1491 | { 1492 | :title [:b "tasks - select and exclude task types"] 1493 | :query [:find (pull ?block [*]) 1494 | :where 1495 | [?block :block/content ?blockcontent] 1496 | [?block :block/page ?page] 1497 | [?page :block/name ?pagename] 1498 | [?block :block/marker ?marker] 1499 | [(contains? #{"TODO"} ?marker)] 1500 | (not [(contains? #{"DOING"} ?marker)]) 1501 | ] 1502 | } 1503 | #+END_QUERY 1504 | ` 1505 | functions.initialiseQuery() 1506 | functions.processCommandList(commands) 1507 | advancedquery = functions.constructQuery() 1508 | resetGlobalsToDefault() 1509 | expect(advancedquery).toEqual(expectedresults) 1510 | }) 1511 | 1512 | // ------------------------------------ 1513 | test('tasks - test 2', () => { 1514 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1515 | setGlobals() 1516 | commands = `title: tasks and pages 1517 | - pages 1518 | - *7 1519 | - tasks 1520 | - TODO 1521 | - not DOING 1522 | ` 1523 | expectedresults = `#+BEGIN_QUERY 1524 | 1525 | ;; **ERROR: tasks not valid with pages command use blocks command instead 1526 | 1527 | { 1528 | :title [:b "tasks and pages"] 1529 | :query [:find (pull ?block [*]) 1530 | :where 1531 | [?block :block/name ?pagename] 1532 | [(clojure.string/ends-with? ?pagename "7")] 1533 | ] 1534 | } 1535 | #+END_QUERY 1536 | ` 1537 | functions.initialiseQuery() 1538 | functions.processCommandList(commands) 1539 | advancedquery = functions.constructQuery() 1540 | resetGlobalsToDefault() 1541 | expect(advancedquery).toEqual(expectedresults) 1542 | }) 1543 | 1544 | 1545 | test('scheduled blocks in date range', () => { 1546 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1547 | setGlobals() 1548 | commands = `title: scheduled blocks in date range 1549 | - scheduledbetween 1550 | - :today :30d-after 1551 | ` 1552 | expectedresults = `#+BEGIN_QUERY 1553 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1554 | ;; otherwise the query cannot get any information 1555 | ;; Inserting a blocks command for you 1556 | 1557 | { 1558 | :title [:b "scheduled blocks in date range"] 1559 | :query [:find (pull ?block [*]) 1560 | :in $ ?startdate ?enddate 1561 | :where 1562 | [?block :block/content ?blockcontent] 1563 | [?block :block/page ?page] 1564 | [?page :block/name ?pagename] 1565 | [?block :block/scheduled ?scheduleddate] 1566 | [(>= ?scheduleddate ?startdate)] 1567 | [(<= ?scheduleddate ?enddate)] 1568 | ] 1569 | :inputs [:today :30d-after] 1570 | } 1571 | #+END_QUERY 1572 | ` 1573 | functions.initialiseQuery() 1574 | functions.processCommandList(commands) 1575 | advancedquery = functions.constructQuery() 1576 | resetGlobalsToDefault() 1577 | expect(advancedquery).toEqual(expectedresults) 1578 | }) 1579 | 1580 | test('journal pages only', () => { 1581 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1582 | setGlobals() 1583 | commands = `title: journal pages only 1584 | - journalonly 1585 | ` 1586 | expectedresults = `#+BEGIN_QUERY 1587 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1588 | ;; otherwise the query cannot get any information 1589 | ;; Inserting a blocks command for you 1590 | 1591 | { 1592 | :title [:b "journal pages only"] 1593 | :query [:find (pull ?block [*]) 1594 | :where 1595 | [?block :block/content ?blockcontent] 1596 | [?block :block/page ?page] 1597 | [?page :block/name ?pagename] 1598 | [?page :block/journal? true] 1599 | ] 1600 | } 1601 | #+END_QUERY 1602 | ` 1603 | functions.initialiseQuery() 1604 | functions.processCommandList(commands) 1605 | advancedquery = functions.constructQuery() 1606 | resetGlobalsToDefault() 1607 | expect(advancedquery).toEqual(expectedresults) 1608 | }) 1609 | 1610 | 1611 | 1612 | }) // end describe 1613 | 1614 | 1615 | //------------------------------------------------------- 1616 | // automated test cases 1617 | //------------------------------------------------------- 1618 | 1619 | //TODO: use queryTestDB to run all the tests automatically 1620 | // note console.debug output is in the Jest Log not the Code Log 1621 | // see drop down at the top of the right panel (View, Output) 1622 | 1623 | test('automated testcases access', () => { 1624 | var msg = '' 1625 | console.log("****** AUTOMATED TESTS STARTED ******"); 1626 | functions.setquerylineDBDict(functions.initialisteQueryLineDict()) 1627 | setGlobals() 1628 | testcases = functions.getquerytestcases() 1629 | for (let testno = 0; testno < testcases.length; testno++) { 1630 | commands = gettestcommands(testno) 1631 | console.log(" - test " + (testno + 1) + " started ..." + 1632 | '\n---------------------------------' + 1633 | '\n' + commands + 1634 | '\n---------------------------------') 1635 | msg += " - test " + (testno + 1) + " started ..." + 1636 | '\n---------------------------------' + 1637 | '\n' + commands + 1638 | '\n---------------------------------' 1639 | expectedresults = gettestexpectedresults(testno) 1640 | functions.initialiseQuery() 1641 | functions.processCommandList(commands) 1642 | advancedquery = functions.constructQuery() 1643 | msg += advancedquery + 1644 | '\n---------------------------------\n\n' 1645 | 1646 | // if (testno == 44) { 1647 | // var a = 1 1648 | // } 1649 | resetGlobalsToDefault() 1650 | expect(advancedquery).toEqual(expectedresults) 1651 | console.log(" - test " + testno + " completed ok"); 1652 | 1653 | } 1654 | saveAutomatedTestOutput(msg) 1655 | 1656 | }) 1657 | 1658 | //------------------------------------------------------- 1659 | // test local functions 1660 | //------------------------------------------------------- 1661 | 1662 | describe('Housekeeping Tests', () => { 1663 | test('set globals', () => { 1664 | setGlobals( 1665 | { 1666 | querygroup: "abc", 1667 | showcommandcomments: true, 1668 | codeblock: false 1669 | }) 1670 | returnval = functions.getquerygroup() 1671 | expect(returnval).toBe("abc") 1672 | returnval = functions.getshowcommandcomments() 1673 | expect(returnval).toBe(true) 1674 | returnval = functions.getcodeblock() 1675 | expect(returnval).toBe(false) 1676 | resetGlobalsToDefault() 1677 | }) 1678 | 1679 | test('load global value querygroup', () => { 1680 | functions.setquerygroup("def") 1681 | returnval = functions.getquerygroup() 1682 | expect(returnval).toBe("def") 1683 | }) 1684 | 1685 | test('remove last generated query', () => { 1686 | content = `- pages 1687 | - * 1688 | - blocktags 1689 | - mytag 1690 | ` 1691 | expectedresult = content 1692 | content += `#+BEGIN_QUERY 1693 | { 1694 | :title [:b "Pages only - Access page properties"] 1695 | :query [:find (pull ?block [*]) 1696 | :where 1697 | [?block :block/original-name ?originalpagename] 1698 | [?block :block/name ?pagename] 1699 | (page-property ?block :pagetype "p-minor") 1700 | ] 1701 | } 1702 | #+END_QUERY` 1703 | 1704 | returnval = functions.removeLastGeneratedQuery(content) 1705 | expect(returnval).toBe(expectedresult) 1706 | content = `- pages 1707 | - * 1708 | - blocktags 1709 | - mytag 1710 | ` 1711 | expect(returnval).toBe(expectedresult) 1712 | }) 1713 | 1714 | 1715 | 1716 | test('gettestcommands', () => { 1717 | var commands = gettestcommands(0) 1718 | expect(commands).toBe( 1719 | `title: pages command - select all pages 1720 | - pages 1721 | - *` 1722 | ) 1723 | }) 1724 | 1725 | test('gettestexpectedresults', () => { 1726 | var expectedresults = gettestexpectedresults(0) 1727 | expect(expectedresults).toBe( 1728 | `#+BEGIN_QUERY 1729 | { 1730 | :title [:b "pages command - select all pages"] 1731 | :query [:find (pull ?block [*]) 1732 | :where 1733 | [?block :block/name ?pagename] 1734 | ] 1735 | } 1736 | #+END_QUERY 1737 | ` 1738 | ) 1739 | }) 1740 | 1741 | //------------------------------------------------------- 1742 | // data access tests 1743 | //------------------------------------------------------- 1744 | 1745 | test('queryDBRead queryDB start entry', () => { 1746 | returnval = functions.test_queryDBRead("common-querylines", "start") 1747 | expect(returnval).toEqual( 1748 | `start 1749 | start 1750 | start 1751 | #+BEGIN_QUERY 1752 | 1753 | `) 1754 | }) 1755 | 1756 | test('queryDBRead queryDB not_arg_pagename_contains entry', () => { 1757 | returnval = functions.test_queryDBRead("pages-querylines", "not_arg_pagename_contains") 1758 | expect(returnval).toEqual( 1759 | `not_arg_pagename_contains 1760 | filters 1761 | filters 1762 | (not [(clojure.string/includes? ?pagename "$$ARG1")]) 1763 | Exclude if page title contains arg1 1764 | `) 1765 | }) 1766 | 1767 | 1768 | }) // end describe 1769 | 1770 | 1771 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // logseq-advanced-query-builder v0.1 2 | // set mode of execution at end of this script 3 | // 'local' mode for desktop testing with Jest Test framework 4 | // 'logseq-plugin' for operating as a plugin within logseq 5 | 6 | // Deployment 7 | // copy index.js to deploy folder 8 | // do not copy package.json to deploy folder as the main package.json 9 | // is configure for jest testing 10 | 11 | // Dictionary of query lines data 12 | var querylineDict = { 13 | 'common-querylines': { 14 | 'start': 15 | { 16 | 'name': 'start', 17 | 'useincommands': ['common'], 18 | 'segment': 'start', 19 | 'datalog': '#+BEGIN_QUERY', 20 | 'comment': '' 21 | }, 22 | 'open': 23 | { 24 | 'name': 'open', 25 | 'useincommands': ['common'], 26 | 'segment': 'open', 27 | 'datalog': '{', 28 | 'comment': '' 29 | }, 30 | 'title': 31 | { 32 | 'name': 'title', 33 | 'useincommands': ['common'], 34 | 'segment': 'title', 35 | 'datalog': ':title [:b "$$ARG1"]', 36 | 'comment': '' 37 | }, 38 | 'findblocks': 39 | { 40 | 'name': 'findblocks', 41 | 'useincommands': ['common'], 42 | 'segment': 'query', 43 | 'datalog': ':query [:find (pull ?block [*])', 44 | 'comment': 'Get every block into variable ?block' 45 | }, 46 | 'daterange': 47 | { 48 | 'name': 'daterange', 49 | 'useincommands': ['common'], 50 | 'segment': 'in', 51 | 'datalog': ':in $ ?startdate ?enddate', 52 | 'comment': 'give query the ?startdate variable and the ?enddate variable (set by :inputs)' 53 | }, 54 | 'where': 55 | { 56 | 'name': 'where', 57 | 'useincommands': ['common'], 58 | 'segment': 'where', 59 | 'datalog': ':where', 60 | 'comment': 'filter command' 61 | }, 62 | 'tasks_are': 63 | { 64 | 'name': 'tasks_are', 65 | 'useincommands': ['common'], 66 | 'segment': 'filters', 67 | 'datalog': '[(contains? #{"$$ARG1"} ?marker)]', 68 | 'comment': 'Select block if it has one or more tasks (TODO or DONE etc)' 69 | }, 70 | 'not_tasks_are': 71 | { 72 | 'name': 'not_tasks_are', 73 | 'useincommands': ['common'], 74 | 'segment': 'filters', 75 | 'datalog': '(not [(contains? #{"$$ARG1"} ?marker)])', 76 | 'comment': 'Exclude block if it has one or more tasks' 77 | }, 78 | 'pagelinks_are': 79 | { 80 | 'name': 'pagelinks_are', 81 | 'useincommands': ['common'], 82 | 'segment': 'filters', 83 | 'datalog': '[?block :block/path-refs [:block/name "$$ARG1"]]', 84 | 'comment': 'Select block if it has one or more links to other pages' 85 | }, 86 | 'not_pagelinks_are': 87 | { 88 | 'name': 'not_pagelinks_are', 89 | 'useincommands': ['common'], 90 | 'segment': 'filters', 91 | 'datalog': '(not [?block :block/path-refs [:block/name "$$ARG1"]])', 92 | 'comment': 'Exclude block if it has one or more links to other pages' 93 | }, 94 | 'scheduledbetween': 95 | { 96 | 'name': 'scheduledbetween', 97 | 'useincommands': ['common'], 98 | 'segment': 'inputs', 99 | 'datalog': ':inputs [$$ARG1]', 100 | 'comment': 'set the input values for a date range for example could be\n :today :365d-after' 101 | }, 102 | 'journalfrom': 103 | { 104 | 'name': 'journalfrom', 105 | 'useincommands': ['common'], 106 | 'segment': 'filters', 107 | 'datalog': '[(>= ?journaldate ?startdate)]', 108 | 'comment': 'Select if journaldate greater than start date' 109 | }, 110 | 'journalto': 111 | { 112 | 'name': 'journalto', 113 | 'useincommands': ['common'], 114 | 'segment': 'filters', 115 | 'datalog': '[(<= ?journaldate ?enddate)]', 116 | 'comment': 'Select if journalddate less than end date' 117 | }, 118 | 'journalsbetween': 119 | { 120 | 'name': 'journalsbetween', 121 | 'useincommands': ['common'], 122 | 'segment': 'inputs', 123 | 'datalog': ':inputs [$$ARG1]', 124 | 'comment': 'set the input values for a date range for example could be\n :today :365d-after' 125 | }, 126 | 'breadcrumb_show_false': 127 | { 128 | 'name': 'breadcrumb_show_false', 129 | 'useincommands': ['common'], 130 | 'segment': 'options', 131 | 'datalog': ':breadcrumb-show? false', 132 | 'comment': 'Suppress breadcrumbs view' 133 | }, 134 | 'breadcrumb_show_true': 135 | { 136 | 'name': 'breadcrumb_show_true', 137 | 'useincommands': ['common'], 138 | 'segment': 'options', 139 | 'datalog': ':breadcrumb-show? true', 140 | 'comment': 'Show breadcrumbs above each block' 141 | }, 142 | 'collapse_false': 143 | { 144 | 'name': 'collapse_false', 145 | 'useincommands': ['common'], 146 | 'segment': 'options', 147 | 'datalog': ':collapsed? false', 148 | 'comment': 'Toggle collapse or fold' 149 | }, 150 | 'collapse_true': 151 | { 152 | 'name': 'collapse_true', 153 | 'useincommands': ['common'], 154 | 'segment': 'options', 155 | 'datalog': ':collapsed? true', 156 | 'comment': 'Toggle collapse or fold' 157 | }, 158 | 'closefind': 159 | { 160 | 'name': 'closefind', 161 | 'useincommands': ['common'], 162 | 'segment': 'closefind', 163 | 'datalog': ']', 164 | 'comment': '' 165 | }, 166 | 'closequery': 167 | { 168 | 'name': 'closequery', 169 | 'useincommands': ['common'], 170 | 'segment': 'closequery', 171 | 'datalog': '}', 172 | 'comment': '' 173 | }, 174 | 'end': 175 | { 176 | 'name': 'end', 177 | 'useincommands': ['common'], 178 | 'segment': 'end', 179 | 'datalog': '#+END_QUERY', 180 | 'comment': '' 181 | }, 182 | }, 183 | 'blocks-querylines': { 184 | 'blockcontent': 185 | { 186 | 'name': 'blockcontent', 187 | 'useincommands': ['blocks'], 188 | 'segment': 'filters', 189 | 'datalog': '[?block :block/content ?blockcontent]', 190 | 'comment': 'get block content into variable ?blockcontent' 191 | }, 192 | 'page': 193 | { 194 | 'name': 'page', 195 | 'useincommands': ['blocks'], 196 | 'segment': 'filters', 197 | 'datalog': '[?block :block/page ?page]', 198 | 'comment': 'get page (special type of block) into variable ?page (used later)' 199 | }, 200 | 'pagename': 201 | { 202 | 'name': 'pagename', 203 | 'useincommands': ['blocks'], 204 | 'segment': 'filters', 205 | 'datalog': '[?page :block/name ?pagename]', 206 | 'comment': 'get page name (lowercase) from the page block into variable ?pagename' 207 | }, 208 | 'marker': 209 | { 210 | 'name': 'marker', 211 | 'useincommands': ['blocks'], 212 | 'segment': 'filters', 213 | 'datalog': '[?block :block/marker ?marker]', 214 | 'comment': 'get block marker (TODO LATER ETC) into variable ?marker' 215 | }, 216 | 'scheduled': 217 | { 218 | 'name': 'scheduled', 219 | 'useincommands': ['blocks'], 220 | 'segment': 'filters', 221 | 'datalog': '[?block :block/scheduled ?scheduleddate]', 222 | 'comment': 'get scheduled date in the block into variable ?scheduleddate' 223 | }, 224 | 'scheduledfrom': 225 | { 226 | 'name': 'scheduledfrom', 227 | 'useincommands': ['blocks'], 228 | 'segment': 'filters', 229 | 'datalog': '[(>= ?scheduleddate ?startdate)]', 230 | 'comment': 'Select if scheduleddate greater than start date' 231 | }, 232 | 'scheduledto': 233 | { 234 | 'name': 'scheduledto', 235 | 'useincommands': ['blocks'], 236 | 'segment': 'filters', 237 | 'datalog': '[(<= ?scheduleddate ?enddate)]', 238 | 'comment': 'Select if scheduleddate less than end date' 239 | }, 240 | 'deadline': 241 | { 242 | 'name': 'deadline', 243 | 'useincommands': ['blocks'], 244 | 'segment': 'filters', 245 | 'datalog': '[?block :block/deadline ?deadlinedate]', 246 | 'comment': 'get deadline date in the block into variable ?date' 247 | }, 248 | 'deadlinefrom': 249 | { 250 | 'name': 'deadlinefrom', 251 | 'useincommands': ['blocks'], 252 | 'segment': 'filters', 253 | 'datalog': '[(>= ?deadlinedate ?startdate)]', 254 | 'comment': 'Select if deadlinedate greater than start date' 255 | }, 256 | 'deadlineto': 257 | { 258 | 'name': 'deadlineto', 259 | 'useincommands': ['blocks'], 260 | 'segment': 'filters', 261 | 'datalog': '[(<= ?deadlinedate ?enddate)]', 262 | 'comment': 'Select if deadlinedate less than end date' 263 | }, 264 | 'pagename_is': 265 | { 266 | 'name': 'pagename_is', 267 | 'useincommands': ['blocks'], 268 | 'segment': 'filters', 269 | 'datalog': '[?block :block/name "$$ARG1"]', 270 | 'comment': 'Select specific page from the page special block\nPresence of :block/name in a blocks means \nit is a block that has the page attributes' 271 | }, 272 | 'not_pagename_is': 273 | { 274 | 'name': 'not_pagename_is', 275 | 'useincommands': ['blocks'], 276 | 'segment': 'filters', 277 | 'datalog': '(not [?block :block/name "$$ARG1"])', 278 | 'comment': 'Exclude specific page from the page special block' 279 | }, 280 | 'page_is_journal': 281 | { 282 | 'name': 'page_is_journal', 283 | 'useincommands': ['blocks'], 284 | 'segment': 'filters', 285 | 'datalog': '[?page :block/journal? true]', 286 | 'comment': 'Select block if its belongs to a journal page' 287 | }, 288 | 'not_page_is_journal': 289 | { 290 | 'name': 'not_page_is_journal', 291 | 'useincommands': ['blocks'], 292 | 'segment': 'filters', 293 | 'datalog': '[?page :block/journal? false]', 294 | 'comment': 'Exclude block if its belongs to a journal page' 295 | }, 296 | 'journal_date': 297 | { 298 | 'name': 'journal_date', 299 | 'useincommands': ['blocks'], 300 | 'segment': 'filters', 301 | 'datalog': '[?page :block/journal-day ?journaldate]', 302 | 'comment': 'get journal date into variable ?journaldate' 303 | }, 304 | 'block_properties_are': 305 | { 306 | 'name': 'block_properties_are', 307 | 'useincommands': ['blocks'], 308 | 'segment': 'filters', 309 | 'datalog': '(property ?block :$$ARG1 $$ARG2)', 310 | 'comment': 'Select if block has a single property (arg1) with value arg2' 311 | }, 312 | 'not_block_properties_are': 313 | { 314 | 'name': 'not_block_properties_are', 315 | 'useincommands': ['blocks'], 316 | 'segment': 'filters', 317 | 'datalog': '(not (property ?block :$$ARG1 $$ARG2))', 318 | 'comment': 'Exclude if block has a single property (arg1) with value arg2' 319 | }, 320 | 'blocktags_are': 321 | { 322 | 'name': 'blocktags_are', 323 | 'useincommands': ['blocks'], 324 | 'segment': 'filters', 325 | 'datalog': '(page-ref ?block "$$ARG1")', 326 | 'comment': 'Select block if it has a specific tag or page link' 327 | }, 328 | 'not_blocktags_are': 329 | { 330 | 'name': 'not_blocktags_are', 331 | 'useincommands': ['blocks'], 332 | 'segment': 'filters', 333 | 'datalog': '(not (page-ref ?block "$$ARG1"))', 334 | 'comment': 'Exclude block if it has a specific tag or page link' 335 | }, 336 | 'page_properties_are': 337 | { 338 | 'name': 'page_properties_are', 339 | 'useincommands': ['blocks'], 340 | 'segment': 'filters', 341 | 'datalog': '(page-property ?page :$$ARG1 $$ARG2)', 342 | 'comment': 'Select block if the page it belongs to\nhas a page property (arg1) with value arg2' 343 | }, 344 | 'not_page_properties_are': 345 | { 346 | 'name': 'not_page_properties_are', 347 | 'useincommands': ['blocks'], 348 | 'segment': 'filters', 349 | 'datalog': '(not (page-property ?page :$$ARG1 $$ARG2))', 350 | 'comment': 'Exclude block if the page it belongs to\nhas a page property (arg1) with value arg2' 351 | }, 352 | 'pagetags_are': 353 | { 354 | 'name': 'pagetags_are', 355 | 'useincommands': ['blocks'], 356 | 'segment': 'filters', 357 | 'datalog': '(page-tags ?page #{"$$ARG1"})', 358 | 'comment': 'Select block if the page it belongs to\nhas a one or more page tags' 359 | }, 360 | 'not_pagetags_are': 361 | { 362 | 'name': 'not_pagetags_are', 363 | 'useincommands': ['blocks'], 364 | 'segment': 'filters', 365 | 'datalog': '(not (page-tags ?page #{"$$ARG1"}))', 366 | 'comment': 'Exclude block if the page it belongs to\nhas one or more page tags' 367 | }, 368 | 'namespace': 369 | { 370 | 'name': 'namespace', 371 | 'useincommands': ['blocks'], 372 | 'segment': 'filters', 373 | 'datalog': '(namespace ?page "$$ARG1")', 374 | 'comment': 'Select block if the page it belongs to is within namespace arg1' 375 | }, 376 | 'not_namespace': 377 | { 378 | 'name': 'not_namespace_blocks', 379 | 'useincommands': ['blocks'], 380 | 'segment': 'filters', 381 | 'datalog': '(not (namespace ?page "$$ARG1"))', 382 | 'comment': 'Exclude block if the page it belongs to is within namespace arg1' 383 | }, 384 | 'arg_blockcontent_startswith': 385 | { 386 | 'name': 'arg_blockcontent_startswith', 387 | 'useincommands': ['blocks'], 388 | 'segment': 'filters', 389 | 'datalog': '[(clojure.string/starts-with? ?blockcontent "$$ARG1")]', 390 | 'comment': 'Select if block content starts with arg1' 391 | }, 392 | 'arg_blockcontent_endswith': 393 | { 394 | 'name': 'arg_blockcontent_endswith', 395 | 'useincommands': ['blocks'], 396 | 'segment': 'filters', 397 | 'datalog': '[(clojure.string/ends-with? ?blockcontent "$$ARG1")]', 398 | 'comment': 'Select if block content ends with arg1' 399 | }, 400 | 'arg_blockcontent_contains': 401 | { 402 | 'name': 'arg_blockcontent_contains', 403 | 'useincommands': ['blocks'], 404 | 'segment': 'filters', 405 | 'datalog': '[(clojure.string/includes? ?blockcontent "$$ARG1")]', 406 | 'comment': 'Select if block content contains arg1' 407 | }, 408 | 'not_arg_blockcontent_startswith': 409 | { 410 | 'name': 'not_arg_blockcontent_startswith', 411 | 'useincommands': ['blocks'], 412 | 'segment': 'filters', 413 | 'datalog': '(not [(clojure.string/starts-with? ?blockcontent "$$ARG1")])', 414 | 'comment': 'Exclude if block content starts with arg1' 415 | }, 416 | 'not_arg_blockcontent_endswith': 417 | { 418 | 'name': 'not_arg_blockcontent_endswith', 419 | 'useincommands': ['blocks'], 420 | 'segment': 'filters', 421 | 'datalog': '(not [(clojure.string/ends-with? ?blockcontent "$$ARG1")])', 422 | 'comment': 'Exclude if block content ends with arg1' 423 | }, 424 | 'not_arg_blockcontent_contains': 425 | { 426 | 'name': 'not_arg_blockcontent_contains', 427 | 'useincommands': ['blocks'], 428 | 'segment': 'filters', 429 | 'datalog': '(not [(clojure.string/includes? ?blockcontent "$$ARG1")])', 430 | 'comment': 'Exclude if block content contains arg1' 431 | }, 432 | 'scheduledbetween': 433 | { 434 | 'name': 'scheduledbetween', 435 | 'useincommands': ['blocks'], 436 | 'segment': 'inputs', 437 | 'datalog': ':inputs [$$ARG1]', 438 | 'comment': 'set the input values for a date range for example could be\n :today :365d-after' 439 | }, 440 | 'deadlinebetween': 441 | { 442 | 'name': 'deadlinebetween', 443 | 'useincommands': ['blocks'], 444 | 'segment': 'inputs', 445 | 'datalog': ':inputs [$$ARG1]', 446 | 'comment': 'set the input values for a date range for example could be\n :today :365d-after' 447 | }, 448 | }, 449 | 'pages-querylines': { 450 | 'original-pagename': 451 | { 452 | 'name': 'original-pagename', 453 | 'useincommands': ['pages'], 454 | 'segment': 'filters', 455 | 'datalog': '[?block :block/original-name ?originalpagename]', 456 | 'comment': 'get original page name (case sensitive) into variable ?originalpagename', 457 | }, 458 | 'pagename': 459 | { 460 | 'name': 'pagename', 461 | 'useincommands': ['pages'], 462 | 'segment': 'filters', 463 | 'datalog': '[?block :block/name ?pagename]', 464 | 'comment': 'get page name (lowercase) from the special page block into variable ?pagename', 465 | }, 466 | 'pagename_is': 467 | { 468 | 'name': 'pagename_is', 469 | 'useincommands': ['pages'], 470 | 'segment': 'filters', 471 | 'datalog': '[?block :block/name "$$ARG1"]', 472 | 'comment': 'Select specific page using the :block/name\nwhich is only present in special blocks that\nhave the page attributes' 473 | }, 474 | 'not_pagename_is': 475 | { 476 | 'name': 'not_pagename_is', 477 | 'useincommands': ['pages'], 478 | 'segment': 'filters', 479 | 'datalog': '(not [?block :block/name "$$ARG1"])', 480 | 'comment': 'Exclude specific page using the :block/name\nwhich is only present in special blocks that\nhave the page attributes' 481 | }, 482 | 'page_is_journal': 483 | { 484 | 'name': 'page_is_journal', 485 | 'useincommands': ['pages'], 486 | 'segment': 'filters', 487 | 'datalog': '[?block :block/journal? true]', 488 | 'comment': 'Select block if it belonhs to a a journal' 489 | }, 490 | 'not_page_is_journal': 491 | { 492 | 'name': 'not_page_is_journal', 493 | 'useincommands': ['pages'], 494 | 'segment': 'filters', 495 | 'datalog': '[?block :block/journal? false]', 496 | 'comment': 'Exclude block if it belonhs to a a journal' 497 | }, 498 | 'journal_date': 499 | { 500 | 'name': 'journal_date', 501 | 'useincommands': ['pages'], 502 | 'segment': 'filters', 503 | 'datalog': '[?block :block/journal-day ?journaldate]', 504 | 'comment': 'get journal date into variable ?journaldate' 505 | }, 506 | 'page_properties_are': 507 | { 508 | 'name': 'page_properties_are', 509 | 'useincommands': ['pages'], 510 | 'segment': 'filters', 511 | 'datalog': '(page-property ?block :$$ARG1 $$ARG2)', 512 | 'comment': 'Select if block is a special page blockand has a single property (arg1) with value arg2' 513 | }, 514 | 'not_page_properties_are': 515 | { 516 | 'name': 'not_page_properties_are', 517 | 'useincommands': ['pages'], 518 | 'segment': 'filters', 519 | 'datalog': '(not (page-property ?block :$$ARG1 $$ARG2))', 520 | 'comment': 'Exclude if block is a special page blockand has a single property (arg1) with value arg2' 521 | }, 522 | 'block_properties_are': 523 | { 524 | 'name': 'block_properties_are', 525 | 'useincommands': ['pages'], 526 | 'segment': 'filters', 527 | 'datalog': '(page-property ?block :$$ARG1 $$ARG2)', 528 | 'comment': 'Select if block has a single property (arg1) with value arg2' 529 | }, 530 | 'not_block_properties_are': 531 | { 532 | 'name': 'not_block_properties_are', 533 | 'useincommands': ['pages'], 534 | 'segment': 'filters', 535 | 'datalog': '(not (property ?block :$$ARG1 $$ARG2))', 536 | 'comment': 'Exclude if block has a single property (arg1) with value arg2' 537 | }, 538 | 'pagetags_are': 539 | { 540 | 'name': 'pagetags_are', 541 | 'useincommands': ['pages'], 542 | 'segment': 'filters', 543 | 'datalog': '(page-tags ?block #{"$$ARG1"})', 544 | 'comment': 'Select special page block with one or more page tags' 545 | }, 546 | 'not_pagetags_are': 547 | { 548 | 'name': 'not_pagetags_are', 549 | 'useincommands': ['pages'], 550 | 'segment': 'filters', 551 | 'datalog': '(not (page-tags ?block #{"$$ARG1"}))', 552 | 'comment': 'Exclude special page block with one or more page tags' 553 | }, 554 | 'blocktags_are': 555 | { 556 | 'name': 'blocktags_are', 557 | 'useincommands': ['blocks'], 558 | 'segment': 'filters', 559 | 'datalog': '(page-tags ?block #{"$$ARG1"})', 560 | 'comment': 'Select block if the page it belongs to\nhas a specific page tag or page link' 561 | }, 562 | 'not_blocktags_are': 563 | { 564 | 'name': 'not_blocktags_are', 565 | 'useincommands': ['blocks'], 566 | 'segment': 'filters', 567 | 'datalog': '(not (page-tags ?block #{"$$ARG1"}))', 568 | 'comment': 'Exclude block if the page it belongs to\nhas a specific page tag or page link' 569 | }, 570 | 'namespace': 571 | { 572 | 'name': 'namespace_pages', 573 | 'useincommands': ['pages'], 574 | 'segment': 'filters', 575 | 'datalog': '(namespace ?block "$$ARG1")', 576 | 'comment': 'Select if special page block is within namespace arg1' 577 | }, 578 | 'not_namespace': 579 | { 580 | 'name': 'not_namespace_pages', 581 | 'useincommands': ['pages'], 582 | 'segment': 'filters', 583 | 'datalog': '(not (namespace ?block "$$ARG1"))', 584 | 'comment': 'Select if special page block is within namespace arg1' 585 | }, 586 | 'arg_pagename_startswith': 587 | { 588 | 'name': 'arg_pagename_startswith', 589 | 'useincommands': ['pages'], 590 | 'segment': 'filters', 591 | 'datalog': '[(clojure.string/starts-with? ?pagename "$$ARG1")]', 592 | 'comment': 'Select if page title starts with arg1' 593 | }, 594 | 'arg_pagename_endswith': 595 | { 596 | 'name': 'arg_pagename_endswith', 597 | 'useincommands': ['pages'], 598 | 'segment': 'filters', 599 | 'datalog': '[(clojure.string/ends-with? ?pagename "$$ARG1")]', 600 | 'comment': 'Select if page title ends with arg1' 601 | }, 602 | 'arg_pagename_contains': 603 | { 604 | 'name': 'arg_pagename_contains', 605 | 'useincommands': ['common'], 606 | 'segment': 'filters', 607 | 'datalog': '[(clojure.string/includes? ?pagename "$$ARG1")]', 608 | 'comment': 'Select if page title contains arg1' 609 | }, 610 | 'not_arg_pagename_startswith': 611 | { 612 | 'name': 'not_arg_pagename_startswith', 613 | 'useincommands': ['common'], 614 | 'segment': 'filters', 615 | 'datalog': '(not [(clojure.string/starts-with? ?pagename "$$ARG1")])', 616 | 'comment': 'Exclude if page title ends with arg1' 617 | }, 618 | 'not_arg_pagename_endswith': 619 | { 620 | 'name': 'not_arg_pagename_endswith', 621 | 'useincommands': ['pages'], 622 | 'segment': 'filters', 623 | 'datalog': '(not [(clojure.string/ends-with? ?pagename "$$ARG1")])', 624 | 'comment': 'Exclude if page title ends with arg1' 625 | }, 626 | 'not_arg_pagename_contains': 627 | { 628 | 'name': 'not_arg_pagename_contains', 629 | 'useincommands': ['pages'], 630 | 'segment': 'filters', 631 | 'datalog': '(not [(clojure.string/includes? ?pagename "$$ARG1")])', 632 | 'comment': 'Exclude if page title contains arg1' 633 | }, 634 | } 635 | } 636 | 637 | // Dictionary of query builder commands 638 | var commandsDict = { 639 | "blocks": { 640 | "querylines": [ 641 | "findblocks", 642 | "where", 643 | "blockcontent", 644 | "page", 645 | "pagename", 646 | ], 647 | "description": "select logseq blocks by wildcards" 648 | }, 649 | "blockproperties": { 650 | "querylines": [ 651 | ], 652 | "description": "select blocks by property values" 653 | }, 654 | "blocktags": { 655 | "querylines": [ 656 | ], 657 | "description": "select blocks by tag" 658 | }, 659 | "deadline": { 660 | "querylines": [ 661 | "deadline" 662 | ], 663 | "description": "select blocks that have a deadline" 664 | }, 665 | "deadlinebetween": { 666 | "querylines": [ 667 | "deadline", 668 | "deadlinefrom", 669 | "deadlineto", 670 | "daterange" 671 | ], 672 | "description": "select blocks that have a deadline in a date range" 673 | }, 674 | "journalsbetween": { 675 | "querylines": [ 676 | "journal_date", 677 | "journalfrom", 678 | "journalto", 679 | "daterange" 680 | ], 681 | "description": "only select journal pages in a date range" 682 | }, 683 | "journalonly": { 684 | "querylines": [ 685 | "page_is_journal" 686 | ], 687 | "description": "only select journal pages" 688 | }, 689 | "namespace": { 690 | "querylines": [ 691 | ], 692 | "description": "select pages or blocks within a namespace" 693 | }, 694 | "pages": { 695 | "querylines": [ 696 | "findblocks", 697 | "where", 698 | "pagename", 699 | ], 700 | "description": "select pages by wildcards" 701 | }, 702 | "pageproperties": { 703 | "querylines": [ 704 | ], 705 | "description": "select pages by page properties" 706 | }, 707 | "pagetags": { 708 | "querylines": [ 709 | "not_page_is_journal" 710 | ], 711 | "description": "select pages by tag" 712 | }, 713 | "pagelinks": { 714 | "querylines": [ 715 | ], 716 | "description": "select any blocks that has links to pages" 717 | }, 718 | "tasks": { 719 | "querylines": [ 720 | 'marker', 721 | ], 722 | "description": "select blocks that have tasks present" 723 | }, 724 | "scheduled": { 725 | "querylines": [ 726 | "scheduled" 727 | ], 728 | "description": "select blocks that are scheduled" 729 | }, 730 | "scheduledbetween": { 731 | "querylines": [ 732 | "scheduled", 733 | "scheduledfrom", 734 | "scheduledto", 735 | "daterange" 736 | ], 737 | "description": "select blocks that are scheduled in a date range" 738 | }, 739 | "collapse": { 740 | "querylines": [ 741 | "collapse_true" 742 | ], 743 | "description": "collapse found blocks" 744 | }, 745 | "expand": { 746 | "querylines": [ 747 | "collapse_false" 748 | ], 749 | "description": "expand found blocks" 750 | }, 751 | "showbreadcrumb": { 752 | "querylines": [ 753 | "breadcrumb_show_true" 754 | ], 755 | "description": "show breadcrumb for found blocks" 756 | }, 757 | "hidebreadcrumb": { 758 | "querylines": [ 759 | "breadcrumb_show_false" 760 | ], 761 | "description": "hide breadcrumb for found blocks" 762 | }, 763 | } 764 | 765 | // Local tests cases for automated test suite 766 | var QueryTestCases = [ 767 | 768 | // test 1 769 | `title: pages command - select all pages 770 | - pages 771 | - * 772 | #+BEGIN_QUERY 773 | { 774 | :title [:b "pages command - select all pages"] 775 | :query [:find (pull ?block [*]) 776 | :where 777 | [?block :block/name ?pagename] 778 | ] 779 | } 780 | #+END_QUERY 781 | `, 782 | 783 | // test 2 784 | `title: pages command - specific pages 785 | - pages 786 | - testpage001 787 | - testpage002 788 | #+BEGIN_QUERY 789 | { 790 | :title [:b "pages command - specific pages"] 791 | :query [:find (pull ?block [*]) 792 | :where 793 | [?block :block/name ?pagename] 794 | ( or 795 | [?block :block/name "testpage001"] 796 | [?block :block/name "testpage002"] 797 | ) 798 | ] 799 | } 800 | #+END_QUERY 801 | `, 802 | 803 | // test 3 804 | `title: pages command - pages by wildcards 805 | - pages 806 | - testpage00* 807 | #+BEGIN_QUERY 808 | { 809 | :title [:b "pages command - pages by wildcards"] 810 | :query [:find (pull ?block [*]) 811 | :where 812 | [?block :block/name ?pagename] 813 | [(clojure.string/starts-with? ?pagename "testpage00")] 814 | ] 815 | } 816 | #+END_QUERY 817 | `, 818 | 819 | // test 4 820 | `title: pages command - pages by wildcards 821 | - pages 822 | - *002 823 | #+BEGIN_QUERY 824 | { 825 | :title [:b "pages command - pages by wildcards"] 826 | :query [:find (pull ?block [*]) 827 | :where 828 | [?block :block/name ?pagename] 829 | [(clojure.string/ends-with? ?pagename "002")] 830 | ] 831 | } 832 | #+END_QUERY 833 | `, 834 | 835 | // test 5 836 | `title: pages command - pages by wildcards 837 | - pages 838 | - *page00* 839 | #+BEGIN_QUERY 840 | { 841 | :title [:b "pages command - pages by wildcards"] 842 | :query [:find (pull ?block [*]) 843 | :where 844 | [?block :block/name ?pagename] 845 | [(clojure.string/includes? ?pagename "page00")] 846 | ] 847 | } 848 | #+END_QUERY 849 | `, 850 | 851 | // test 6 852 | `title: pages command - ignore pages (including wildcards) 853 | - pages 854 | - not testpage* 855 | - not Queries* 856 | #+BEGIN_QUERY 857 | { 858 | :title [:b "pages command - ignore pages (including wildcards)"] 859 | :query [:find (pull ?block [*]) 860 | :where 861 | [?block :block/name ?pagename] 862 | (not [(clojure.string/starts-with? ?pagename "testpage")]) 863 | (not [(clojure.string/starts-with? ?pagename "Queries")]) 864 | ] 865 | } 866 | #+END_QUERY 867 | `, 868 | 869 | // test 7 870 | `title: blocks command - ignore blocks using wildcards 871 | - blocks 872 | - not And sir dare view* 873 | - not *here leave merit enjoy forth. 874 | - not *roof gutters* 875 | #+BEGIN_QUERY 876 | { 877 | :title [:b "blocks command - ignore blocks using wildcards"] 878 | :query [:find (pull ?block [*]) 879 | :where 880 | [?block :block/content ?blockcontent] 881 | [?block :block/page ?page] 882 | [?page :block/name ?pagename] 883 | (not [(clojure.string/ends-with? ?blockcontent "And sir dare view")]) 884 | (not [(clojure.string/starts-with? ?blockcontent "here leave merit enjoy forth.")]) 885 | (not [(clojure.string/includes? ?blockcontent "roof gutters")]) 886 | ] 887 | } 888 | #+END_QUERY 889 | `, 890 | 891 | // test 8 892 | `title: blocktags - select and exclude block level tags 893 | - blocks 894 | - * 895 | - blocktags 896 | - tagA 897 | - tagD 898 | - not tagB 899 | #+BEGIN_QUERY 900 | { 901 | :title [:b "blocktags - select and exclude block level tags"] 902 | :query [:find (pull ?block [*]) 903 | :where 904 | [?block :block/content ?blockcontent] 905 | [?block :block/page ?page] 906 | [?page :block/name ?pagename] 907 | ( or 908 | (page-ref ?block "taga") 909 | (page-ref ?block "tagd") 910 | ) 911 | (not (page-ref ?block "tagb")) 912 | ] 913 | } 914 | #+END_QUERY 915 | `, 916 | `title: blocktags and pages don't mix 917 | - pages 918 | - testpage00* 919 | - blocktags 920 | - tagA 921 | - not tagB 922 | #+BEGIN_QUERY 923 | 924 | ;; **ERROR: blocktags not valid with pages command use blocks command instead 925 | 926 | { 927 | :title [:b "blocktags and pages don't mix"] 928 | :query [:find (pull ?block [*]) 929 | :where 930 | [?block :block/name ?pagename] 931 | [(clojure.string/starts-with? ?pagename "testpage00")] 932 | ] 933 | } 934 | #+END_QUERY 935 | `, 936 | 937 | // test 10 938 | `title: pagetags - page level tags 939 | - pages 940 | - testpage* 941 | - pagetags 942 | - classA 943 | #+BEGIN_QUERY 944 | { 945 | :title [:b "pagetags - page level tags"] 946 | :query [:find (pull ?block [*]) 947 | :where 948 | [?block :block/name ?pagename] 949 | [(clojure.string/starts-with? ?pagename "testpage")] 950 | [?block :block/journal? false] 951 | (page-tags ?block #{"classa"}) 952 | ] 953 | } 954 | #+END_QUERY 955 | `, 956 | 957 | // test 11 958 | `title: pagetags and pages 959 | - pages 960 | - *dynamics* 961 | - pagetags 962 | - classB 963 | #+BEGIN_QUERY 964 | { 965 | :title [:b "pagetags and pages"] 966 | :query [:find (pull ?block [*]) 967 | :where 968 | [?block :block/name ?pagename] 969 | [(clojure.string/includes? ?pagename "dynamics")] 970 | [?block :block/journal? false] 971 | (page-tags ?block #{"classb"}) 972 | ] 973 | } 974 | #+END_QUERY 975 | `, 976 | 977 | // test 12 978 | `title: select and exclude task types 979 | - tasks 980 | - TODO 981 | - not DOING 982 | #+BEGIN_QUERY 983 | ;; WARNING: Must have 'pages' command or 'blocks' Command 984 | ;; otherwise the query cannot get any information 985 | ;; Inserting a blocks command for you 986 | 987 | { 988 | :title [:b "select and exclude task types"] 989 | :query [:find (pull ?block [*]) 990 | :where 991 | [?block :block/content ?blockcontent] 992 | [?block :block/page ?page] 993 | [?page :block/name ?pagename] 994 | [?block :block/marker ?marker] 995 | [(contains? #{"TODO"} ?marker)] 996 | (not [(contains? #{"DOING"} ?marker)]) 997 | ] 998 | } 999 | #+END_QUERY 1000 | `, 1001 | 1002 | `title: select and exclude task types 1003 | - pages 1004 | - testpage00* 1005 | - tasks 1006 | - TODO 1007 | - not DOING 1008 | #+BEGIN_QUERY 1009 | 1010 | ;; **ERROR: tasks not valid with pages command use blocks command instead 1011 | 1012 | { 1013 | :title [:b "select and exclude task types"] 1014 | :query [:find (pull ?block [*]) 1015 | :where 1016 | [?block :block/name ?pagename] 1017 | [(clojure.string/starts-with? ?pagename "testpage00")] 1018 | ] 1019 | } 1020 | #+END_QUERY 1021 | `, 1022 | 1023 | // test 14 1024 | `title: select and exclude pages with page properties 1025 | - pages 1026 | - * 1027 | - pageproperties 1028 | - pagetype, "p-major" 1029 | - pagetype, "p-minor" 1030 | - not pagetype, "p-advanced" 1031 | #+BEGIN_QUERY 1032 | { 1033 | :title [:b "select and exclude pages with page properties"] 1034 | :query [:find (pull ?block [*]) 1035 | :where 1036 | [?block :block/name ?pagename] 1037 | ( or 1038 | (page-property ?block :pagetype "p-major") 1039 | (page-property ?block :pagetype "p-minor") 1040 | ) 1041 | (not (page-property ?block :pagetype "p-advanced")) 1042 | ] 1043 | } 1044 | #+END_QUERY 1045 | `, 1046 | 1047 | // test 15 1048 | `title: select and exclude blocks with block properties 1049 | - blocks 1050 | - * 1051 | - blockproperties 1052 | - category, "b-thriller" 1053 | - category, "b-western" 1054 | - grade, "b-fiction" 1055 | #+BEGIN_QUERY 1056 | { 1057 | :title [:b "select and exclude blocks with block properties"] 1058 | :query [:find (pull ?block [*]) 1059 | :where 1060 | [?block :block/content ?blockcontent] 1061 | [?block :block/page ?page] 1062 | [?page :block/name ?pagename] 1063 | ( or 1064 | (property ?block :category "b-thriller") 1065 | (property ?block :category "b-western") 1066 | (property ?block :grade "b-fiction") 1067 | ) 1068 | ] 1069 | } 1070 | #+END_QUERY 1071 | `, 1072 | 1073 | // test 16 1074 | `title: only search pages in specific namespace 1075 | - pages 1076 | - * 1077 | - namespace 1078 | - physics 1079 | #+BEGIN_QUERY 1080 | { 1081 | :title [:b "only search pages in specific namespace"] 1082 | :query [:find (pull ?block [*]) 1083 | :where 1084 | [?block :block/name ?pagename] 1085 | (namespace ?block "physics") 1086 | ] 1087 | } 1088 | #+END_QUERY 1089 | `, 1090 | 1091 | // test 17 1092 | `title: find block properties in a namespace 1093 | - blocks 1094 | - * 1095 | - namespace 1096 | - tech/python 1097 | - blockproperties 1098 | - grade, "b-fiction" 1099 | #+BEGIN_QUERY 1100 | { 1101 | :title [:b "find block properties in a namespace"] 1102 | :query [:find (pull ?block [*]) 1103 | :where 1104 | [?block :block/content ?blockcontent] 1105 | [?block :block/page ?page] 1106 | [?page :block/name ?pagename] 1107 | (namespace ?page "tech/python") 1108 | (property ?block :grade "b-fiction") 1109 | ] 1110 | } 1111 | #+END_QUERY 1112 | `, 1113 | 1114 | // test 18 1115 | `title: find scheduled blocks in a namespace 1116 | - blocks 1117 | - * 1118 | - namespace 1119 | - physics 1120 | - scheduled 1121 | #+BEGIN_QUERY 1122 | { 1123 | :title [:b "find scheduled blocks in a namespace"] 1124 | :query [:find (pull ?block [*]) 1125 | :where 1126 | [?block :block/content ?blockcontent] 1127 | [?block :block/page ?page] 1128 | [?page :block/name ?pagename] 1129 | (namespace ?page "physics") 1130 | [?block :block/scheduled ?scheduleddate] 1131 | ] 1132 | } 1133 | #+END_QUERY 1134 | `, 1135 | 1136 | // test 19 1137 | `title: scheduled - find scheduled blocks in a date range 1138 | - blocks 1139 | - * 1140 | - scheduledbetween 1141 | - :720d-before :700d-after 1142 | #+BEGIN_QUERY 1143 | { 1144 | :title [:b "scheduled - find scheduled blocks in a date range"] 1145 | :query [:find (pull ?block [*]) 1146 | :in $ ?startdate ?enddate 1147 | :where 1148 | [?block :block/content ?blockcontent] 1149 | [?block :block/page ?page] 1150 | [?page :block/name ?pagename] 1151 | [?block :block/scheduled ?scheduleddate] 1152 | [(>= ?scheduleddate ?startdate)] 1153 | [(<= ?scheduleddate ?enddate)] 1154 | ] 1155 | :inputs [:720d-before :700d-after] 1156 | } 1157 | #+END_QUERY 1158 | `, 1159 | 1160 | // test 20 1161 | `title: find blocks with deadlines 1162 | - blocks 1163 | - * 1164 | - deadline 1165 | #+BEGIN_QUERY 1166 | { 1167 | :title [:b "find blocks with deadlines"] 1168 | :query [:find (pull ?block [*]) 1169 | :where 1170 | [?block :block/content ?blockcontent] 1171 | [?block :block/page ?page] 1172 | [?page :block/name ?pagename] 1173 | [?block :block/deadline ?deadlinedate] 1174 | ] 1175 | } 1176 | #+END_QUERY 1177 | `, 1178 | 1179 | // test 21 1180 | `title: find blocks with deadlines in a date range 1181 | - blocks 1182 | - * 1183 | - deadlinebetween 1184 | - :120d-before :30d-after 1185 | #+BEGIN_QUERY 1186 | { 1187 | :title [:b "find blocks with deadlines in a date range"] 1188 | :query [:find (pull ?block [*]) 1189 | :in $ ?startdate ?enddate 1190 | :where 1191 | [?block :block/content ?blockcontent] 1192 | [?block :block/page ?page] 1193 | [?page :block/name ?pagename] 1194 | [?block :block/deadline ?deadlinedate] 1195 | [(>= ?deadlinedate ?startdate)] 1196 | [(<= ?deadlinedate ?enddate)] 1197 | ] 1198 | :inputs [:120d-before :30d-after] 1199 | } 1200 | #+END_QUERY 1201 | `, 1202 | 1203 | // test 22 1204 | `title: find journals 1205 | - pages 1206 | - * 1207 | - journalonly 1208 | #+BEGIN_QUERY 1209 | { 1210 | :title [:b "find journals"] 1211 | :query [:find (pull ?block [*]) 1212 | :where 1213 | [?block :block/name ?pagename] 1214 | [?block :block/journal? true] 1215 | ] 1216 | } 1217 | #+END_QUERY 1218 | `, 1219 | 1220 | // test 23 1221 | `title: find journal in a date range 1222 | - pages 1223 | - * 1224 | - journalsbetween 1225 | - :today :30d-after 1226 | #+BEGIN_QUERY 1227 | { 1228 | :title [:b "find journal in a date range"] 1229 | :query [:find (pull ?block [*]) 1230 | :in $ ?startdate ?enddate 1231 | :where 1232 | [?block :block/name ?pagename] 1233 | [?block :block/journal-day ?journaldate] 1234 | [(>= ?journaldate ?startdate)] 1235 | [(<= ?journaldate ?enddate)] 1236 | ] 1237 | :inputs [:today :30d-after] 1238 | } 1239 | #+END_QUERY 1240 | `, 1241 | 1242 | // test 24 1243 | `title: find journals between dates 1244 | - blocks 1245 | - * 1246 | - journalsbetween 1247 | - :30d-before :today 1248 | #+BEGIN_QUERY 1249 | { 1250 | :title [:b "find journals between dates"] 1251 | :query [:find (pull ?block [*]) 1252 | :in $ ?startdate ?enddate 1253 | :where 1254 | [?block :block/content ?blockcontent] 1255 | [?block :block/page ?page] 1256 | [?page :block/name ?pagename] 1257 | [?page :block/journal-day ?journaldate] 1258 | [(>= ?journaldate ?startdate)] 1259 | [(<= ?journaldate ?enddate)] 1260 | ] 1261 | :inputs [:30d-before :today] 1262 | } 1263 | #+END_QUERY 1264 | `, 1265 | 1266 | // test 25 1267 | `title: collapse results 1268 | - pages 1269 | - testpage00* 1270 | - collapse 1271 | #+BEGIN_QUERY 1272 | { 1273 | :title [:b "collapse results"] 1274 | :query [:find (pull ?block [*]) 1275 | :where 1276 | [?block :block/name ?pagename] 1277 | [(clojure.string/starts-with? ?pagename "testpage00")] 1278 | ] 1279 | :collapsed? true 1280 | } 1281 | #+END_QUERY 1282 | `, 1283 | 1284 | // test 26 1285 | `title: expand results 1286 | - pages 1287 | - testpage00* 1288 | - expand 1289 | #+BEGIN_QUERY 1290 | { 1291 | :title [:b "expand results"] 1292 | :query [:find (pull ?block [*]) 1293 | :where 1294 | [?block :block/name ?pagename] 1295 | [(clojure.string/starts-with? ?pagename "testpage00")] 1296 | ] 1297 | :collapsed? false 1298 | } 1299 | #+END_QUERY 1300 | `, 1301 | 1302 | // test 27 1303 | `title: show breadcrumbs 1304 | - pages 1305 | - testpage00* 1306 | - showbreadcrumb 1307 | #+BEGIN_QUERY 1308 | { 1309 | :title [:b "show breadcrumbs"] 1310 | :query [:find (pull ?block [*]) 1311 | :where 1312 | [?block :block/name ?pagename] 1313 | [(clojure.string/starts-with? ?pagename "testpage00")] 1314 | ] 1315 | :breadcrumb-show? true 1316 | } 1317 | #+END_QUERY 1318 | `, 1319 | 1320 | // test 28 1321 | `title: hide breadcrumbs 1322 | - pages 1323 | - testpage00* 1324 | - hidebreadcrumb 1325 | #+BEGIN_QUERY 1326 | { 1327 | :title [:b "hide breadcrumbs"] 1328 | :query [:find (pull ?block [*]) 1329 | :where 1330 | [?block :block/name ?pagename] 1331 | [(clojure.string/starts-with? ?pagename "testpage00")] 1332 | ] 1333 | :breadcrumb-show? false 1334 | } 1335 | #+END_QUERY 1336 | `, 1337 | 1338 | // test 29 1339 | `title: find journal in a date range using blocks 1340 | - blocks 1341 | - * 1342 | - journalsbetween 1343 | - :today :30d-after 1344 | #+BEGIN_QUERY 1345 | { 1346 | :title [:b "find journal in a date range using blocks"] 1347 | :query [:find (pull ?block [*]) 1348 | :in $ ?startdate ?enddate 1349 | :where 1350 | [?block :block/content ?blockcontent] 1351 | [?block :block/page ?page] 1352 | [?page :block/name ?pagename] 1353 | [?page :block/journal-day ?journaldate] 1354 | [(>= ?journaldate ?startdate)] 1355 | [(<= ?journaldate ?enddate)] 1356 | ] 1357 | :inputs [:today :30d-after] 1358 | } 1359 | #+END_QUERY 1360 | `, 1361 | // test 30 1362 | `title: All blocks - test access to parent pages tags, journal 1363 | - blocks 1364 | - * 1365 | - tasks 1366 | - TODO 1367 | - pagetags 1368 | - classC 1369 | #+BEGIN_QUERY 1370 | { 1371 | :title [:b "All blocks - test access to parent pages tags, journal"] 1372 | :query [:find (pull ?block [*]) 1373 | :where 1374 | [?block :block/content ?blockcontent] 1375 | [?block :block/page ?page] 1376 | [?page :block/name ?pagename] 1377 | [?block :block/marker ?marker] 1378 | [(contains? #{"TODO"} ?marker)] 1379 | [?page :block/journal? false] 1380 | (page-tags ?page #{"classc"}) 1381 | ] 1382 | } 1383 | #+END_QUERY 1384 | `, 1385 | // test 31 1386 | `title: Pages only - Access page properties 1387 | - pages 1388 | - * 1389 | - pageproperties 1390 | - pagetype, "p-minor" 1391 | #+BEGIN_QUERY 1392 | { 1393 | :title [:b "Pages only - Access page properties"] 1394 | :query [:find (pull ?block [*]) 1395 | :where 1396 | [?block :block/name ?pagename] 1397 | (page-property ?block :pagetype "p-minor") 1398 | ] 1399 | } 1400 | #+END_QUERY 1401 | `, 1402 | // test 32 1403 | `title: Pages only - access multiple property values 1404 | - pages 1405 | - * 1406 | - pageproperties 1407 | - pagetype, "p-minor" 1408 | - pagetype, "p-major" 1409 | #+BEGIN_QUERY 1410 | { 1411 | :title [:b "Pages only - access multiple property values"] 1412 | :query [:find (pull ?block [*]) 1413 | :where 1414 | [?block :block/name ?pagename] 1415 | ( or 1416 | (page-property ?block :pagetype "p-minor") 1417 | (page-property ?block :pagetype "p-major") 1418 | ) 1419 | ] 1420 | } 1421 | #+END_QUERY 1422 | `, 1423 | // test 33 1424 | `title: Page blocks only - pagetags 1425 | - pages 1426 | - * 1427 | - pagetags 1428 | - classC 1429 | #+BEGIN_QUERY 1430 | { 1431 | :title [:b "Page blocks only - pagetags"] 1432 | :query [:find (pull ?block [*]) 1433 | :where 1434 | [?block :block/name ?pagename] 1435 | [?block :block/journal? false] 1436 | (page-tags ?block #{"classc"}) 1437 | ] 1438 | } 1439 | #+END_QUERY 1440 | `, 1441 | // test 34 1442 | `title: Pages only - select all pages 1443 | - pages 1444 | - * 1445 | #+BEGIN_QUERY 1446 | { 1447 | :title [:b "Pages only - select all pages"] 1448 | :query [:find (pull ?block [*]) 1449 | :where 1450 | [?block :block/name ?pagename] 1451 | ] 1452 | } 1453 | #+END_QUERY 1454 | `, 1455 | // test 35 1456 | `title: pages command - specific pages 1457 | - pages 1458 | - testpage001 1459 | - testpage002 1460 | #+BEGIN_QUERY 1461 | { 1462 | :title [:b "pages command - specific pages"] 1463 | :query [:find (pull ?block [*]) 1464 | :where 1465 | [?block :block/name ?pagename] 1466 | ( or 1467 | [?block :block/name "testpage001"] 1468 | [?block :block/name "testpage002"] 1469 | ) 1470 | ] 1471 | } 1472 | #+END_QUERY 1473 | `, 1474 | // test 36 1475 | `title: pages command - pages by wildcards 1476 | - pages 1477 | - testpage00* 1478 | #+BEGIN_QUERY 1479 | { 1480 | :title [:b "pages command - pages by wildcards"] 1481 | :query [:find (pull ?block [*]) 1482 | :where 1483 | [?block :block/name ?pagename] 1484 | [(clojure.string/starts-with? ?pagename "testpage00")] 1485 | ] 1486 | } 1487 | #+END_QUERY 1488 | `, 1489 | // test 37 1490 | `title: pages command - pages by wildcards 1491 | - pages 1492 | - *002 1493 | #+BEGIN_QUERY 1494 | { 1495 | :title [:b "pages command - pages by wildcards"] 1496 | :query [:find (pull ?block [*]) 1497 | :where 1498 | [?block :block/name ?pagename] 1499 | [(clojure.string/ends-with? ?pagename "002")] 1500 | ] 1501 | } 1502 | #+END_QUERY 1503 | `, 1504 | // test 38 1505 | `title: journalsbetween defaulting to blocks retrieval 1506 | - journalsbetween 1507 | - :today :30d-after 1508 | #+BEGIN_QUERY 1509 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1510 | ;; otherwise the query cannot get any information 1511 | ;; Inserting a blocks command for you 1512 | 1513 | { 1514 | :title [:b "journalsbetween defaulting to blocks retrieval"] 1515 | :query [:find (pull ?block [*]) 1516 | :in $ ?startdate ?enddate 1517 | :where 1518 | [?block :block/content ?blockcontent] 1519 | [?block :block/page ?page] 1520 | [?page :block/name ?pagename] 1521 | [?page :block/journal-day ?journaldate] 1522 | [(>= ?journaldate ?startdate)] 1523 | [(<= ?journaldate ?enddate)] 1524 | ] 1525 | :inputs [:today :30d-after] 1526 | } 1527 | #+END_QUERY 1528 | `, 1529 | // test 39 1530 | `title: journalsbetween using pages retrieval 1531 | - pages 1532 | - * 1533 | - journalsbetween 1534 | - :today :30d-after 1535 | #+BEGIN_QUERY 1536 | { 1537 | :title [:b "journalsbetween using pages retrieval"] 1538 | :query [:find (pull ?block [*]) 1539 | :in $ ?startdate ?enddate 1540 | :where 1541 | [?block :block/name ?pagename] 1542 | [?block :block/journal-day ?journaldate] 1543 | [(>= ?journaldate ?startdate)] 1544 | [(<= ?journaldate ?enddate)] 1545 | ] 1546 | :inputs [:today :30d-after] 1547 | } 1548 | #+END_QUERY 1549 | `, 1550 | // test 40 1551 | `title: page property combinations using and and or 1552 | - pages 1553 | - * 1554 | - pageproperties 1555 | - pagecategory, "p-minor" 1556 | - or pagecategory, "p-minimum" 1557 | - and pagetype, "p-type1" 1558 | #+BEGIN_QUERY 1559 | { 1560 | :title [:b "page property combinations using and and or"] 1561 | :query [:find (pull ?block [*]) 1562 | :where 1563 | [?block :block/name ?pagename] 1564 | ( or 1565 | (page-property ?block :pagecategory "p-minor") 1566 | (page-property ?block :pagecategory "p-minimum") 1567 | ) 1568 | (page-property ?block :pagetype "p-type1") 1569 | ] 1570 | } 1571 | #+END_QUERY 1572 | `, 1573 | // test 41 1574 | `title: page tag combinations using and and or 1575 | - pages 1576 | - * 1577 | - pagetags 1578 | - classA 1579 | - or classB 1580 | - and classH 1581 | #+BEGIN_QUERY 1582 | { 1583 | :title [:b "page tag combinations using and and or"] 1584 | :query [:find (pull ?block [*]) 1585 | :where 1586 | [?block :block/name ?pagename] 1587 | [?block :block/journal? false] 1588 | ( or 1589 | (page-tags ?block #{"classa"}) 1590 | (page-tags ?block #{"classb"}) 1591 | ) 1592 | (page-tags ?block #{"classh"}) 1593 | ] 1594 | } 1595 | #+END_QUERY 1596 | `, 1597 | // test 42 1598 | `title: block property combinations using and and or 1599 | - blocks 1600 | - * 1601 | - blockproperties 1602 | - category, "b-fiction" 1603 | - or grade, "b-western" 1604 | - and category, "b-travel" 1605 | #+BEGIN_QUERY 1606 | { 1607 | :title [:b "block property combinations using and and or"] 1608 | :query [:find (pull ?block [*]) 1609 | :where 1610 | [?block :block/content ?blockcontent] 1611 | [?block :block/page ?page] 1612 | [?page :block/name ?pagename] 1613 | ( or 1614 | (property ?block :category "b-fiction") 1615 | (property ?block :grade "b-western") 1616 | ) 1617 | (property ?block :category "b-travel") 1618 | ] 1619 | } 1620 | #+END_QUERY 1621 | `, 1622 | // test 43 1623 | `title: block tag combinations using and and or 1624 | - blocks 1625 | - * 1626 | - blocktags 1627 | - tagA 1628 | - or tagB 1629 | - and tagD 1630 | #+BEGIN_QUERY 1631 | { 1632 | :title [:b "block tag combinations using and and or"] 1633 | :query [:find (pull ?block [*]) 1634 | :where 1635 | [?block :block/content ?blockcontent] 1636 | [?block :block/page ?page] 1637 | [?page :block/name ?pagename] 1638 | ( or 1639 | (page-ref ?block "taga") 1640 | (page-ref ?block "tagb") 1641 | ) 1642 | (page-ref ?block "tagd") 1643 | ] 1644 | } 1645 | #+END_QUERY 1646 | `, 1647 | // test 44 1648 | `title: task and or combintions 1649 | - blocks 1650 | - * 1651 | - tasks 1652 | - TODO 1653 | - and WAITING 1654 | - or LATER 1655 | - not DOING 1656 | #+BEGIN_QUERY 1657 | { 1658 | :title [:b "task and or combintions"] 1659 | :query [:find (pull ?block [*]) 1660 | :where 1661 | [?block :block/content ?blockcontent] 1662 | [?block :block/page ?page] 1663 | [?page :block/name ?pagename] 1664 | [?block :block/marker ?marker] 1665 | ( or 1666 | [(contains? #{"TODO"} ?marker)] 1667 | [(contains? #{"LATER"} ?marker)] 1668 | ) 1669 | [(contains? #{"WAITING"} ?marker)] 1670 | (not [(contains? #{"DOING"} ?marker)]) 1671 | ] 1672 | } 1673 | #+END_QUERY 1674 | `, 1675 | // test 45 1676 | `title: select blocks with links to pages 1677 | - blocks 1678 | - * 1679 | - pagelinks 1680 | - gardening 1681 | - vegetables 1682 | - not turnips 1683 | #+BEGIN_QUERY 1684 | { 1685 | :title [:b "select blocks with links to pages"] 1686 | :query [:find (pull ?block [*]) 1687 | :where 1688 | [?block :block/content ?blockcontent] 1689 | [?block :block/page ?page] 1690 | [?page :block/name ?pagename] 1691 | ( or 1692 | [?block :block/path-refs [:block/name "gardening"]] 1693 | [?block :block/path-refs [:block/name "vegetables"]] 1694 | ) 1695 | (not [?block :block/path-refs [:block/name "turnips"]]) 1696 | ] 1697 | } 1698 | #+END_QUERY 1699 | `, 1700 | // test 46 1701 | `title: select blocks with links to journals 1702 | - blocks 1703 | - * 1704 | - pagelinks 1705 | - Dec 25th, 2022 1706 | - Jan 1st, 2019 1707 | #+BEGIN_QUERY 1708 | { 1709 | :title [:b "select blocks with links to journals"] 1710 | :query [:find (pull ?block [*]) 1711 | :where 1712 | [?block :block/content ?blockcontent] 1713 | [?block :block/page ?page] 1714 | [?page :block/name ?pagename] 1715 | ( or 1716 | [?block :block/path-refs [:block/name "dec 25th, 2022"]] 1717 | [?block :block/path-refs [:block/name "jan 1st, 2019"]] 1718 | ) 1719 | ] 1720 | } 1721 | #+END_QUERY 1722 | ` 1723 | 1724 | ] 1725 | 1726 | function test_queryDBRead(section, key) { 1727 | try { 1728 | read1 = querylineDict[section][key]['name'] 1729 | read2 = querylineDict[section][key]['segment'] 1730 | read3 = querylineDict[section][key]['segment'] 1731 | read4 = querylineDict[section][key]['datalog'] 1732 | read5 = querylineDict[section][key]['comment'] 1733 | // return read1 1734 | return read1 + '\n' + read2 + '\n' + read3 + '\n' + read4 + '\n' + read5 + '\n' 1735 | 1736 | } catch { 1737 | return "Failed" 1738 | } 1739 | } 1740 | 1741 | 1742 | 1743 | 1744 | function add(num1, num2) { 1745 | return num1 + num2 1746 | } 1747 | 1748 | 1749 | // ========= global variables 1750 | 1751 | // ---query structure 1752 | var query_template = { 1753 | "start": [], 1754 | "errors": [], 1755 | "open": [], 1756 | "title": [], 1757 | "query": [], 1758 | "in": [], 1759 | "where": [], 1760 | "filters": [], 1761 | "closefind": [], 1762 | "inputs": [], 1763 | "view": [], 1764 | "options": [], 1765 | "closequery": [], 1766 | "end": [] 1767 | } 1768 | 1769 | var codeblock = false 1770 | var mode = "javascript" 1771 | query = query_template 1772 | querygroup = "pages-querylines" 1773 | querylineDBDict = {} 1774 | showcommandcomments = false 1775 | 1776 | // =========== FUNCTIONS ================ 1777 | 1778 | function initialiseQuery() { 1779 | query = query_template; 1780 | for (key of Object.keys(query)) { 1781 | query[key] = [] 1782 | } 1783 | return query; 1784 | } 1785 | 1786 | function initialisteQueryLineDict() { 1787 | var tempDict; 1788 | tempDict = {}; 1789 | tempDict["pages-querylines"] = querylineDict["pages-querylines"]; 1790 | tempDict["blocks-querylines"] = querylineDict["blocks-querylines"]; 1791 | 1792 | for (const [commonqueryline, value] of Object.entries(querylineDict['common-querylines'])) { 1793 | tempDict["pages-querylines"][commonqueryline] = querylineDict["common-querylines"][commonqueryline]; 1794 | tempDict["blocks-querylines"][commonqueryline] = querylineDict["common-querylines"][commonqueryline]; 1795 | } 1796 | return tempDict; 1797 | } 1798 | 1799 | 1800 | 1801 | function getQueryLineSegment(querylinekey) { 1802 | var errormsg, errortext; 1803 | 1804 | try { 1805 | return ["ok", querylineDBDict[querygroup][querylinekey]["segment"]]; 1806 | } catch (e) { 1807 | errortext = querylinekey + " segment value not found in QueryDB\n"; 1808 | errormsg = ";; **ERROR: " + errortext + "\n"; 1809 | return ["error", errormsg]; 1810 | } 1811 | } 1812 | 1813 | 1814 | function getQueryLine(querylinekey, querysegment) { 1815 | var errormsg; 1816 | 1817 | try { 1818 | if (showcommandcomments === true) { 1819 | var comment = getQueryLineComment(querylinekey) 1820 | if (comment != '') { 1821 | if (!query[querysegment].includes('\n' + comment)) { 1822 | query[querysegment].push("\n" + getQueryLineComment(querylinekey)); 1823 | } 1824 | } 1825 | } 1826 | 1827 | return querylineDBDict[querygroup][querylinekey]["datalog"]; 1828 | } catch (e) { 1829 | errormsg = "\n---- COMMAND ERROR ----\n"; 1830 | errormsg += querylinekey + " not found in QueryDB or invalid usage\n"; 1831 | 1832 | if (querygroup === "pages-querylines") { 1833 | errormsg += querylinekey + " invalid within a 'pages' command query"; 1834 | } 1835 | 1836 | if (querygroup === "blocks-querylines") { 1837 | errormsg += querylinekey + " invalid within a 'blocks' command query"; 1838 | } 1839 | 1840 | errormsg += "\n----------------------\n"; 1841 | return errormsg; 1842 | } 1843 | } 1844 | 1845 | function getQueryLineComment(querylinekey) { 1846 | var comment, separator; 1847 | separator = '' 1848 | try { 1849 | comment = querylineDBDict[querygroup][querylinekey]["comment"]; 1850 | if (comment == '') { 1851 | return '' 1852 | } 1853 | return separator + ";; ---- " + comment; 1854 | } catch (e) { 1855 | return querylinekey + " not found in queryline Dictionary"; 1856 | } 1857 | } 1858 | 1859 | function getCommandQueryLineKeys(command) { 1860 | var commandlist; 1861 | 1862 | if (command === null) { 1863 | return null; 1864 | } 1865 | 1866 | try { 1867 | commandlist = commandsDict[command]["querylines"]; 1868 | } catch (e) { 1869 | return null; 1870 | } 1871 | 1872 | return commandlist; 1873 | } 1874 | 1875 | 1876 | function processCommand(command, commandLinesDict) { 1877 | var commandline, commandvalidity, negativecommandlines, originalcommandlines, positivecommandlines, queryline, querylinekeys, querysegment, querysegmentdata, querysegmentresponse; 1878 | commandvalidity = checkCommandValid(command); 1879 | 1880 | if (commandvalidity[0] === false) { 1881 | query["errors"].push(commandvalidity[1]); 1882 | return; 1883 | } 1884 | 1885 | querylinekeys = getCommandQueryLineKeys(command); 1886 | 1887 | if (querylinekeys === null) { 1888 | return [command + " is not a valid command"]; 1889 | } 1890 | 1891 | for (value of querylinekeys) { 1892 | querysegmentresponse = getQueryLineSegment(value); 1893 | 1894 | querysegmentdata = querysegmentresponse[1]; 1895 | 1896 | if (querysegmentresponse[0] === "error") { 1897 | query["errors"].push(querysegmentdata); 1898 | continue; 1899 | } 1900 | 1901 | querysegment = querysegmentdata; 1902 | queryline = getQueryLine(value, querysegment); 1903 | 1904 | if (query[querysegment].includes(queryline)) { 1905 | continue; 1906 | } 1907 | 1908 | query[querysegment].push(queryline); 1909 | } 1910 | 1911 | // sort the argument lines by positive and negative 1912 | positivecommandlines = []; 1913 | negativecommandlines = []; 1914 | originalcommandlines = commandLinesDict[command]["commandlines"].slice(1); 1915 | 1916 | 1917 | for (commandline of originalcommandlines) { 1918 | if (commandline.trim().substring(2).startsWith("not ") || commandline.trim().substring(2).startsWith("and ")) { 1919 | commandline = commandline.replace("and ", ""); 1920 | negativecommandlines.push(commandline); 1921 | } else { 1922 | if (commandline.trim().substring(2).startsWith("or ")) { 1923 | commandline = commandline.replace("or ", ""); 1924 | positivecommandlines.push(commandline); 1925 | } else { 1926 | positivecommandlines.push(commandline); 1927 | } 1928 | } 1929 | } 1930 | 1931 | if (positivecommandlines.length > 0) { 1932 | processCommandLines("include", command, positivecommandlines); 1933 | } 1934 | 1935 | if (negativecommandlines.length > 0) { 1936 | processCommandLines("exclude", command, negativecommandlines); 1937 | } 1938 | 1939 | return; 1940 | } 1941 | 1942 | function checkCommandValid(command) { 1943 | var commandvalidity, errormessage; 1944 | commandvalidity = true; 1945 | errormessage = ""; 1946 | 1947 | if (querygroup === "pages-querylines") { 1948 | if (command === "blocktags") { 1949 | commandvalidity = false; 1950 | errormessage = "\n;; **ERROR: " + command + " not valid with pages command use blocks command instead\n"; 1951 | } else { 1952 | if (command === "tasks" || command === "pagelinks") { 1953 | commandvalidity = false; 1954 | errormessage = "\n;; **ERROR: " + command + " not valid with pages command use blocks command instead\n"; 1955 | } 1956 | } 1957 | } else { 1958 | if (querygroup === "blocks-querylines") { 1959 | if ([].includes(command)) { 1960 | commandvalidity = false; 1961 | } 1962 | } 1963 | } 1964 | 1965 | return [commandvalidity, errormessage]; 1966 | } 1967 | 1968 | function addQueryLines(command, prefix, querylinekey, arg) { 1969 | var arg1, arg2, args, querysegment, querysegmentdata, querysegmentresponse, updatedqueryline; 1970 | querysegment = getQueryLineSegment(querylinekey); 1971 | querysegmentresponse = getQueryLineSegment(querylinekey)[0]; 1972 | querysegmentdata = getQueryLineSegment(querylinekey)[1]; 1973 | 1974 | if (querysegmentresponse === "error") { 1975 | query["errors"].push(querysegmentdata); 1976 | return; 1977 | } 1978 | 1979 | querysegment = querysegmentdata; 1980 | if (command == 'pagelinks') { 1981 | args = [] 1982 | args.push(arg) 1983 | } else { 1984 | args = arg.split(","); 1985 | } 1986 | 1987 | if (args.length === 1) { 1988 | querylinekey = prefix + querylinekey; 1989 | updatedqueryline = getQueryLine(querylinekey, querysegment).replace("$$ARG1", arg); 1990 | 1991 | if (!query[querysegment].includes(updatedqueryline)) { 1992 | query[querysegment].push(updatedqueryline); 1993 | } 1994 | } else { 1995 | if (args.length === 2) { 1996 | arg1 = args[0].trim(); 1997 | arg2 = args[1].trim(); 1998 | querylinekey = prefix + querylinekey; 1999 | updatedqueryline = getQueryLine(querylinekey, querysegment).replace("$$ARG1", arg1); 2000 | updatedqueryline = updatedqueryline.replace("$$ARG2", arg2); 2001 | 2002 | if (!query[querysegment].includes(updatedqueryline)) { 2003 | query[querysegment].push(updatedqueryline); 2004 | } 2005 | } else { 2006 | query[querysegment].push(command + " => Invalid line => " + arg); 2007 | } 2008 | } 2009 | } 2010 | 2011 | function processCommandLines(action, command, commandlines) { 2012 | var arg, args, firstline, lastline, prefix; 2013 | 2014 | if (commandlines === []) { 2015 | return; 2016 | } 2017 | 2018 | firstline = ""; 2019 | lastline = ""; 2020 | 2021 | if (["pages", "blocks", "blocktags", "pagetags", "pagelinks", "tasks", "pageproperties", "blockproperties", "namespace"].includes(command)) { 2022 | if (commandlines.length > 1) { 2023 | if (action === "include") { 2024 | firstline = "( or "; 2025 | lastline = ")"; 2026 | } 2027 | } 2028 | } 2029 | 2030 | if (firstline !== "") { 2031 | query["filters"].push(firstline); 2032 | } 2033 | 2034 | for (arg of commandlines) { 2035 | arg = arg.trim().substring(2); 2036 | 2037 | if (arg === "") { 2038 | continue; 2039 | } 2040 | 2041 | if (arg === "*") { 2042 | continue; 2043 | } 2044 | 2045 | prefix = ""; 2046 | 2047 | if (arg.startsWith("not ")) { 2048 | prefix = "not_"; 2049 | arg = arg.substring(prefix.length); 2050 | } 2051 | 2052 | if (command === "pages") { 2053 | if (arg[0] !== "*" && arg[arg.length - 1] === "*") { 2054 | addQueryLines(command, prefix, "arg_pagename_startswith", arg.substring(0, arg.length - 1)); 2055 | continue; 2056 | } 2057 | 2058 | if (arg[0] === "*" && arg[arg.length - 1] !== "*") { 2059 | addQueryLines(command, prefix, "arg_pagename_endswith", arg.substring(1)); 2060 | continue; 2061 | } 2062 | 2063 | if (arg[0] === "*" && arg[arg.length - 1] === "*") { 2064 | addQueryLines(command, prefix, "arg_pagename_contains", arg.substring(1, arg.length - 1)); 2065 | continue; 2066 | } 2067 | 2068 | addQueryLines(command, prefix, "pagename_is", arg); 2069 | } 2070 | 2071 | if (command === "blocks") { 2072 | if (arg[0] !== "*" && arg[arg.length - 1] === "*") { 2073 | addQueryLines(command, prefix, "arg_blockcontent_startswith", arg.substring(0, arg.length - 1)); 2074 | continue; 2075 | } 2076 | 2077 | if (arg[0] === "*" && arg[arg.length - 1] !== "*") { 2078 | addQueryLines(command, prefix, "arg_blockcontent_endswith", arg.substring(1)); 2079 | continue; 2080 | } 2081 | 2082 | if (arg[0] === "*" && arg[arg.length - 1] === "*") { 2083 | addQueryLines(command, prefix, "arg_blockcontent_contains", arg.substring(1, arg.length - 1)); 2084 | continue; 2085 | } 2086 | } 2087 | 2088 | if (command === "pagetags") { 2089 | args = arg.split(); 2090 | 2091 | for (arg of args) { 2092 | addQueryLines(command, prefix, "pagetags_are", arg.toLowerCase()); 2093 | } 2094 | } 2095 | 2096 | if (command === "blocktags") { 2097 | args = arg.split(); 2098 | 2099 | for (arg of args) { 2100 | addQueryLines(command, prefix, "blocktags_are", arg.toLowerCase()); 2101 | } 2102 | } 2103 | 2104 | if (command === "pageproperties") { 2105 | addQueryLines(command, prefix, "page_properties_are", arg); 2106 | } 2107 | 2108 | if (command === "blockproperties") { 2109 | addQueryLines(command, prefix, "block_properties_are", arg); 2110 | } 2111 | 2112 | if (command === "pagelinks") { 2113 | args = arg.split(); 2114 | 2115 | for (arg of args) { 2116 | addQueryLines(command, prefix, "pagelinks_are", arg.toLowerCase()); 2117 | } 2118 | } 2119 | if (command === "tasks") { 2120 | addQueryLines(command, prefix, "tasks_are", arg); 2121 | } 2122 | 2123 | if (command === "namespace") { 2124 | addQueryLines(command, prefix, "namespace", arg); 2125 | } 2126 | 2127 | if (command === "scheduled") { 2128 | addQueryLines(command, prefix, "scheduled", arg); 2129 | } 2130 | 2131 | if (command === "scheduledbetween") { 2132 | addQueryLines(command, prefix, "scheduledbetween", arg); 2133 | } 2134 | 2135 | if (command === "deadline") { 2136 | addQueryLines(command, prefix, "deadline", arg); 2137 | } 2138 | 2139 | if (command === "deadlinebetween") { 2140 | addQueryLines(command, prefix, "deadlinebetween", arg); 2141 | } 2142 | 2143 | if (command === "journalonly") { 2144 | addQueryLines(command, prefix, "page_is_journal", arg); 2145 | } 2146 | 2147 | if (command === "journalsbetween") { 2148 | addQueryLines(command, prefix, "journalsbetween", arg); 2149 | } 2150 | 2151 | if (command === "daterange") { 2152 | addQueryLines(command, prefix, "daterange", arg); 2153 | } 2154 | 2155 | if (command === "collapse") { 2156 | addQueryLines(command, prefix, "collapse", arg); 2157 | } 2158 | 2159 | if (command === "collapse") { 2160 | addQueryLines(command, prefix, "expand", arg); 2161 | } 2162 | } 2163 | 2164 | if (lastline !== "") { 2165 | query["filters"].push(lastline); 2166 | } 2167 | 2168 | return; 2169 | } 2170 | 2171 | function insertQueryLineIntoSegment(key) { 2172 | var querysegment, querysegmentdata, querysegmentresponse; 2173 | querysegmentresponse = getQueryLineSegment(key)[0]; 2174 | querysegmentdata = getQueryLineSegment(key)[1]; 2175 | 2176 | if (querysegmentresponse === "error") { 2177 | query["errors"].push(querysegmentdata); 2178 | return; 2179 | } 2180 | 2181 | querysegment = querysegmentdata; 2182 | query[key].push(getQueryLine(key, querysegment)); 2183 | } 2184 | 2185 | function checkUsingPagesorBlocks(commandlines) { 2186 | var blocksfound, commandline, pagesfound 2187 | pagesfound = false; 2188 | blocksfound = false; 2189 | 2190 | for (commandline of commandlines) { 2191 | 2192 | commandline = commandline.trim(); 2193 | 2194 | if (commandline.startsWith("- pages")) { 2195 | pagesfound = true; 2196 | } 2197 | 2198 | if (commandline.startsWith("- blocks")) { 2199 | blocksfound = true; 2200 | } 2201 | } 2202 | 2203 | if (pagesfound === true && blocksfound === false) { 2204 | querygroup = "pages-querylines"; 2205 | return; 2206 | } 2207 | 2208 | if (blocksfound === true && pagesfound === false) { 2209 | querygroup = "blocks-querylines"; 2210 | return; 2211 | } 2212 | 2213 | if (pagesfound === false && blocksfound === false) { 2214 | query["errors"].push(";; WARNING: Must have 'pages' command or 'blocks' Command\n;; otherwise the query cannot get any information\n;; Inserting a blocks command for you\n"); 2215 | insertBlocksCommand(commandlines); 2216 | blocksfound = true; 2217 | querygroup = "blocks-querylines"; 2218 | return; 2219 | } 2220 | 2221 | if (pagesfound && blocksfound) { 2222 | query["errors"].push(";; ERROR: Cannot have 'pages' command and 'blocks' command together in a command list\n\n"); 2223 | return; 2224 | } 2225 | } 2226 | 2227 | function insertBlocksCommand(commandlines) { 2228 | if (commandlines.length > 0) { 2229 | if (commandlines[0].indexOf("title:") > -1) { 2230 | commandlines.splice(1, 0, " - *"); 2231 | commandlines.splice(1, 0, "- blocks"); 2232 | } 2233 | } else { 2234 | commandlines.splice(0, 0, "- blocks\n - *\n"); 2235 | } 2236 | return; 2237 | } 2238 | 2239 | function validCommand(command) { 2240 | try { 2241 | if (commandsDict[command]) { 2242 | return true; 2243 | } else { 2244 | return false 2245 | } 2246 | } catch (e) { 2247 | return false; 2248 | } 2249 | } 2250 | 2251 | function processCommandList(commandlists) { 2252 | var commandLinesDict, commandlines, commandname, currentcommand, fields, query; 2253 | query = initialiseQuery(); 2254 | commandlines = commandlists.split("\n"); 2255 | checkUsingPagesorBlocks(commandlines); 2256 | currentcommand = ""; 2257 | commandLinesDict = {}; 2258 | 2259 | for (line of commandlines) { 2260 | 2261 | if (line === "" || line.startsWith(";;")) { 2262 | continue; 2263 | } 2264 | 2265 | if (line.trim().startsWith("title:")) { 2266 | query["title"].push(getQueryLine("title", "title").replace("$$ARG1", line.split(":")[1].trim())); 2267 | continue; 2268 | } 2269 | 2270 | if (line.trim().startsWith("option:")) { 2271 | var option = line.split(":")[1].trim() 2272 | if (option == "includecomments") { 2273 | setshowcommandcomments(true) 2274 | } else { 2275 | query['errors'].push( 2276 | ";; WARNING: '" + line + "' is not valid option. \n;; Valid options: includecomments") 2277 | } 2278 | continue; 2279 | } 2280 | 2281 | if (line.startsWith("- ")) { 2282 | fields = line.split(" "); 2283 | commandname = fields[1]; 2284 | 2285 | if (validCommand(commandname)) { 2286 | commandLinesDict[commandname] = {}; 2287 | 2288 | if (currentcommand === "" || line !== currentcommand) { 2289 | currentcommand = commandname; 2290 | commandLinesDict[commandname]["commandlines"] = []; 2291 | commandLinesDict[commandname]["commandlines"].push(line); 2292 | continue; 2293 | } 2294 | } else { 2295 | query["errors"].push(";; WARNING: '" + line + "' is not valid command.\n;; Either a mispelt command or no leading dash"); 2296 | // continue; 2297 | } 2298 | } else { 2299 | if (line.startsWith(" ") && line.trim().startsWith("- ")) { 2300 | if (currentcommand === "") { 2301 | query["errors"].push(";; ERROR: '" + line + "' is a command argument but does not have a parent command\n;; Either a command is missing (or invalid) or this should be an argument line"); 2302 | } else { 2303 | if (validCommand(commandname)) { 2304 | commandLinesDict[commandname]["commandlines"].push(line); 2305 | } else { 2306 | query["errors"].push(";; ERROR: '" + line + "' is a command argument but does not have a parent command\n;; Either a command is missing (or invalid) or this should be an argument line"); 2307 | } 2308 | } 2309 | } else { 2310 | if (line.includes("title ")) { 2311 | query["errors"].push(";; WARNING: title line should start with title:"); 2312 | } else { 2313 | if (!line.trim().startsWith("- ") && !(line.indexOf("title:") > -1)) { 2314 | query["errors"].push(";; WARNING: " + line + " has no leading hypen eg '- pages'"); 2315 | } 2316 | } 2317 | } 2318 | } 2319 | } 2320 | 2321 | insertQueryLineIntoSegment("start"); 2322 | insertQueryLineIntoSegment("open"); 2323 | insertQueryLineIntoSegment("where"); 2324 | insertQueryLineIntoSegment("closefind"); 2325 | insertQueryLineIntoSegment("closequery"); 2326 | insertQueryLineIntoSegment("end"); 2327 | 2328 | for (command in commandLinesDict) { 2329 | processCommand(command, commandLinesDict); 2330 | } 2331 | 2332 | query["closefind"] = [getQueryLine("closefind", "closefind")]; 2333 | query["closequery"] = [getQueryLine("closequery", "closequery")]; 2334 | query["end"] = [getQueryLine("end", "end")]; 2335 | return; 2336 | } 2337 | 2338 | 2339 | function constructQuery() { 2340 | var advancedquery; 2341 | advancedquery = ""; 2342 | 2343 | for (const [key, value] of Object.entries(query)) { 2344 | for (let j = 0; j < query[key].length; j++) { 2345 | queryline = query[key][j] 2346 | advancedquery += queryline + "\n"; 2347 | } 2348 | } 2349 | return advancedquery; 2350 | } 2351 | 2352 | function printGeneratedAdvancedQuery(advancedquery) { 2353 | var msg, prefix, suffix; 2354 | 2355 | if (codeblock) { 2356 | prefix = "```clojure\n"; 2357 | suffix = "```"; 2358 | } else { 2359 | prefix = ""; 2360 | suffix = ""; 2361 | } 2362 | 2363 | if (mode === "website") { 2364 | // msg = prefix + advancedquery.replace("\n", "
") + suffix; 2365 | msg = prefix + advancedquery + suffix; 2366 | websitePrintToDiv('advanced_query', msg) 2367 | } else { 2368 | console.log("----------------------------"); 2369 | console.log("Logseq Advanced Query"); 2370 | console.log("----------------------------"); 2371 | console.log(prefix + advancedquery + suffix); 2372 | } 2373 | } 2374 | 2375 | 2376 | // global value functions 2377 | function getquerygroup() { 2378 | return querygroup 2379 | } 2380 | 2381 | function setquerygroup(value) { 2382 | querygroup = value 2383 | } 2384 | 2385 | function getshowcommandcomments() { 2386 | return showcommandcomments 2387 | } 2388 | 2389 | function setshowcommandcomments(value) { 2390 | showcommandcomments = value 2391 | } 2392 | 2393 | function getcodeblock() { 2394 | return codeblock 2395 | } 2396 | 2397 | function setcodeblock(value) { 2398 | codeblock = value 2399 | } 2400 | 2401 | function getquerylineDBDict() { 2402 | return querylineDBDict 2403 | } 2404 | 2405 | function setquerylineDBDict(value) { 2406 | querylineDBDict = value 2407 | } 2408 | 2409 | // Test queryTestDB works 2410 | function test_queryTestDBRead() { 2411 | testcases = QueryTestCases 2412 | return testcases[0]; 2413 | } 2414 | 2415 | function getquerytestcases() { 2416 | return QueryTestCases 2417 | } 2418 | 2419 | function removeLastGeneratedQuery(content) { 2420 | let lines = content.split("\n") 2421 | let newcontent = '' 2422 | for (const line of lines) { 2423 | if (line.startsWith('#+BEGIN_QUERY')) { 2424 | break 2425 | } 2426 | newcontent += line + '\n' 2427 | } 2428 | return newcontent 2429 | } 2430 | 2431 | function showErrors() { 2432 | msg = 'QB: Errors Found - check built query' 2433 | if (query["errors"].length > 0) { 2434 | for (errormsg of query["errors"]) { 2435 | msg += errormsg + '\n' 2436 | } 2437 | return msg 2438 | } else { 2439 | return "QB: Query Built OK" 2440 | } 2441 | } 2442 | 2443 | 2444 | function main() { 2445 | 2446 | logseq.Editor.registerBlockContextMenuItem( 2447 | 'Advanced Query Builder', 2448 | async (e) => { 2449 | const block = await logseq.Editor.getBlock(e.uuid) 2450 | content = block.content 2451 | content = removeLastGeneratedQuery(content) 2452 | commands = content.replaceAll(/```/g, '') 2453 | showcommandcomments = false; 2454 | querygroup = "blocks-querylines"; 2455 | codeblock = false; 2456 | // query = initialiseQuery(); 2457 | // querylineDBDict = initialisteQueryLineDict(); 2458 | // querygroup = "pages-querylines" 2459 | // logseq.App.showMsg( 2460 | // commands, 2461 | // ) 2462 | setquerylineDBDict(initialisteQueryLineDict()) 2463 | console.log("\ncommands\n" + commands); 2464 | console.log("\ncontent\n" + content); 2465 | initialiseQuery() 2466 | processCommandList(commands) 2467 | advancedquery = constructQuery() 2468 | 2469 | // Currently will add a child block with the generated query 2470 | // user can right click on the query and remove it or leave it there 2471 | // IDEA: Maybe always remove the last query so how do I do that 2472 | // remove any children blocks 2473 | // TODO: Returns undefined for the child uuid????? Mayne just let user insert a new child for each query execution 2474 | // if (block.children.length > 0) { 2475 | // for (let child of block.children) { 2476 | // await logseq.Editor.removeBlock(child.uuid); 2477 | // } 2478 | // } 2479 | 2480 | 2481 | // place the advanced query in a child of the current block (that has the commands in it) 2482 | await logseq.Editor.insertBlock(e.uuid, advancedquery, { sibling: false }) 2483 | 2484 | logseq.App.showMsg( 2485 | showErrors() 2486 | ) 2487 | }) 2488 | 2489 | } // end main 2490 | 2491 | // ======== website functions 2492 | 2493 | function websiteInitialise() { 2494 | 2495 | if (mode != "website") { 2496 | return 2497 | } 2498 | 2499 | // connect the generate advanced query button 2500 | generate_query_button = document.getElementById('generate_query_button') 2501 | generate_query_button.addEventListener("click", websiteQueryBuild) 2502 | 2503 | // connect the Clear Commands button 2504 | clear_commands_button = document.getElementById('clear_commands_button') 2505 | clear_commands_button.addEventListener("click", websiteClearCommands) 2506 | 2507 | // connect the Examples button 2508 | examples_options = document.getElementById('command_examples') 2509 | examples_options.addEventListener("input", websiteChooseExample) 2510 | examples_options.value = "" // set to first option 2511 | 2512 | // connect the Command Comments Checkbox 2513 | command_comments_checkbox = document.getElementById( 2514 | 'command_comments_checkbox') 2515 | command_comments_checkbox.addEventListener("click", websiteCommandComments) 2516 | command_comments_checkbox.checked = false 2517 | 2518 | commands_input = document.getElementById('commands_input') 2519 | commands_input.value = '' 2520 | 2521 | // connect the Code Block Output Checkbox 2522 | codeblock_checkbox = document.getElementById( 2523 | 'codeblock_checkbox') 2524 | codeblock_checkbox.addEventListener("click", websiteCodeBlock) 2525 | codeblock_checkbox.checked = false 2526 | 2527 | } 2528 | 2529 | function websiteClearCommands(event) { 2530 | if (mode != "website") { 2531 | return 2532 | } 2533 | 2534 | // # hide copy to clipboard button 2535 | websitePrintToDiv('print_output', 'Clear Commands Button Pressed') 2536 | commands_input = document.getElementById('commands_input') 2537 | commands_input.value = '' 2538 | } 2539 | 2540 | 2541 | function websiteCommandComments(event) { 2542 | if (mode != "website") { 2543 | return 2544 | } 2545 | if (document.getElementById('command_comments_checkbox').checked == true) { 2546 | setshowcommandcomments(true) 2547 | } else { 2548 | setshowcommandcomments(false) 2549 | } 2550 | } 2551 | 2552 | 2553 | function websiteCodeBlock(event) { 2554 | // TODO: Check this works re global codeblock variable 2555 | if (mode != "website") { 2556 | return 2557 | } 2558 | if (document.getElementById('codeblock_checkbox').checked == true) { 2559 | codeblock = true 2560 | } else { 2561 | codeblock = false 2562 | } 2563 | } 2564 | 2565 | 2566 | function websiteChooseExample(event) { 2567 | if (mode != "website") { 2568 | return 2569 | } 2570 | // get selected Example and fill the commands Input Text Area 2571 | examples_options = document.getElementById('command_examples') 2572 | if (examples_options.value != "Choose Example..") { 2573 | advanced_query_text = document.getElementById('advanced_query') 2574 | advanced_query_text.textContent = '' 2575 | websitePrintToDiv('print_output', 2576 | "Example selected .. now press 'Generate Advanced Query' button") 2577 | 2578 | document.getElementById( 2579 | 'commands_input').value = examples_options.value 2580 | // console.log('value is ', examples_options.value) 2581 | } 2582 | } 2583 | 2584 | function websiteAdvancedQueryText(event) { 2585 | if (mode != "website") { 2586 | return 2587 | } 2588 | } 2589 | 2590 | function websitePrintToDiv(divname, text) { 2591 | if (mode != "website") { 2592 | return 2593 | } 2594 | document.getElementById(divname).innerText = text 2595 | } 2596 | 2597 | 2598 | function websiteQueryBuild(event) { 2599 | if (mode != "website") { 2600 | return 2601 | } 2602 | 2603 | // # hide copy to clipboard button 2604 | copy_button = document.getElementById('copy') 2605 | copy_button.setAttribute("hidden", "hidden") 2606 | 2607 | websitePrintToDiv('print_output', 'Processing Commands ..') 2608 | 2609 | commands_input = document.getElementById('commands_input') 2610 | if (!commands_input) { 2611 | websitePrintToDiv('print_output', 'Bug: Element is None') 2612 | return 2613 | } 2614 | processCommandList(commands_input.value) 2615 | advancedquery = constructQuery() 2616 | printGeneratedAdvancedQuery(advancedquery) 2617 | 2618 | // show copy to clipboard button 2619 | var copy_button = document.getElementById('copy') 2620 | hidden = copy_button.getAttribute("hidden") 2621 | copy_button.removeAttribute("hidden") 2622 | 2623 | websitePrintToDiv('print_output', 2624 | "Advanced Query Generated!\n- Tick 'Include Query Comments' if desired\n- Tick 'Copy as code block' if desired\nClick 'Copy Query to Clipboard") 2625 | 2626 | return 2627 | } 2628 | 2629 | 2630 | 2631 | // MAIN ENTRY POINT 2632 | 2633 | // ******************************* 2634 | // LOCAL MODE TESTING WITH JEST TESTING 2635 | // (Comment out WEBSITE MODE section below and PLUGIN MODE section above) 2636 | // (Uncomment this section for local testing with JEST) 2637 | // ******************************* 2638 | // // var mode = "local" 2639 | // module.exports = { 2640 | // //querygroup, 2641 | // //showcommandcomments, 2642 | // getquerygroup, 2643 | // setquerygroup, 2644 | // getshowcommandcomments, 2645 | // setshowcommandcomments, 2646 | // getcodeblock, 2647 | // setcodeblock, 2648 | // getquerylineDBDict, 2649 | // setquerylineDBDict, 2650 | // getquerytestcases, 2651 | // add, 2652 | // test_queryDBRead, 2653 | // test_queryTestDBRead, 2654 | // addQueryLines, 2655 | // checkCommandValid, 2656 | // checkUsingPagesorBlocks, 2657 | // constructQuery, 2658 | // getCommandQueryLineKeys, 2659 | // getQueryLine, 2660 | // getQueryLineComment, 2661 | // getQueryLineSegment, 2662 | // initialiseQuery, 2663 | // initialisteQueryLineDict, 2664 | // insertBlocksCommand, 2665 | // insertQueryLineIntoSegment, 2666 | // printGeneratedAdvancedQuery, 2667 | // processCommand, 2668 | // processCommandLines, 2669 | // processCommandList, 2670 | // removeLastGeneratedQuery, 2671 | // validCommand 2672 | // } 2673 | // ******************************* 2674 | 2675 | // ******************************* 2676 | // LOGSEQ WEBSITE MODE 2677 | // (Comment out LOCAL MODE section above and PLUGIN mode section below and uncomment this section) 2678 | // ******************************* 2679 | mode = "website" 2680 | // ******************************* 2681 | 2682 | 2683 | // ******************************* 2684 | // LOGSEQ PLUGIN MODE 2685 | // (Comment out LOCAL MODE section above and WEBSITE section above and uncomment this section) 2686 | // ******************************* 2687 | // mode = "logseq-plugin" 2688 | // ******************************* 2689 | 2690 | // MAIN STARTING POINT 2691 | if (mode == "logseq-plugin") { 2692 | console.log('logseq-advanced-query-builder plugin loaded') 2693 | logseq.ready(main).catch(console.error) 2694 | } 2695 | if (mode == "local") { 2696 | console.log('logseq-advanced-query-builder code running locally') 2697 | querygroup = ""; 2698 | codeblock = false; 2699 | query = initialiseQuery(); 2700 | querylineDBDict = initialisteQueryLineDict(); 2701 | } 2702 | if (mode == "website") { 2703 | console.log('logseq-advanced-query-builder code running in browser') 2704 | querygroup = ""; 2705 | codeblock = false; 2706 | query = initialiseQuery(); 2707 | querylineDBDict = initialisteQueryLineDict(); 2708 | websiteInitialise() 2709 | } 2710 | 2711 | 2712 | --------------------------------------------------------------------------------